Files
company-haness/docs/superpowers/specs/2026-07-10-p0-execution-integrity-design.md

16 KiB

P0 실행 무결성 복구 — 설계/실행 스펙

status: approved supersedes: (none) applies-to-version: company-haness @ fix/p0-execution-integrity date: 2026-07-10

배경 / 목표

외부 리뷰(2026-07-10)가 하네스의 P0(실행 무결성) 결함 6건을 지적했다. 검증 결과 모두 사실이다. 이 스펙은 그 6건 + 부속 도구(doctor·ref-linter·lifecycle 테스트)를 파일 소유권이 겹치지 않는 6개 work-package로 나눠, 각 package를 격리 subagent 1명이 구현하게 한다. 병렬 실행 중 어떤 두 에이전트도 같은 파일을 쓰지 않는다.

이 패스의 범위는 P0만이다. P1/P2는 이 패스 완료 후 별도로 논의한다.

핵심 한 줄(리뷰 인용):

올바른 프로젝트 문맥 → 실존하는 agent → 검증된 context → 실물 산출물 → 실제 실행 근거 → task-specific acceptance

대상 P0 결함

  • #1 문서상 "강제"인 hook이 실제로 꺼져 있음(.claude/settings.json 부재; settings.hooks.json은 자동 로드 안 됨). Stop 미배선.
  • #2 SubagentStop 검증이 대부분 보고서를 못 찾고 fail-open. agent_id/last_assistant_message 미사용, 재귀 탐색 아님, YAML 오류 시 rc=1(차단 아님).
  • #3 커맨드가 존재하지 않는 family agent(fam-architecture-tech/fam-design/fam-data/fam-security/fam-product-mgmt) 호출.
  • #4 cascade 커맨드가 필수 context-package를 만들지 않음(/run-wave만 만든다).
  • #5 실제 회사·프로젝트 문맥 부재(org-os/01-05,07 없음). workspace 기본값이 test 프로젝트(test-labs-documents).
  • #6 validator가 헤더 모양만 검사. E4/E5 등급이 에이전트 자기신고이며 실제 실행 receipt와 대조되지 않음.

Work-package 분해 (파일 소유권 = 충돌 매트릭스)

각 WP가 쓰는(write/create) 파일은 서로 배타적이다. 아래 목록 밖 파일은 그 WP가 수정하지 않는다.

WP 결함 쓰는 파일 (배타 소유)
WP-1 #1 .claude/settings.json(신규), .claude/hooks/doctor.py(신규), .claude/commands/doctor.md(신규, 선택)
WP-2 #2 .claude/hooks/stop_validate.py(재작성), .claude/hooks/subagent_register.py(신규), .claude/tests/test_subagent_lifecycle.py(신규)
WP-3 #3 .claude/hooks/gen_agents.py, .claude/hooks/lint_refs.py(신규), 생성물 .claude/agents/fam-*.md(gen_agents 출력)
WP-4 #5 .claude/hooks/_workspace.py, .orgos-workspace, org-os/01-company/..07-knowledge-base/(신규 스텁), org-os/01-company/company-context.yaml(신규), org-os/00-role-registry/README 무관
WP-6 #6 .claude/hooks/evidence_ledger.py(신규), .claude/hooks/validate_report.py(재작성), .claude/hooks/render_report.py(게이트 추가), .claude/schemas/*.json(신규), .claude/tests/test_enforcement.py(갱신)
WP-5 #4 .claude/hooks/context_package.py(신규), org-os/06-agent-work/context-package-spec.yaml, .claude/commands/decide.md·ground.md·design.md·spec.md·build.md

문서 정직성 수정(CLAUDE.md/README.md)은 어느 WP도 하지 않는다 — Wave 3에서 오케스트레이터가 실제 구현 결과에 맞춰 한 곳에서 반영한다(문서 충돌 방지).

실행 순서 (waves)

  • Wave 1 (병렬): WP-1, WP-2, WP-3, WP-4, WP-6. 파일 소유가 배타적이라 동시 실행 안전.
  • Wave 2 (Wave 1 이후): WP-5. WP-4(회사문맥 스키마)·WP-6(report/evidence 계약)·WP-3(agent 존재)에 의존.
  • Wave 3 (오케스트레이터 직접): 문서 정직성 반영, doctor+테스트 실행, 통합 검증.

공유 계약 (SHARED CONTRACTS — 모든 WP가 준수)

병렬 에이전트가 일관되게 맞물리도록, 아래 인터페이스는 고정이다. 임의로 바꾸지 말 것.

C1. _workspace.py 공개 API (불변 시그니처)

workspace_name(), work_root(), records_dir(), evidence_dir(), reports_dir(), state_dir(), slack_outbox(), slack_inbox() — 함수명/반환(경로 문자열) 유지. WP-4는 해석 로직만 바꾼다: 하드코딩 기본값(test-labs-documents) 제거. 미설정 시 WorkspaceNotSetError(명확한 안내 메시지)로 중단.

C2. 불변 report 경로/포맷 (new_report.py — 변경 없음, 참조용)

  • 경로: <work_root>/completion-records/<workflow>/<role>-<UTCstamp>.report.yaml
  • 최상단 필드: report-id, workflow-id, role-id, created-at, report-header{bottom-line, decision-needed, confidence, risks, evidence}.
  • 재귀 탐색 시 glob 패턴은 completion-records/**/*.report.yaml.

C3. validate_report.validate() 시그니처 (WP-2 ↔ WP-6 경계)

def validate(report: dict, report_path: str | None = None) -> list[str]:
    # 반환: 위반 사유 문자열 리스트(빈 리스트 = 통과). 예외를 던지지 않는다.
  • WP-6는 인자를 (report, report_path=None)확장하되 기존 호출부(vr.validate(report))와 하위호환 유지.
  • report_path가 주어지면 WP-6는 그 경로에서 workspace를 해석해 evidence-ledger(C5)를 대조한다.
  • WP-2의 stop_validate.py는 계속 vr.validate(report, report_path=path)를 호출한다.

C4. subagent 등록 레지스트리 (WP-1 배선 ↔ WP-2 구현)

  • 파일: <state_dir>/subagent-registry.jsonl (append-only, 한 줄 = JSON).
  • SubagentStart 레코드 필드(최소): {agent_id, agent_type, workflow_id?, role?, expected_report_dir?, started_at}.
  • 값이 없으면 필드 생략 가능하나 agent_id는 필수.
  • SubagentStop이 이 레지스트리에서 agent_id로 조회해 기대 보고서를 판정한다.

C5. evidence-ledger receipt (WP-1 배선 ↔ WP-6 구현)

  • 파일: <evidence_dir>/ledger.jsonl (append-only, 한 줄 = JSON receipt).
  • receipt 필드: {tool_use_id, tool_name, ts, cwd, command?, exit_code?, stdout_sha256?, artifact_path?, artifact_sha256?}.
    • Bash: command, exit_code, stdout_sha256 채움.
    • Write/Edit: artifact_path, artifact_sha256 채움.
  • validator(C6)는 이 파일을 읽어 E4/E5 주장과 대조한다. 파일 없으면 receipt 0개로 취급(주장 미검증 → 차단/강등).

C6. evidence 등급 파생 규칙 (WP-6)

  • 에이전트가 선언한 grade주장일 뿐, validator가 receipt로 검증한다.
  • E5/E4: evidence 항목이 command+exit-code:0을 주장하면 ledger(C5)에 command 문자열이 일치하고 exit_code:0인 receipt가 있어야 한다. 없으면 위반(차단). 파일 산출을 주장하면 artifact_sha256 receipt가 있어야 한다.
  • 일반 파일 참조(예: 기존 CLAUDE.md)만으로는 E5 불가.
  • 기존 헤더/BLUF/dissent 검사(C3의 validate 본문)는 유지·강화.

C7. hook event 배선표 (WP-1이 .claude/settings.json에 작성)

아래 스크립트 경로/이벤트로 배선한다. 스크립트 구현은 각 소유 WP가 한다. 스크립트가 아직 없어도(병렬) settings.json 작성은 가능(다음 세션에 적용).

PreToolUse   [Bash|Write|Edit|NotebookEdit] -> guard_tools.py        (기존)
PostToolUse  [Bash|Write|Edit]              -> evidence_ledger.py     (WP-6)
SubagentStart                               -> subagent_register.py   (WP-2)
SubagentStop                                -> stop_validate.py       (WP-2)
Stop                                        -> stop_validate.py --main (WP-2: 메인 세션 최종 산출도 검증)
  • doctor.py(WP-1)는 settings.json 존재·hook 배선·참조 스크립트 실존·python/pyyaml·workspace 설정 여부를 점검한다.

C8. agent 이름 규약 (WP-3)

  • fan-out-split family(멤버≥2, lead-role-id 없음) 10개 각각에 fam-<family-id 소문자> router agent를 추가 생성한다. 대상: FAM-PRODUCT-MGMT, FAM-UX-RESEARCH, FAM-DESIGN, FAM-ARCHITECTURE-TECH, FAM-ARCHITECTURE-BIZ, FAM-DATA, FAM-SECURITY, FAM-GTM-GROWTH, FAM-GTM-SALES, FAM-REVOPS.
  • router는 멤버 role들의 관점을 담되(build_agent 스타일) fan-out 멤버 목록 + 종합/conflict 계약을 명시한다.
  • 개별 worker agent(role-id.md)는 그대로 유지. router 이름(fam-*)은 worker 이름(role-id)과 충돌하지 않음.

WP별 상세

WP-1 — 설정 활성화 + doctor

목표: 문서상 "강제"를 실제로 켠다.

  • .claude/settings.json 생성: C7 배선표대로. 기존 settings.hooks.json의 PreToolUse(guard_tools)를 포함하고 PostToolUse/SubagentStart/SubagentStop/Stop을 추가.
  • .claude/hooks/doctor.py: 설정·hook·의존성·workspace·command→agent 참조(WP-3의 lint_refs.py가 있으면 호출) 점검 → 문제 시 비영점 종료 + 사람이 읽는 리포트.
  • (선택) .claude/commands/doctor.md/doctor 노출. 하지 않는 것: CLAUDE.md/README 수정(Wave 3), hook 스크립트 구현(각 소유 WP). 수용: python3 .claude/hooks/doctor.py가 실행되고 현재 결함(스크립트 부재 등)을 정확히 보고. settings.json은 유효 JSON이며 C7과 일치.

WP-2 — subagent 생명주기 (fail-closed 보고서 바인딩)

목표: #2를 닫는다.

  • subagent_register.py(SubagentStart): stdin JSON 파싱, C4 레지스트리에 append. 예외/malformed JSON은 안전 처리하되 등록 실패를 로그.
  • stop_validate.py 재작성:
    • stdin에서 agent_id·last_assistant_message·(있으면) agent_transcript_path 사용.
    • 보고서 경로 해석 우선순위: (1) last_assistant_message에 포함된 report-path, (2) $CLAUDE_REPORT_PATH, (3) 레지스트리 expected_report_dir 하위 최신, (4) records_dir()/**/*.report.yaml 재귀 중 이 agent 소속.
    • fail-closed: 등록된(보고서 산출 대상) agent인데 보고서 없음 → rc=2. YAML 파싱 오류 → rc=2. workspace 밖 경로 → rc=2. malformed hook JSON → rc=2.
    • 읽기전용/면제 agent(레지스트리에 report 비대상으로 표기되거나 알려진 helper type)는 통과 허용(과잉차단 방지).
    • --main 플래그: 메인 세션 Stop용(해당 workflow 최종 산출 검증). 메인엔 보고서가 없을 수 있으니 이 경우의 정책을 명확히(면제 or 최종 산출 존재 시 검증).
    • vr.validate(report, report_path=path) 호출(C3).
  • test_subagent_lifecycle.py: SubagentStart→Stop 페이로드를 스크립트에 파이프하는 E2E 유닛. 케이스: 유효 보고서 통과 / 보고서 없음 차단 / malformed YAML 차단 / 경로 이탈 차단 / 동시 2개 agent가 서로의 보고서를 오검증하지 않음. 수용: 새 테스트 전부 통과. stop_validate.py가 rc 규약(2=block)을 지킴.

WP-3 — family agent 참조 복구 + ref linter

목표: #3을 닫고 재발을 CI로 막는다.

  • gen_agents.py: fan-out-split & lead 없음 family(C8의 10개)에 router agent(fam-<id>)를 추가 생성. 기존 worker/lead/family 로직은 보존. --check 개수 계약을 새 총계로 갱신(현재 60 → +10 router = 70; role/ lead/ family 카운트는 유지, router 종류 추가). --check 어서션·본문 검증도 router에 맞게 추가.
  • lint_refs.py: .claude/commands/*.md(및 필요 시 agents/hooks)에서 참조하는 (a) agent 이름(fam-*, role-id), (b) hook 스크립트 경로, (c) 파일 경로를 추출해 실존 검증. 미해결 참조 있으면 비영점 종료(리스트 출력). CI/doctor에서 호출 가능. 하지 않는 것: commands/*.md 수정(WP-5 소유). CLAUDE.md 수정. 수용: gen_agents.py(무인자)로 fam-architecture-tech/design/data/security/product-mgmt(+나머지 5) 파일 생성됨. gen_agents.py --check 통과. lint_refs.py가 현 커맨드의 깨진 참조를 수정 전엔 잡고, router 생성 후엔 통과.

WP-4 — workspace 강제 + 회사 문맥

목표: #5를 닫는다.

  • _workspace.py: C1 유지. DEFAULT_WORKSPACE 하드코딩 제거. 미설정 시 WorkspaceNotSetError(명확 안내: ORGOS_WORKSPACE 또는 .orgos-workspace 지정)로 중단. 공개 함수 시그니처 불변.
  • .orgos-workspace: test 프로젝트 고정 대신 로컬 개발용 명시 포인터로 취급. 값은 그대로 두되(로컬 편의), 코드가 "포인터 없으면 중단"을 강제. (필요 시 파일 상단 주석으로 "운영은 ORGOS_WORKSPACE 필수" 명기.)
  • org-os/01-company/ … 07-knowledge-base/: README 스텁 디렉터리 생성(리뷰가 지적한 약속된 구조 실체화).
  • org-os/01-company/company-context.yaml + 프로젝트 manifest 스키마: stack, build/test/lint/run 명령, 제품 목적, 사용자, 제약, 코드 규약, 민감도, 최근 결정 필드. (템플릿/스키마 수준으로 충분 — 실데이터 강요 아님.)
  • confidence 상한 규칙: 실제 회사 자료가 없으면 해당 판단 confidence 상한 E1/E2 — 이 규칙을 관련 정책 문서(예: company-context.yaml 주석 또는 org-os/01-company/README)에 명문화. 하지 않는 것: new_report/validate 등 다른 hook 수정. CLAUDE.md 수정. 수용: ORGOS_WORKSPACE 미설정 상태에서 workspace 필요 hook이 명확 오류로 중단. org-os/01-05,07 실존. company-context 스키마 유효 YAML.

WP-6 — receipt 기반 evidence + semantic validator

목표: #6을 닫는다.

  • evidence_ledger.py(PostToolUse): C5 receipt를 <evidence_dir>/ledger.jsonl에 append. workspace 미설정 등 예외는 안전 처리(무한루프/크래시 금지).
  • validate_report.py 재작성: C3 시그니처로 report_path 지원. C6 규칙으로 E4/E5 주장을 ledger와 대조. report-type 판별자 + .claude/schemas/의 JSON Schema로 유형별 필수 필드 검사. 기존 BLUF/decision/confidence/risks/evidence/synthesis 검사 유지.
  • render_report.py: 렌더 전에 validator를 통과했는지 게이트(미통과면 렌더 거부/경고+비영점). 기존 인터페이스(CLI usage) 보존.
  • .claude/schemas/: report 공통 + 유형별(decision/work/completion/review/blocked/design) 스키마.
  • test_enforcement.py 갱신: 자기신고 E5 fixture(command+exit-code만, receipt 없음)는 이제 실패. receipt를 시드한 fixture는 통과. 기존 통과하던 잘못된 케이스(존재하지 않는 linked report, conflicts:null 등)도 차단됨을 검증. 하지 않는 것: stop_validate.py 수정(WP-2 소유, 단 C3 시그니처만 맞춤). CLAUDE.md 수정. 수용: 자기신고 E5가 차단됨. receipt 뒷받침 E5는 통과. validate_report.py가 예외 없이 위반 리스트 반환.

WP-5 (Wave 2) — context-package 컴파일러 + 커맨드 배선

목표: #4를 닫는다.

  • context_package.py(신규): 모든 spawn이 거치는 컴파일러+validator. context-package-spec.yaml 필수 필드 + 신규 필수(workspace, target-repo, acceptance-tests, non-goals, evidence-plan)를 강제. workspace 미설정(C1)이면 중단.
  • context-package-spec.yaml: 위 신규 필수 필드 추가.
  • decide.md·ground.md·design.md·spec.md·build.md: 각 fan-out/spawn 단계가 context_package.py를 호출해 패키지를 만들고 검증한 뒤 워커를 호출하도록 배선. 커맨드마다 절차 중복 대신 공통 primitive 참조. 수용: 각 cascade 커맨드가 워커 spawn 전 context-package 컴파일·검증을 명시. context_package.py가 필수 필드 누락을 거부.

검증 계획 (Wave 3)

  1. CLAUDE_PROJECT_DIR="$PWD" ORGOS_WORKSPACE=<explicit> python3 .claude/hooks/gen_agents.py --check — 70 agents 통과.
  2. python3 .claude/hooks/lint_refs.py — 커맨드 참조 무결성 통과.
  3. CLAUDE_PROJECT_DIR="$PWD" ORGOS_WORKSPACE=<explicit> python3 .claude/tests/test_enforcement.pytest_subagent_lifecycle.py 통과.
  4. python3 .claude/hooks/doctor.py — 그린.
  5. 각 hook 스크립트를 대표 페이로드로 수동 1회 실행해 크래시 없음 확인.
  6. CLAUDE.md/README의 "강제" 서술을 실제 구현(설정 활성화 시 강제)로 정직하게 수정.
  7. 커밋(사용자 승인 시).