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채움.
- Bash:
- 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_sha256receipt가 있어야 한다. - 일반 파일 참조(예: 기존
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).
- stdin에서
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)
CLAUDE_PROJECT_DIR="$PWD" ORGOS_WORKSPACE=<explicit> python3 .claude/hooks/gen_agents.py --check— 70 agents 통과.python3 .claude/hooks/lint_refs.py— 커맨드 참조 무결성 통과.CLAUDE_PROJECT_DIR="$PWD" ORGOS_WORKSPACE=<explicit> python3 .claude/tests/test_enforcement.py및test_subagent_lifecycle.py통과.python3 .claude/hooks/doctor.py— 그린.- 각 hook 스크립트를 대표 페이로드로 수동 1회 실행해 크래시 없음 확인.
- CLAUDE.md/README의 "강제" 서술을 실제 구현(설정 활성화 시 강제)로 정직하게 수정.
- 커밋(사용자 승인 시).