# T0.4: Pi & Error Classification ## Scope Implement `action/skills.go` with `PrepareSkillsActivity` and `classifyPiErr` for skill prep via homelab API. ## Implementation ### File: `action/skills.go` ```go type SkillRef struct { Name string URL string } type PrepareSkillsInput struct { Skills []SkillRef StreamTimeout time.Duration } func PrepareSkillsActivity(ctx context.Context, in PrepareSkillsInput) error // For each skill, run: pi clone-or-fetch $skill.URL // Pass --stream-timeout=$StreamTimeout to pi // Each skill behind its own lock (not orchestrator.lock) // On error, return classified error (see below) func classifyPiErr(err error) error // 4xx (400-499): NonRetryableApplicationError "PiClientError" // 504: ApplicationError "PiStreamTimeout" // 5xx (500-599, except 504): leave retryable // Other network errors: leave retryable ``` ## Error Buckets ### Bucket 1: 4xx (PiClientError) - Status code 400-499 - Non-retryable: bad request, auth error, not found - Temporal stops retrying immediately - Activity fails ### Bucket 2: 5xx except 504 (generic 5xx) - Status code 500-503, 505-599 - Retryable: server error, likely transient - Temporal backs off + retries until `ScheduleToCloseTimeout` (5m) ### Bucket 3: 504 (PiStreamTimeout) - Status code 504 - Means pi's SSE stream-read timed out - Retryable, BUT: Orchestrator doubles `config.Tuning.PiRetry.StreamTimeout` before retry - Next attempt uses wider timeout ## Verification ```bash cd /Users/rockliang/workplace/Poimen/workflows go test -v ./tests -run TestPiErrors # Test file: tests/pi_test.go ``` Test cases: - Mock 400 response → NonRetryableApplicationError returned - Mock 403 response → NonRetryableApplicationError returned - Mock 500 response → retryable error returned - Mock 503 response → retryable error returned - Mock 504 response → ApplicationError type "PiStreamTimeout" returned - Mock network timeout → retryable error returned ## Done Criteria - `go test ./tests -run TestPiErrors` passes all 6 test cases - All error buckets correctly classified - No panics on nil pointers - Error messages include HTTP status code