Correct 4 findings from the independent M11 review:
#1 (Critical) — PipelineCriticalError was downgraded to PipelineWarning:
- Add except PipelineCriticalError: raise before each except Exception
in all 5 non-blocking steps (fetch_blog, compare, caldav_sync, synthesis, send)
- Critical errors now propagate to the outer handler and stop the pipeline
#2 (Critical) — redact_exception() did not use configured secrets:
- Extend redact_exception() with extra_secrets parameter (upward compatible)
- Harden redact_secrets(): sort extra_secrets by length descending
- Add Settings.redaction_secrets() collecting all 6 SecretStr fields
- Add PipelineRunner._redact(exc) using self._redaction_secrets
- All except blocks in run() now use self._redact(exc)
- CalDAV FAILED-status path uses full redaction_secrets collection
#3 (Medium) — BlogRSSClient silently swallowed failures:
- Add error field to BlogRSSFetchResult
- rss.py sets error on failure paths (except Exception, bozo/invalid feed)
- fetch_blog_step raises RuntimeError when result.error is set
- PipelineRunner now produces PipelineWarning for blog failures
#4 (Medium) — Test coverage at 80%, now 91%:
- 11 new integration tests covering blog failure/success, compare failure,
CalDAV failure (exception + FAILED status), send False/exception,
PipelineCriticalError propagation, secret redaction with sentinel,
empty agenda/homework, iCal cache cleanup
- Secret redaction test uses mock (no network) and proves configured-secret
propagation via non-URL sentinel in RuntimeError
Validation: 619 tests pass, ruff/mypy/bandit/pre-commit green, coverage 91%.
Co-authored-by: opencode/coder <coder@agents.invalid>
Co-authored-by: opencode/test-engineer <test-engineer@agents.invalid>