- name
- absence-measurement-validity-check
- description
- Before concluding "X doesn't happen" in an integrated system, verify the pipeline that would produce X is actually running. Measuring zero is trivially easy when the producer is off.
- version
- 1.0.0
- task_types
- ["debugging","design","evaluation"]
- triggers
- [{"pattern":"claim that a tool / feature / behavior isn't being used / fires zero times"},{"pattern":"longrun / E2E experiment showing 0 count of some event"},{"pattern":"prompt engineering attempt judged 'failed' because LLM didn't do X"}]
- category
- trap
# Absence Measurement Validity Check
## The Pattern
You run an experiment and observe `X = 0` (tool never called, artifact never created, path never hit). You conclude "X doesn't work / the prompt failed / the model has a limit".
This conclusion is only valid if the pipeline that would produce X is actually running. Otherwise you measured a system that can't produce X *at all*, not a system that chose not to.
## Real Example (BSNexus Session 10)
Goal: prove or disprove that GLM-4.7-flash can be prompted to call a verification tool (`shell_exec`) via TDD-style instructions.
Measurement loop, three iterations:
| Run | Prompt strategy | `shell_exec_ran` count | Conclusion I drew |
|---|---|---|---|
| v8 | direct "MUST verify" instruction | 0 | "direct instruction doesn't work" |
| v9 | Q1/Q2/Q3 CoT scaffold | 0 | "CoT doesn't work" |
| v10 | E2E reframing + file_read banned | 0 | "prompt-layer ceiling hit" |
Three "failed" prompt strategies in a row. Looks decisive.
What I missed: the user pushed me to run V11 patiently and query the DB directly. Results:
- **Assigned agents**: Designer 8, CTO 4, Marketer 3, Backend_Engineer 1, Frontend_Engineer 1, QA_Lead 1โฆ (routing was **working**)
- **`file_read` calls**: 22 (passive workers were **active**, calling other tools)
- **`create_screen` / `file_write`** also firing normally
- **`shell_exec`**: 0
The passive-worker pipeline was fine. The `shell_exec` absence was a real GLM tool-preference signal, but I could not have known that from v8/v9/v10 โ I didn't check whether the pipeline that *could* use `shell_exec` was even dispatching. For all I knew from those three runs, every task was self-assigned to the planning agent and no passive worker was running at all.
## Why This Traps You
- `X == 0` reads like a clean data point. It feels like certainty.
- Positive signals are loud (logs, state transitions, artifacts); absence is invisible. There's nothing to question, only something missing.
- Repeating the same measurement under different prompts **does not** increase confidence in the conclusion. All three runs can be contaminated by the same upstream gap.
- A later negative result that's real confirms the wrong early reasoning, so the lesson never surfaces.
## The Validity Check โ Before Claiming Absence
Before writing down "X didn't happen โ Y caused it", verify three layers:
1. **Producer liveness**: is the process that would emit X actually dispatched/active in this run? Check logs for "its kind of event happened at all", not just the specific signal.
2. **Sibling signals**: does the same code path emit *anything*? If the producer fires same-category events successfully (other tool calls, other file writes), you're comparing "made a choice not to" vs "never got the chance". Different conclusion, different fix.
3. **Artifact vs state**: state transitions (`status=done`, `phase=completed`) are cheap to fake. File-level / command-level artifacts are what matter. Query the DB / filesystem directly; don't trust API responses that might be showing creator vs assignee, cached vs live, etc.
Only after those three layers check out is `X = 0` evidence of intentional absence rather than blocked pipeline.
## Heuristic
> If your measurement is "how often did the LLM / agent / process do X?", and X is zero across N runs, your first follow-up question must be: **did the thing that produces X even execute?** Query the producer's *sibling* signals. If the siblings are also zero, the pipeline is dead; the experiment was invalid and the prompt / model / feature has not actually been tested.
## When This Skill Applies
- Multi-agent systems where a tool might be offered but never called
- Any E2E test claiming "feature X didn't fire"
- Prompt engineering iterations producing the same null count
- Backend enforcement rules that supposedly triggered no rejections
- Dashboards showing a suspicious zero when activity is expected
## When It Does NOT Apply
- Unit tests with synthetic input (the pipeline is explicit)
- Experiments where the producer is obviously controlled (the call site is in your test code)
## Related Skills
- `systematic-debugging` โ broader root-cause investigation; this skill is the "check absence first" corner of it.
- `verification-before-completion` โ the inverse problem (premature success claims); this one covers premature failure claims.
- `test-against-source-contracts` โ when API field semantics confuse you (e.g., `agent_name` returning creator vs assignee), this ties in.
---
## ์ฌ๋ก โ **์ธก์ ๋ช
๋ น ์์ฒด๊ฐ ์คํจํ์ ๋** (2026-08-17)
์ด ์คํฌ์ "0์ ๋ง๋ค์ด๋ด๋ *์์ฐ์*๊ฐ ๋๋๊ฐ"๋ฅผ ๋ฌป๋๋ค. ๊ทธ ํ ๊ฒน ์๋์ ๋ ํํ ๊ฒ์ด ์๋ค:
**์ธก์ ๋ช
๋ น ์์ฒด๊ฐ ๋๊ธฐ๋ ํ๋๊ฐ.**
BSVibe ์ค์ธก. ๋ ํฌ ๋ฃจํธ์ ์์ ํ์ผ 5๊ฐ๋ฅผ ์ง์ฐ๊ธฐ ์ ์ ์ฐธ์กฐ๋ฅผ ํ์ธํ๋ ค๊ณ :
```bash
grep -rn "_patch_judge\|_patch_parse" --include=*.py backend/ tests/
# โ (zsh) no matches found: --include=*.py โ ๋ช
๋ น์ด ์ฃฝ์๋ค
```
zsh ๊ฐ `--include=*.py` ๋ฅผ ๊ธ๋กญ์ผ๋ก ๋จน์ด **๋ช
๋ น์ด ์คํ๋์ง ์์๋ค.** ์ถ๋ ฅ์ ๋น์ด ์์๊ณ ,
๋๋ ๊ทธ ๊ณต๋ฐฑ์ **"์๋ฌด๋ ์ฐธ์กฐํ์ง ์๋๋ค"** ๋ก ์ฝ๊ณ ํ์ผ์ ์ง์ ๋ค.
> **๋น ์ถ๋ ฅ์ "์์"์ด ์๋๋ค. ๊ทธ ๋ช
๋ น์ด ์คํ๋๋ค๋ ์ฆ๊ฑฐ๊ฐ ๋จผ์ ํ์ํ๋ค.**
์ ๋๋ก ๋ค์ ๋๋ฆฌ๋ 13๊ฐ ํ์ผ์ด ๋งค์นญ๋๋ค(์ ๋ถ `monkeypatch_resolver` ๊ฐ์ ๋ฌด๊ดํ ๋ถ๋ถ
๋ฌธ์์ด์ด๋ผ ์ญ์ ๋ ๊ฒฐ๊ณผ์ ์ผ๋ก ์์ ํ์ง๋ง, **๊ทธ๊ฑด ์ด์ด์๋ค**). ์ญ์ ยท๋จธ์งยท๋ฐฐํฌ์ฒ๋ผ **๋น๊ฐ์ญ**
๋์ ์์์๋ ์ด ํ์ธ์ด ํ์๋ค.
**์ฒ๋ฐฉ**
- ๋ถ์ฌ๋ฅผ ๊ทผ๊ฑฐ๋ก ํ๋ํ๊ธฐ ์ ์ **์์ฑ ๋์กฐ**๋ฅผ ํ๋ ๋ผ์๋ผ โ ๋ฐ๋์ ๋งค์นญ๋ ๋ฌธ์์ด๋ก ๊ฐ์ ๋ช
๋ น์
๋๋ ค **0์ด ์๋ ๊ฒฐ๊ณผ**๊ฐ ๋์ค๋์ง ๋ณธ๋ค. ์ ๋์ค๋ฉด ๋ช
๋ น์ด ์ฃฝ์ ๊ฒ์ด๋ค.
- ์ข
๋ฃ ์ฝ๋๋ฅผ ๋ด๋ผ. `grep` ์ "๋งค์นญ ์์"์ 1, **์ค๋ฅ๋ 2**๋ค. ์
ธ์ด ์ฃฝ์์ผ๋ฉด ๊ทธ๋ง์ ์ ๋์จ๋ค.
- ์
ธ ํ์ฅ์ด ๊ฐ์
ํ ์ ์๋ ์ธ์(`*`, `?`, `[`)๋ **๋ฐ์ดํ๋ก ๊ฐ์ธ๋ผ**.
```bash
# ์์ฑ ๋์กฐ๋ฅผ ๊ฐ์ด ๋๋ฆฐ๋ค
grep -rn "_patch_judge" backend tests; echo "exit=$?"
grep -rn "def test_" tests | head -1 # โ ์ด๊ฒ ๋น๋ฉด ๋ช
๋ น์ด ์ฃฝ์ ๊ฒ์ด๋ค
```
**Detection**: ๋น ์ถ๋ ฅ์ ๊ทผ๊ฑฐ๋ก **์ญ์ /์ ๋ฆฌ/๋กค๋ฐฑ**์ ํ๋ ค ํ๋ค ยท ์
ธ ์ค๋ฅ ๋ฉ์์ง๊ฐ ์ถ๋ ฅ์ ์์ฌ
์๋๋ฐ ๊ฒฐ๊ณผ๋ถ๋ง ์ฝ์๋ค ยท `tail`/`head` ๋ก ์๋ผ ๋ณด๋๋ผ ์์ ์๋ฌ๋ฅผ ๋ชป ๋ดค๋ค(๊ฐ์ ์ธ์
์์
`ruff` ์ F821 ์ `tail` ๋ก ๋์ณ 85๊ฐ ํ
์คํธ๋ฅผ ๊นจ๋จ๋ ธ๋ค).
---
## ์ฌ๋ก โ **์งํ๋ก ๊ณ ๋ฅธ ํ
์ด๋ธ ์์ฒด์ producer ๊ฐ ์์๋ค** (BSVibe, 2026-08-20)
๊ฐ์ฅ ๋น์ผ ํํ๋ค: ๋ถ์ฌ๋ฅผ ์๋ชป ์ฝ์ ๊ฒ ์๋๋ผ, **๋ถ์ฌ๋ฅผ ์ฌ๋ ค๊ณ ๊ณ ๋ฅธ ๊ณ๊ธฐํ์ด ์ ์ด์ ์ ๊ฝํ
์์๋ค.**
`IngestCompiler` ๊ฐ `retriever=` ๋ฅผ ๋ชป ๋ฐ์ ๋๋ฉ์ด ์๋ ๊ฒ์ ๊ณ ์น๊ณ , PR ๋ณธ๋ฌธ์ ๊ฒ์ฆ ๋ฐฉ๋ฒ์
์ด๋ ๊ฒ ์ ์๋ค โ *"๋ฐฐํฌ ํ `ingest_batches` ์ `notes_updated` ๊ฐ 0์์ ์์ง์ด๋์ง ๋ณด๋ผ."*
์ค์ ๋ก ์ฌ๋ณด๋:
| | |
|---|---|
| `ingest_batches` ํ ์ | **0** |
| `IngestBatchRecorder` ํ๋ก๋์
๊ตฌํ์ฒด | **0** (Protocol ์ ์ธ๋ง) |
| `batch_recorder=` ๋ฅผ ๋๊ธฐ๋ ์์ฑ ์ง์ | **0** |
**๋ด๊ฐ ๋ฐฉ๊ธ ๊ณ ์น ๊ฒฐํจ๊ณผ ๋๊ฐ์ ๊ฒฐํจ์ด ๊ฐ์ ์์ฑ์์ ๋ฐ๋ก ์ ์ธ์์ ์์๋ค.** ๊ทธ ์งํ๋
์๋ฆฌ์ ์์ํ 0์ด๋ฏ๋ก, ๋ด ์์ ์ด ๋์ํ๋ ์ ํ๋ "๊ฒ์ฆ ์คํจ"๋ก ์ฝํ์ ๊ฒ์ด๋ค.
> **์งํ๋ฅผ ๊ณ ๋ฅด๋ ๊ฒ๋ ์ธก์ ์ด๋ค. ๊ณ๊ธฐํ์ producer ๊ฐ ์๋์ง๋ถํฐ ์ธ๋ผ.**
**์ฒ๋ฐฉ** โ ๊ฒ์ฆ ์งํ(ํ
์ด๋ธ/์นด์ดํฐ/๋ก๊ทธ/์ด๋ฒคํธ)๋ฅผ ์ ํ ๋ **์ฐ๊ธฐ ์ ์** ํ์ธ:
```bash
# 1) ๊ทธ ์งํ์ ์ง๊ธ ๊ฐ์ด ๋ค์ด ์๋ (์ญ๋ ํ ๋ฒ์ด๋ผ๋)
psql -tAc "select count(*) from <metric_table>"
# 2) ์๋ค๋ฉด โ ํ๋ก๋์
producer ๊ฐ ์กด์ฌํ๋
grep -rn "<RecorderClass>(" backend/ | grep -v tests # 0๊ฐ๋ฉด ์งํ๊ฐ ์๋๋ผ ์ ๋ น์ด๋ค
```
0์ด๋ฉด **์งํ๋ฅผ ๋ฐ๊พธ๊ฑฐ๋, ์งํ๋ฅผ ๋จผ์ ๋ฐฐ์ ํ๋ผ.** ๊ทธ๋ฆฌ๊ณ "๋ฐฐ์ ํ ์ง ์ง์ธ์ง"๋ ๋๊ฐ ์ฌ๋์
๊ฒฐ์ ์ด๋ค โ producer ์๋ ํ
์ด๋ธ์ *"์ฐ๊ฒฐ์ด ๋น ์ง ๊ฒ"* ์ด ์๋๋ผ *"๋ง๋ ์ ์๋ ๋ ๋ฒ์งธ ํํ"*
์ผ ์ ์๊ณ , ๊ทธ๋ฌ๋ฉด ๋ต์ ์ญ์ ๋ค.
### ๊ณ๊ฐ์ง ํจ์ โ ๋ฐฐํฌ๋ ๋ก๊ทธ ํ์คํ ๋ฆฌ๋ฅผ ์๋ฅธ๋ค
๊ฐ์ ๊ฒ์ฆ์์ `docker logs <worker> --since 12h | grep ingest_compile` ๊ฐ **0** ์ ๋๋ค.
๊ฒฐํจ์ผ๋ก ์ฝ์ ๋ปํ๋๋ฐ, ์์ธ์ **๋ฐฐํฌ๊ฐ ์ปจํ
์ด๋๋ฅผ ์ฌ์์ฑ**ํด์ ๊ทธ ์ด์ ๋ก๊ทธ๊ฐ ์์ ์๋
๊ฒ์ด์๋ค. ์ปจํ
์ด๋ ๋ก๊ทธ๋ก "์ผ์ด๋ ์ ์๋ค"๋ฅผ ์ฃผ์ฅํ๋ ค๋ฉด **์ปจํ
์ด๋ ์์ ์๊ฐ๋ถํฐ์ ์ฐฝ**๋ง
์ ํจํ๋ค.
```bash
docker inspect -f '{{.State.StartedAt}}' <container> # ์ด ์๊ฐ ์ดํ๋ง ๊ทผ๊ฑฐ๊ฐ ๋๋ค
```
### ๋ ํ๋ โ ๋ก๊ทธ๊ฐ ์๋ค๊ณ ๊ฒฝ๋ก๊ฐ ์ ๋๋ ๊ฒ ์๋๋ค
`ingest_compile_batch_complete` ๊ฐ ์ ๋ณด์ฌ์ "settle ์ด ์ปดํ์ผ๋ฌ๋ฅผ ์ ํ๋ค"๋ก ๊ฒฐ๋ก ๋ผ ๋ปํ๋ค.
์ฝ๋๋ฅผ ์ฝ์ผ๋ settle ์ `compile_batch` ๊ฐ ์๋๋ผ `extract_entity_names` ๋ฅผ ํ๋๋ฐ,
**๊ทธ ๋ฉ์๋๋ ๊ฐ์ `_find_related` ๋ฅผ ๋ถ๋ฅธ๋ค** โ ๋ก๊ทธ๋ง ์ ๋จ๊ธธ ๋ฟ ๊ฒฝ๋ก๋ ๋๋ค.
**๊ด์ธก ์ฅ์น์ ๋ถ์ฌ๋ฅผ ๋์์ ๋ถ์ฌ๋ก ์ฝ์ง ๋ง๋ผ.**
### ์คํ ํ(arm)์ด **์ ๋ถ ๋๊ฐ์ด** 0 ์ ๋ด๋ฉด ์ฒ์น๊ฐ ์๋๋ผ ํ๋ค์ค๋ฅผ ์์ฌํ๋ผ
BSVibe 2026-08-26. ํ๋กฌํํธ ๋ฒ์ ์ ๋ณ์๋ก A/B ๋ฅผ ๋๋ ธ๋ค โ arm 4๊ฐ ร ์
๋ ฅ 3๊ฑด = 12์
.
**์ ๋ถ ๋ช
๋ น 0๊ฐ**๊ฐ ๋์๋ค. "์ ํ๋กฌํํธ๋ ์ด ๊ฒ์ฌ๋ฅผ ๋ผ ๋ฅ๋ ฅ์ด ์๋ค"๋ก ์ฝํ ์ ์๋
๋ชจ์์ด์๊ณ , ์ค์ ๋ก ๊ทธ๋ ๊ฒ ์ ์ ๋ปํ๋ค.
์์ธ์ ์ฒ์น๊ฐ ์๋๋ผ **๋ฐฐ์ **์ด์๋ค: ํ๋ก๋ธ๊ฐ resolver ์ redis ๋ฅผ ์ ๋๊ฒจ์ 12์
์ ๋ถ
`ExecutorAdapterUnavailable` ๋ก ์ฃฝ์๋ค. LLM ์ด ํ ๋ฒ๋ ํธ์ถ๋์ง ์์๋ค.
**๊ฐ๋ณ ๊ท์น โ ์ฒ์น ํจ๊ณผ๋ ๊ท ์ผํ ์ ์๋ค.**
| ๊ด์ธก | ์ฝ๋ ๋ฒ |
|---|---|
| arm ๋ง๋ค 0 ์ **์ ๋๊ฐ ๋ค๋ฅด๋ค** | ์ง์ง ์ฒ์น ํจ๊ณผ์ผ ์ ์๋ค |
| **๋ชจ๋ arm ์ด ์ ํํ ๊ฐ์ 0** | ํ๋ค์ค/๋ฐฐ์ ๊ณ ์ฅ. ์ฒ์น๋ ์์ง ์ ์ฌ๋ดค๋ค |
| ํต์ (control) arm ๊น์ง 0 | ํ์ ์ ์ผ๋ก ํ๋ค์ค ๊ณ ์ฅ โ ํต์ ๋ ๋๋ ๊ฒ์ด์ด์ผ ํ๋ค |
**How to apply**
- ์คํ ๊ฒฐ๊ณผ์ **์ค๋ฅ ํ๋๋ฅผ ๋จ๊ธฐ๊ณ ์ง๊ณ์ ํจ๊ป ์ถ๋ ฅํ๋ผ**. `error=None` ์ธ ์
์๋ฅผ
๋จผ์ ๋ณด๊ณ ๋์ ํจ๊ณผ๋ฅผ ๋ด๋ผ. ์์ธ๋ฅผ ์ผ์ผ 0 ์ผ๋ก ๋ง๋ค๋ฉด ๊ณ ์ฅ์ด ๋ฐ๊ฒฌ์ผ๋ก ์์ฅํ๋ค.
- **์๋ ค์ง-์์ฑ arm ์ ๋ฐ๋์ ํ๋ ๋ฃ์ด๋ผ.** ์ง๊ธ ์ ๋๋ ์กฐ๊ฑด์ด 0 ์ ๋ด๋ฉด ๊ทธ ํ์
ํต์งธ๋ก ๋ฒ๋ฆฐ๋ค.
- n=1 ์ค๋ชจํฌ๋ฅผ ๋จผ์ ๋๋ ค **๋ฐฐ์ ์ ํ์ธ**ํ๊ณ ๋์ ๋ณธ ์คํ์ ๋น์ฉ์ ์จ๋ผ.
- ๊ด๋ จ: [[ab-on-a-few-items-cannot-recover-a-population-rate]] โ ํ๋ค์ค๊ฐ ๋ฉ์ฉกํด๋
**๋๊ตฌ ์์ฒด๊ฐ ์ง๋ฌธ์ ์ ๋ง์** ์ ์๋ค(๋ชจ์ง๋จ ๋น์จ์ ๋ช ๊ฐ ํญ๋ชฉ์ผ๋ก ๋ฌผ์ ๋).
Auf GitHub ansehen