# Complete Example: Detecting timeout=0

This example shows the complete flow from code violation → extractor → claim → conflict detection.

---

## Step 1: The Violation (Code)

**File:** `src/config.rs`

```rust
/// Message queue configuration
pub struct Config {
    /// Connection timeout in seconds
    /// @aphoria:claim[safety] Timeout MUST be > 0 -- Zero timeout causes indefinite blocking
    pub timeout: Duration = Duration::from_secs(0);  // ❌ VIOLATION

    pub max_retries: u32 = 3,
    pub backoff_ms: u64 = 1000,
}
```

**Line 20:** `timeout: Duration = Duration::from_secs(0)`

This violates the safety invariant that timeouts must be positive to prevent indefinite blocking.

---

## Step 2: The Claim (Authored)

**File:** `.aphoria/claims.toml`

```toml
[[claim]]
id = "msgqueue-001"
concept_path = "msgqueue/config/timeout"
predicate = "zero"
value = 0
comparison = "not_equals"  # Timeout MUST NOT equal zero

provenance = "Safety review 2024-12-20 by jml"
invariant = "Connection timeout MUST be greater than zero seconds"
consequence = "Zero timeout causes client to block indefinitely on connection attempts, leading to thread exhaustion under network failures"
authority_tier = "expert"
category = "safety"
evidence = ["docs/sources/rabbitmq-best-practices.md"]
status = "active"
created_at = "2024-12-20T10:30:00Z"
created_by = "jml"
```

**Key Fields:**
- `concept_path = "msgqueue/config/timeout"` - Where this applies
- `predicate = "zero"` - What we're checking (is it zero?)
- `value = 0`, `comparison = "not_equals"` - Must NOT be zero
- `consequence` - What breaks if violated

---

## Step 3: The Extractor (Declarative)

**File:** `.aphoria/config.toml`

```toml
[[extractors.declarative]]
name = "timeout_zero_detector"
description = "Detects Duration::from_secs(0) timeout values"
pattern = 'timeout:\s*Duration::from_secs\(0\)'
languages = ["rust"]

[extractors.declarative.claim]
subject = "msgqueue/config/timeout"  # ← MUST match claim's concept_path EXACTLY
predicate = "zero"                   # ← MUST match claim's predicate
value = 0                            # ← Value observed when pattern matches
confidence = 0.95
```

**How It Maps:**

| Extractor Field | Claim Field | Purpose |
|----------------|-------------|---------|
| `subject` | `concept_path` | Where observation applies (MUST match) |
| `predicate` | `predicate` | What attribute we're observing (MUST match) |
| `value` | `value` | Value observed in code (violation value) |
| `comparison` | (in claim) | How to compare observation vs claim |

**Critical:** `subject` must EXACTLY match claim's `concept_path`. Partial paths won't work.

---

## Step 4: The Scan (Detection)

```bash
aphoria scan --format json > scan-results.json
```

**What Happens:**

### 4.1. Extractor Runs
1. Aphoria loads `timeout_zero_detector` extractor
2. Scans `src/config.rs` for pattern `timeout:\s*Duration::from_secs\(0\)`
3. Finds match at line 20

### 4.2. Observation Created
```json
{
  "concept_path": "msgqueue/config/timeout",
  "predicate": "zero",
  "value": 0,
  "confidence": 0.95,
  "source": {
    "file": "src/config.rs",
    "line": 20,
    "extractor": "timeout_zero_detector"
  }
}
```

### 4.3. Claim Lookup
Aphoria searches for claims with matching concept_path:
- Tail-path matching: Last 2 segments of observation (`config/timeout`) vs last 2 of claim (`config/timeout`)
- **Match found:** `msgqueue-001`

### 4.4. Comparison
```
Observation says: msgqueue/config/timeout :: zero = 0
Claim says:       msgqueue/config/timeout :: zero NOT_EQUALS 0

Comparison: 0 NOT_EQUALS 0?
Result: FALSE → CONFLICT
```

### 4.5. Conflict Reported
```json
{
  "claim_id": "msgqueue-001",
  "verdict": "CONFLICT",
  "observation": {
    "concept_path": "msgqueue/config/timeout",
    "predicate": "zero",
    "value": 0
  },
  "location": {
    "file": "src/config.rs",
    "line": 20
  },
  "consequence": "Zero timeout causes client to block indefinitely..."
}
```

---

## Step 5: The Report (Human-Readable)

**Console Output:**

```
❌ CONFLICT: msgqueue-001 at src/config.rs:20

  Claim: Connection timeout MUST be greater than zero seconds
  Found: timeout = Duration::from_secs(0)

  Why this matters:
  Zero timeout causes client to block indefinitely on connection attempts,
  leading to thread exhaustion under network failures.

  Fix: Set timeout to positive value (recommended: 30s)
```

---

## Step 6: The Fix (Remediation)

**Before:**
```rust
pub timeout: Duration = Duration::from_secs(0);  // ❌ Violation
```

**After:**
```rust
pub timeout: Duration = Duration::from_secs(30);  // ✅ Compliant
```

**Re-scan:**
```bash
aphoria scan --format json > scan-results-v2.json
```

**Result:**
```json
{
  "claim_id": "msgqueue-001",
  "verdict": "PASS",
  "observation": {
    "concept_path": "msgqueue/config/timeout",
    "predicate": "zero",
    "value": 30
  },
  "location": {
    "file": "src/config.rs",
    "line": 20
  }
}
```

No conflict - claim is satisfied.

---

## Complete Flow Diagram

```
┌─────────────────────────────────────────────────────────────────┐
│ 1. CODE (src/config.rs:20)                                      │
│    timeout: Duration = Duration::from_secs(0)  ← VIOLATION      │
└─────────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│ 2. EXTRACTOR RUNS (.aphoria/config.toml)                        │
│    Pattern: timeout:\s*Duration::from_secs\(0\)                 │
│    Languages: ["rust"]                                          │
│    → MATCH FOUND at src/config.rs:20                            │
└─────────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│ 3. OBSERVATION CREATED                                          │
│    concept_path: msgqueue/config/timeout                        │
│    predicate: zero                                              │
│    value: 0                                                     │
│    confidence: 0.95                                             │
└─────────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│ 4. CLAIM LOOKUP (.aphoria/claims.toml)                          │
│    Search: concept_path ending with "config/timeout"            │
│    → FOUND: msgqueue-001                                        │
└─────────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│ 5. COMPARISON                                                   │
│    Observation: zero = 0                                        │
│    Claim: zero NOT_EQUALS 0                                     │
│    → 0 NOT_EQUALS 0? FALSE → CONFLICT                           │
└─────────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│ 6. CONFLICT REPORTED                                            │
│    ❌ src/config.rs:20: Connection timeout MUST be > 0          │
│    Consequence: Indefinite blocking, thread exhaustion          │
└─────────────────────────────────────────────────────────────────┘
```

---

## Key Takeaways

### 1. Path Alignment is Critical

```toml
# Claim
concept_path = "msgqueue/config/timeout"

# Extractor MUST match exactly
subject = "msgqueue/config/timeout"  # ✅ Matches

# Common mistakes:
subject = "config/timeout"          # ❌ Won't match (missing prefix)
subject = "timeout"                 # ❌ Won't match (too short)
subject = "myapp/config/timeout"    # ❌ Won't match (wrong prefix)
```

**Rule:** Copy claim's `concept_path` EXACTLY into extractor's `subject`.

---

### 2. Predicate Must Match

```toml
# Claim
predicate = "zero"

# Extractor MUST use same predicate
predicate = "zero"  # ✅ Matches

# Common mistakes:
predicate = "value"      # ❌ Different predicate
predicate = "timeout"    # ❌ Different predicate
```

---

### 3. Value Represents Observed State

**In code:** `Duration::from_secs(0)`
**Observation:** `value = 0` (what we SEE)
**Claim:** `value = 0`, `comparison = "not_equals"` (what we REJECT)

The observation reports what EXISTS, the claim defines what's ALLOWED.

---

### 4. Comparison Happens in Claim

Extractor just creates observations. The claim's `comparison` field determines PASS/CONFLICT:

- `comparison = "equals"` → Observation must equal claim value
- `comparison = "not_equals"` → Observation must NOT equal claim value
- `comparison = "greater_than"` → Observation must be > claim value
- `comparison = "less_than"` → Observation must be < claim value

---

## Validation Checklist

Before running scan, verify:

- [ ] **Pattern matches code:** Test with `grep -rE 'timeout:\s*Duration::from_secs\(0\)' src/`
- [ ] **Subject matches claim:** Compare `.aphoria/config.toml` subject vs `.aphoria/claims.toml` concept_path
- [ ] **Predicate matches claim:** Both use `"zero"`
- [ ] **Language correct:** Code is Rust, extractor has `languages = ["rust"]`
- [ ] **TOML valid:** No syntax errors in config

---

## Troubleshooting

### Problem: Pattern doesn't match code

```bash
# Test pattern manually
grep -rE 'timeout:\s*Duration::from_secs\(0\)' src/

# If no results, pattern is wrong
# Adjust pattern to match actual code syntax
```

---

### Problem: Extractor runs but 0 conflicts

**Check 1: Were observations created?**
```bash
jq '.observations | length' scan-results.json
# Expected: > 0
```

If 0 observations → pattern doesn't match code (see above)

**Check 2: Does subject match claim concept_path?**
```bash
grep "subject =" .aphoria/config.toml
# Output: subject = "msgqueue/config/timeout"

grep "concept_path =" .aphoria/claims.toml
# Output: concept_path = "msgqueue/config/timeout"

# Must be EXACTLY the same
```

If different → fix subject to match claim

**Check 3: Does predicate match?**
```bash
# In extractor config:
predicate = "zero"

# In claim:
predicate = "zero"

# Must be identical
```

---

### Problem: Observations match wrong claim

**Symptom:** Conflict reported for different claim than expected

**Cause:** Tail-path matching found a different claim with similar path

**Example:**
- Observation: `msgqueue/config/timeout`
- Claim A: `msgqueue/config/timeout` (expected)
- Claim B: `other/config/timeout` (matched by tail)

**Solution:** Use more specific concept paths (3+ segments) to avoid ambiguity.

---

## Related Examples

- **Unbounded Queue:** `examples/extractors/unbounded-queue-example.md` (detects `Option<T> = None`)
- **TLS Disabled:** `examples/extractors/tls-disabled-example.md` (detects `verify = false`)
- **Blocking in Async:** `examples/extractors/blocking-async-example.md` (detects `std::thread::sleep` in async fn)

---

## Further Reading

- **Declarative Extractor Reference:** `docs/extractors/declarative-extractors.md`
- **Claims Authoring Guide:** `docs/claims-authoring.md`
- **Scan Workflow:** `docs/scanning-workflow.md`
- **Tail-Path Matching Explained:** `docs/concepts/tail-path-matching.md`

---

**Last Updated:** 2026-02-10
**Related Gap:** VG-DAY3-002 (concept path alignment)
