Files
llm-wiki/docs/superpowers/specs/2026-06-06-spec-c-funnel-stats-no-silent-truncation-design.md

9.0 KiB

title, source_type, status, confidence, tags, last_reviewed
title source_type status confidence tags last_reviewed
Spec C — funnel stats + no-silent-truncation 계약 (G4) llm-generated draft medium
harness
claude-code
hooks
agents
design
automation
2026-06-06

Spec C — funnel stats + no-silent-truncation

상위 감사: 2026-06-06-harness-audit-report §3 G4. Spec B 다음 슬라이스. 확정 결정: Lean(계약+경량 funnel), 승인 완료.

1. 문제 (감사에서)

  • G4 (P4): funnel stats 가 거의 어디에도 없다. wiki_structure_lint.pyfail_by_type/fail_by_rule 만 유일. 어떤 judge·command 도 deep-research funnel(후보→처리→탈락→확정)을 안 냄. /ingest 가 promotable 항목을 silent 누락했는지 알 수 없고, coverage-auditor 가 관심사를 몇 개 열거했는지 보이지 않는다.

2. 핵심 원리 / 비목표

  • B 와 동일 철학: 결정론 코어(파싱·검증)는 wiki_rules.py(단일 SSOT), enforcement 는 SubagentStop hook(Claude·Codex)·G3 in-prompt(Antigravity). dispatch/계수는 controller/agent(self-report).
  • self-report stats 는 완벽한 진실이 아니지만, funnel 균형 검증(found = processed + dropped)과 dropped_reason 필수가 "조용한 누락"을 보이게 만든다 — 그게 G4 의 목표(관측가능성), 정확한 회계가 아니다.
  • 비목표: 모든 agent/command 에 stats 강제(YAGNI) → 핵심 4 agent + /ingest 만. branch-spec/project-spec Workflow → Spec D.

3. 설계 결정 (확정)

DD1 — Lean 범위

  • 결정론 코어: wiki_rules.validate_stats_block + claim_gate SubagentStop 검증(B 메커니즘 재사용).
  • 계약: rules/reporting-standards.md 에 no-silent-truncation 절.
  • funnel 방출: 핵심 4 agent(coverage-auditor·branch-depth-auditor·wiki-decision-researcher·wiki-research-lane) + /ingest(command, advisory).

DD2 — funnel 균형이 teeth

found == processed + dropped 가 깨지면 오류. dropped>0 인데 dropped_reason 비면 오류. 이 두 검사가 "조용한 truncation" 을 차단한다.

4. 아키텍처

.claude/hooks/
  wiki_rules.py          ← MOD. validate_stats_block() + 상수
  wiki_claim_gate.py     ← MOD. subagent_stop_gate 가 wiki-stats 마커 시 검증
  test_wiki_rules.py     ← MOD. TestStatsBlock
rules/reporting-standards.md  ← MOD. "No silent truncation" 계약 절
.claude/agents/{coverage-auditor,branch-depth-auditor,wiki-decision-researcher,wiki-research-lane}.md
                          ← MOD. Output 에 ## Stats wiki-stats 블록
.claude/commands/ingest.md  ← MOD. funnel(found→promoted→skipped+이유) 출력 계약

3-플랫폼 미러: 4 agent 의 Antigravity(.agents/plugins/.../*.md G3 통합) + Codex(.codex/agents/*.md+*.toml). (wiki-decision-researcher·wiki-research-lane 도 shared 9 agent 에 포함 → 미러 대상.)

4.1 wiki-stats 기계 블록

```wiki-stats
agent: coverage-auditor
found: 12
processed: 10
dropped: 2
dropped_reason: 2 out-of-scope (governing §4)
```
  • found = 후보로 식별한 총 항목(관심사/claim/후보/파일).
  • processed = 실제 판정한 수 — 결과 무관. covered·delegated·missing·verified·promoted 등 어떤 판정이든 "다뤘으면" processed. (coverage 의 missing 은 drop 이 아니라 판정된 gap 이므로 processed 에 포함된다.)
  • dropped = 판정하지 않고 의도적으로 제외한 수(범위 밖/bound 초과). dropped>0dropped_reason 필수.
  • 불변식: found = processed + dropped. (모든 식별 항목은 판정됨 이거나 제외됨 — 제3의 침묵 누락이 없다.)

4.2 wiki_rules.py 추가

STATS_FENCE_RE = re.compile(r"```wiki-stats\s*\n(.*?)\n```", re.S)


def parse_stats_block(text):
    m = STATS_FENCE_RE.search(text or "")
    if not m:
        return None
    out = {"agent": None, "kv": {}}
    for line in m.group(1).splitlines():
        line = line.strip()
        if not line:
            continue
        kv = re.match(r"([a-z_]+):\s*(.+)$", line)
        if kv:
            k, v = kv.group(1), kv.group(2).strip()
            if k == "agent":
                out["agent"] = v
            else:
                out["kv"][k] = v
    return out


def validate_stats_block(text):
    """(parsed, errors). parsed None → 마커 없음(통과). errors → SubagentStop 차단."""
    parsed = parse_stats_block(text)
    if parsed is None:
        return None, []
    errors = []
    if not parsed["agent"]:
        errors.append("wiki-stats 블록에 `agent:` 누락")
    nums = {}
    for k in ("found", "processed", "dropped"):
        try:
            nums[k] = int(parsed["kv"].get(k, ""))
        except ValueError:
            errors.append(f"wiki-stats `{k}` 는 정수여야 함 (funnel 필수 필드)")
    if len(nums) == 3:
        if nums["found"] != nums["processed"] + nums["dropped"]:
            errors.append(
                f"funnel 불균형: found({nums['found']}) ≠ processed({nums['processed']}) "
                f"+ dropped({nums['dropped']}) — 조용한 누락 의심"
            )
        if nums["dropped"] > 0 and not parsed["kv"].get("dropped_reason", "").strip():
            errors.append("dropped>0 인데 `dropped_reason` 누락 (no-silent-truncation 위반)")
    return parsed, errors

4.3 SubagentStop 강제 (wiki_claim_gate.subagent_stop_gate 확장)

verdict 검사 뒤에 추가(같은 패턴):

    sparsed, serr = wiki_rules.validate_stats_block(message)
    if sparsed is not None and serr and not event.get("stop_hook_active"):
        emit_block("wiki-stats 블록 오류:\n- " + "\n- ".join(serr))

마커 없는 출력 영향 없음. settings.json 무변경.

4.4 rules/reporting-standards.md — No silent truncation 계약

새 절 추가:

No silent truncation. 출력이 캡/슬라이스/top-N/skip 으로 coverage 를 bound 하면 드롭한 수 + 이유를 반드시 보고한다. funnel 은 균형해야 한다: found = processed + dropped. agent 출력은 wiki-stats 블록으로(SubagentStop 검증), command 는 ## Stats 절로 보고한다. 침묵 누락은 "전부 다뤘다" 는 거짓 신호다.

4.5 agent .md 편집 (4, 3-플랫폼 미러)

각 Output 에 ## Statswiki-stats 블록 추가. funnel 의미 매핑:

  • coverage-auditor: found=governing 관심사 수, processed=covered+delegated, dropped=명시 제외(있으면 이유).
  • branch-depth-auditor: found=점검한 claim/결정 수, processed=판정 완료, dropped=범위 밖(이유).
  • wiki-decision-researcher: found=식별 후보 수, processed=archive 한 수, dropped=bound 초과 제외(이유).
  • wiki-research-lane: found=슬라이스 파일 수, processed=정독+추출, dropped=무관/제외(이유).

Antigravity 는 G3 Output Schema 에 {{ }} 스타일로 통합("형식 외 응답 금지").

4.6 /ingest 출력 계약 (advisory)

출력 끝에 ## Stats funnel: found promotable 항목 / promoted(canonical 경로별) / skipped(항목+이유). daily/branch 특수처리는 추출/미추출 카운트를 명시(이미 "추출 안 한 항목 raw 보존" 규칙 있음 — 카운트만 추가).

5. 위험

위험 완화
agent 가 found/dropped 를 부정확 self-report funnel 균형 검증이 내부 모순은 잡음(완벽한 회계는 아님 — 관측가능성이 목표).
SubagentStop 가 비-stats subagent 오차단 parsed is None(마커 부재) 시 통과 — opt-in.
3-플랫폼 미러 drift B 와 동일 4 agent, grep-verify; 생성기 부재로 수기.
dropped_reason 강제가 noise dropped==0 이면 reason 불요 — 실제 누락 시에만.

6. 수용 기준 (검증 가능)

  1. validate_stats_block: 균형 블록 → errors=[]. found=12 processed=10 dropped=0 → 불균형 오류. dropped=2 + reason 없음 → 오류. 마커 부재 → (None, []).
  2. subagent_stop_gate: wiki-stats 불균형 마커 → exit 2. 균형 마커 → exit 0. 마커 없음 → exit 0.
  3. rules/reporting-standards.md 에 "No silent truncation" 절 존재(grep).
  4. 4 agent .md 각 Output 에 wiki-stats 블록 존재 + 3-플랫폼 미러(grep parity).
  5. /ingest.md## Stats funnel 계약 존재(grep).
  6. 기존 4 스위트 + 신규 stats 테스트 전부 green.

7. 구현 순서 (writing-plans)

  1. wiki_rules.py parse_stats_block/validate_stats_block (TDD) + test_wiki_rules TestStatsBlock.
  2. claim_gate.subagent_stop_gate 확장 + subprocess 회귀(균형/불균형/마커없음).
  3. rules/reporting-standards.md no-silent-truncation 절.
  4. 4 agent .md (Claude) ## Stats 블록.
  5. /ingest.md funnel 계약.
  6. 3-플랫폼 미러(4 agent) + grep parity.
  7. 전체 스위트 + §6 스모크.

8. 메모

  • wiki_rules.py 가 A(구조)·B(verdict)·C(stats) 의 공유 SSOT 로 계속 성장 — 전부 기계장치(스키마/검증), 정책은 agent .md+rules.
  • self-report 한계는 인정 — Spec C 는 "조용한 누락을 보이게" 가 목표(deep-research 의 결정론 funnel 은 Workflow=Spec D 에서만 가능).