Completes Task #3 of httpclient dogfooding with 100% detection rate (7/7 violations). ## New Extractors - **OptionBoundsExtractor**: Detects Option<T> fields set to None (unbounded) - **OptionValueExtractor**: Extracts values from Some(n) for threshold checks Both extractors use context-aware pattern matching to understand Rust Option<T> semantics, which declarative extractors cannot handle. ## Implementation **Files Created**: - applications/aphoria/src/extractors/option_bounds.rs (257 lines) - applications/aphoria/src/extractors/option_value.rs (277 lines) - applications/aphoria/docs/examples/extractors/programmatic-option-semantics.md **Files Modified**: - applications/aphoria/src/extractors/mod.rs - Added module declarations - applications/aphoria/src/extractors/registry.rs - Registered extractors - applications/aphoria/dogfood/httpclient/.aphoria/claims.toml - Added 4 claims - applications/aphoria/dogfood/httpclient/TASK-1-SUMMARY.md - Task #3 completion ## Results | Metric | Value | |--------|-------| | Detection Rate | 100% (7/7 violations) | | Improvement | +29 percentage points (from 71%) | | New Violations | 2 (max_redirects, max_retries unbounded) | | Unit Tests | 13 (all passing) | ## Two-Claim Strategy For each bounded Option<T> field: 1. **configured** claim - Detects None (unbounded) 2. **max_value** claim - Validates Some(n) threshold Example: - `max_redirects: None` → CONFLICT (not configured) - `max_redirects: Some(20)` → CONFLICT (exceeds 10) - `max_redirects: Some(5)` → PASS ## Enterprise Quality ✓ Proper error handling (no unwrap/expect) ✓ Comprehensive tests (6+7 unit tests) ✓ Full documentation with examples ✓ Reusable for 10+ similar patterns ✓ Screening patterns for performance ## Cachewrap Dogfood Also includes complete cachewrap dogfood exercise: - 10 claims for Redis cache wrapper - Day 1-5 summaries - Full retrospective and evaluation - Declarative extractors for all patterns Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
210 lines
7.7 KiB
JSON
210 lines
7.7 KiB
JSON
{
|
|
"claim_verification": [
|
|
{
|
|
"claim_id": "httpclient-connect-timeout-001",
|
|
"concept_path": "httpclient/connect_timeout",
|
|
"explanation": "Expected 10, found: Text(\"connect_timeout: Duration::from_secs(60)\"), Text(\"connect_timeout: Duration::from_secs(10)\")",
|
|
"invariant": "TCP connection timeout MUST NOT exceed 10 seconds",
|
|
"verdict": "CONFLICT"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-request-timeout-001",
|
|
"concept_path": "httpclient/request_timeout",
|
|
"explanation": "Expected 30, found: Text(\"request_timeout: Duration::from_secs(120)\"), Text(\"request_timeout: Duration::from_secs(30)\")",
|
|
"invariant": "HTTP request timeout MUST NOT exceed 30 seconds",
|
|
"verdict": "CONFLICT"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-read-timeout-001",
|
|
"concept_path": "httpclient/read_timeout",
|
|
"explanation": "No matching observation found",
|
|
"invariant": "Response body read timeout MUST NOT exceed 30 seconds",
|
|
"verdict": "MISSING"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-idle-timeout-001",
|
|
"concept_path": "httpclient/idle_timeout",
|
|
"explanation": "Expected true, found: Boolean(false)",
|
|
"invariant": "Idle connection timeout MUST be configured",
|
|
"verdict": "CONFLICT"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-idle-timeout-default-001",
|
|
"concept_path": "httpclient/idle_timeout",
|
|
"explanation": "No matching observation found",
|
|
"invariant": "Idle timeout default SHOULD be 60 seconds",
|
|
"verdict": "MISSING"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-tls-cert-validation-001",
|
|
"concept_path": "httpclient/tls/certificate_validation",
|
|
"explanation": "Expected true, found: Boolean(false)",
|
|
"invariant": "HTTPS connections MUST validate server certificates",
|
|
"verdict": "CONFLICT"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-tls-enabled-001",
|
|
"concept_path": "httpclient/tls/enabled",
|
|
"explanation": "No matching observation found",
|
|
"invariant": "HTTPS SHOULD be enabled by default for all connections",
|
|
"verdict": "MISSING"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-tls-min-version-001",
|
|
"concept_path": "httpclient/tls/min_version",
|
|
"explanation": "Expected 1.2, found: Text(\"1.0\")",
|
|
"invariant": "TLS version MUST be >= 1.2 (TLS 1.0/1.1 deprecated)",
|
|
"verdict": "CONFLICT"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-tls-ciphers-001",
|
|
"concept_path": "httpclient/tls/cipher_suites",
|
|
"explanation": "No matching observation found",
|
|
"invariant": "TLS cipher suites SHOULD use modern ciphers only",
|
|
"verdict": "MISSING"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-max-redirects-001",
|
|
"concept_path": "httpclient/max_redirects",
|
|
"explanation": "No matching observation found",
|
|
"invariant": "HTTP redirect limit MUST NOT exceed 10",
|
|
"verdict": "MISSING"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-redirect-loop-001",
|
|
"concept_path": "httpclient/redirects/loop_detection",
|
|
"explanation": "No matching observation found",
|
|
"invariant": "Redirect loop detection MUST be implemented",
|
|
"verdict": "MISSING"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-retry-max-001",
|
|
"concept_path": "httpclient/retry/max_attempts",
|
|
"explanation": "No matching observation found",
|
|
"invariant": "Retry attempts MUST NOT exceed 3",
|
|
"verdict": "MISSING"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-retry-backoff-001",
|
|
"concept_path": "httpclient/retry/backoff",
|
|
"explanation": "No matching observation found",
|
|
"invariant": "Retry backoff MUST use exponential strategy",
|
|
"verdict": "MISSING"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-retry-idempotent-001",
|
|
"concept_path": "httpclient/retry/idempotent_only",
|
|
"explanation": "No matching observation found",
|
|
"invariant": "Retries MUST only apply to idempotent methods",
|
|
"verdict": "MISSING"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-retry-post-excluded-001",
|
|
"concept_path": "httpclient/retry/post_excluded",
|
|
"explanation": "No matching observation found",
|
|
"invariant": "POST requests MUST be excluded from automatic retries",
|
|
"verdict": "MISSING"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-metrics-enabled-001",
|
|
"concept_path": "httpclient/metrics/enabled",
|
|
"explanation": "No matching observation found",
|
|
"invariant": "Metrics collection SHOULD be enabled for production HTTP clients",
|
|
"verdict": "MISSING"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-metrics-exposed-001",
|
|
"concept_path": "httpclient/metrics/exposed",
|
|
"explanation": "No matching observation found",
|
|
"invariant": "Core HTTP metrics MUST be exposed: request_count, active_connections, latency_p99, error_rate",
|
|
"verdict": "MISSING"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-pool-size-001",
|
|
"concept_path": "httpclient/pool_size",
|
|
"explanation": "No matching observation found",
|
|
"invariant": "Connection pool size SHOULD be 50-100 per host in production",
|
|
"verdict": "MISSING"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-pool-default-size-001",
|
|
"concept_path": "httpclient/pool/default_size",
|
|
"explanation": "No matching observation found",
|
|
"invariant": "Default pool size SHOULD be 10 connections per host",
|
|
"verdict": "MISSING"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-connection-pooling-001",
|
|
"concept_path": "httpclient/sessions/connection_pooling",
|
|
"explanation": "No matching observation found",
|
|
"invariant": "Connection pooling SHOULD be enabled for multi-request scenarios",
|
|
"verdict": "MISSING"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-user-agent-001",
|
|
"concept_path": "httpclient/headers/user_agent",
|
|
"explanation": "No matching observation found",
|
|
"invariant": "User-Agent header MUST be sent with all requests",
|
|
"verdict": "MISSING"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-error-handling-001",
|
|
"concept_path": "httpclient/error_handling/request_failure",
|
|
"explanation": "No matching observation found",
|
|
"invariant": "HTTP request failures MUST return Result, NEVER panic",
|
|
"verdict": "MISSING"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-max-redirects-configured",
|
|
"concept_path": "httpclient/max_redirects",
|
|
"explanation": "Expected true, found: Boolean(false)",
|
|
"invariant": "Redirect limit MUST be configured (not unbounded)",
|
|
"verdict": "CONFLICT"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-max-redirects-threshold",
|
|
"concept_path": "httpclient/max_redirects",
|
|
"explanation": "No matching observation found",
|
|
"invariant": "Redirect limit MUST NOT exceed 10",
|
|
"verdict": "MISSING"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-max-retries-configured",
|
|
"concept_path": "httpclient/retry/max_attempts",
|
|
"explanation": "Expected true, found: Boolean(false)",
|
|
"invariant": "Retry limit MUST be configured (not unbounded)",
|
|
"verdict": "CONFLICT"
|
|
},
|
|
{
|
|
"claim_id": "httpclient-max-retries-threshold",
|
|
"concept_path": "httpclient/retry/max_attempts",
|
|
"explanation": "No matching observation found",
|
|
"invariant": "Retry attempts MUST NOT exceed 3",
|
|
"verdict": "MISSING"
|
|
}
|
|
],
|
|
"conflicts": [],
|
|
"deprecated_usages": [],
|
|
"drifts": [],
|
|
"project": "httpclient",
|
|
"scan_id": "scan-1770791729261",
|
|
"strict": false,
|
|
"summary": {
|
|
"acks": 0,
|
|
"authority_conflicts": 0,
|
|
"blocks": 0,
|
|
"claims_conflict": 7,
|
|
"claims_missing": 19,
|
|
"claims_pass": 0,
|
|
"claims_total": 26,
|
|
"claims_unclaimed": 16,
|
|
"deprecated_usages": 0,
|
|
"drifts": 0,
|
|
"files_scanned": 14,
|
|
"flags": 0,
|
|
"observations_extracted": 25,
|
|
"observations_recorded": 0,
|
|
"passes": 0
|
|
}
|
|
}
|