actionable-errors 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. actionable_errors-0.1.0/.envrc +65 -0
  2. actionable_errors-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  3. actionable_errors-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +30 -0
  4. actionable_errors-0.1.0/.github/pull_request_template.md +19 -0
  5. actionable_errors-0.1.0/.github/secret_scanning.yml +2 -0
  6. actionable_errors-0.1.0/.github/workflows/ci.yml +54 -0
  7. actionable_errors-0.1.0/.github/workflows/publish.yml +58 -0
  8. actionable_errors-0.1.0/.gitignore +36 -0
  9. actionable_errors-0.1.0/.pre-commit-config.yaml +13 -0
  10. actionable_errors-0.1.0/CODE_OF_CONDUCT.md +40 -0
  11. actionable_errors-0.1.0/CONTRIBUTING.md +142 -0
  12. actionable_errors-0.1.0/LICENSE +21 -0
  13. actionable_errors-0.1.0/PKG-INFO +131 -0
  14. actionable_errors-0.1.0/README.md +105 -0
  15. actionable_errors-0.1.0/SECURITY.md +28 -0
  16. actionable_errors-0.1.0/docs/ARCHITECTURE.md +93 -0
  17. actionable_errors-0.1.0/docs/PUBLISHING.md +71 -0
  18. actionable_errors-0.1.0/pyproject.toml +111 -0
  19. actionable_errors-0.1.0/src/actionable_errors/__init__.py +28 -0
  20. actionable_errors-0.1.0/src/actionable_errors/classifier.py +61 -0
  21. actionable_errors-0.1.0/src/actionable_errors/error.py +227 -0
  22. actionable_errors-0.1.0/src/actionable_errors/guidance.py +49 -0
  23. actionable_errors-0.1.0/src/actionable_errors/py.typed +0 -0
  24. actionable_errors-0.1.0/src/actionable_errors/result.py +92 -0
  25. actionable_errors-0.1.0/src/actionable_errors/sanitizer.py +149 -0
  26. actionable_errors-0.1.0/src/actionable_errors/types.py +25 -0
  27. actionable_errors-0.1.0/tests/__init__.py +1 -0
  28. actionable_errors-0.1.0/tests/test_classifier.py +275 -0
  29. actionable_errors-0.1.0/tests/test_error.py +589 -0
  30. actionable_errors-0.1.0/tests/test_guidance.py +235 -0
  31. actionable_errors-0.1.0/tests/test_result.py +435 -0
  32. actionable_errors-0.1.0/tests/test_sanitizer.py +374 -0
  33. actionable_errors-0.1.0/tests/test_types.py +291 -0
  34. actionable_errors-0.1.0/uv.lock +575 -0
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Auto-activate uv virtual environment
4
+ if ! has uv; then
5
+ echo "❌ uv is not installed. Please install it first:"
6
+ echo " curl -LsSf https://astral.sh/uv/install.sh | sh"
7
+ exit 1
8
+ fi
9
+
10
+ # Create virtual environment if it doesn't exist
11
+ if [[ ! -d ".venv" ]]; then
12
+ echo "📦 Creating virtual environment with uv..."
13
+ uv venv
14
+ fi
15
+
16
+ # Activate the virtual environment
17
+ source .venv/bin/activate
18
+ # Source .envrc.local if present (for user/machine-specific settings)
19
+ if [[ -f ".envrc.local" ]]; then
20
+ echo "🔒 Sourcing .envrc.local..."
21
+ source .envrc.local
22
+ fi
23
+
24
+ # Sync dependencies (including dev dependencies)
25
+ echo "🔄 Syncing dependencies..."
26
+ # Use --all-extras for maximum compatibility with any dependency structure
27
+ # (works with both dependency-groups and optional-dependencies)
28
+ uv sync --all-extras
29
+
30
+ # Git configuration for this project (optional)
31
+ # Uncomment and customize as needed or add to .envrc.local:
32
+ # git config user.name "Your Name"
33
+ # git config user.email "your.email@example.com"
34
+
35
+ # Install pre-commit hooks if they don't exist
36
+ if [[ -f ".pre-commit-config.yaml" ]] && ! uv run pre-commit --version >/dev/null 2>&1; then
37
+ echo "🪝 Installing pre-commit hooks..."
38
+ uv run pre-commit install
39
+ fi
40
+
41
+ # Export environment variables
42
+ export PYTHONPATH="${PWD}/src:${PYTHONPATH}"
43
+ export UV_PROJECT_ENVIRONMENT="${PWD}/.venv"
44
+
45
+ echo "✅ Environment activated!"
46
+ echo "📁 Virtual environment: ${VIRTUAL_ENV}"
47
+ echo "🐍 Python: $(python --version)"
48
+ echo "📦 uv: $(uv --version)"
49
+
50
+ # Show available commands
51
+ echo ""
52
+ echo "🚀 Available commands:"
53
+ echo " uv run pytest # Run tests"
54
+ echo " uv run pytest --cov # Run tests with coverage report"
55
+ echo " uv run pytest --cov --cov-report=html # Generate HTML coverage report"
56
+ echo " uv run ruff check # Lint code"
57
+ echo " uv run ruff format # Format code"
58
+ echo " uv run mypy src/ # Type check"
59
+ echo " uv run pre-commit run --all # Run all pre-commit hooks"
60
+ # Show available taskipy commands if task is available
61
+ if has task; then
62
+ echo ""
63
+ echo "📝 Available taskipy commands (via 'task'):"
64
+ task --list
65
+ fi
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: Bug Report
3
+ about: Something isn't working as expected
4
+ title: ""
5
+ labels: bug
6
+ assignees: ""
7
+ ---
8
+
9
+ ## Who / What / Why
10
+
11
+ - **WHO:** Who is affected? (e.g., library consumer, AI agent, operator)
12
+ - **WHAT:** What behavior is broken or unexpected?
13
+ - **WHY:** Why does it matter? What's the impact?
14
+
15
+ ## Steps to Reproduce
16
+
17
+ 1. ...
18
+ 2. ...
19
+ 3. ...
20
+
21
+ ## Expected Behavior
22
+
23
+ What you expected to happen.
24
+
25
+ ## Actual Behavior
26
+
27
+ What actually happened. Include the full error message if applicable.
28
+
29
+ ## Environment
30
+
31
+ - OS: [e.g., Ubuntu 24.04]
32
+ - Python version: [e.g., 3.12.3]
33
+ - actionable-errors version: [e.g., 0.1.0]
34
+ - Install method: [pip, uv, from source]
35
+
36
+ ## Additional Context
37
+
38
+ Any other context — stack traces, minimal reproduction script, etc.
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: Feature Request
3
+ about: Suggest a new feature or improvement
4
+ title: ""
5
+ labels: enhancement
6
+ assignees: ""
7
+ ---
8
+
9
+ ## Who / What / Why
10
+
11
+ - **WHO:** Who needs this? (e.g., library consumer, AI agent, operator)
12
+ - **WHAT:** What capability or behavior is needed?
13
+ - **WHY:** Why does it matter? What problem does it solve?
14
+
15
+ ## Proposed Solution
16
+
17
+ How do you think it should work?
18
+
19
+ ## Zero-Dependency Constraint
20
+
21
+ Can this be implemented with stdlib only? If not, explain why an exception
22
+ is warranted and what the dependency cost would be to consumers.
23
+
24
+ ## Alternatives Considered
25
+
26
+ Any other approaches you thought about and why they're less ideal.
27
+
28
+ ## Additional Context
29
+
30
+ Anything else — code samples, related issues, etc.
@@ -0,0 +1,19 @@
1
+ # Pull Request
2
+
3
+ ## Summary
4
+ <!-- One paragraph: what this PR does and why -->
5
+
6
+ ## Changes
7
+ <!-- Bullet list of changes -->
8
+
9
+ ## Testing
10
+ <!-- How were these changes verified? -->
11
+ - [ ] All existing tests pass (`task check`)
12
+ - [ ] New tests added for new behavior
13
+ - [ ] Coverage target maintained (100%)
14
+
15
+ ## Checklist
16
+ - [ ] `from __future__ import annotations` at top of every new module
17
+ - [ ] mypy strict passes
18
+ - [ ] ruff lint + format passes
19
+ - [ ] No runtime dependencies added (stdlib only)
@@ -0,0 +1,2 @@
1
+ paths-ignore:
2
+ - "tests/**"
@@ -0,0 +1,54 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ test:
14
+ runs-on: ubuntu-latest
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v4
21
+
22
+ - name: Set up Python 3.12
23
+ run: uv python install 3.12
24
+
25
+ - name: Install dependencies
26
+ run: uv sync --extra dev
27
+
28
+ - name: Lint with ruff
29
+ run: uv run ruff check src/ tests/
30
+
31
+ - name: Type check with mypy
32
+ run: uv run mypy src/
33
+
34
+ - name: Run tests with coverage
35
+ run: uv run pytest --cov=actionable_errors --cov-report=term-missing --cov-report=json
36
+
37
+ - name: Extract coverage percentage
38
+ if: github.ref == 'refs/heads/main'
39
+ run: |
40
+ COVERAGE=$(python -c "import json; print(int(json.load(open('coverage.json'))['totals']['percent_covered']))")
41
+ echo "COVERAGE_PCT=$COVERAGE" >> $GITHUB_ENV
42
+
43
+ - name: Update coverage badge
44
+ if: github.ref == 'refs/heads/main'
45
+ uses: schneegans/dynamic-badges-action@v1.7.0
46
+ with:
47
+ auth: ${{ secrets.GIST_SECRET }}
48
+ gistID: ${{ vars.COVERAGE_GIST_ID }}
49
+ filename: actionable-errors-coverage.json
50
+ label: coverage
51
+ message: ${{ env.COVERAGE_PCT }}%
52
+ valColorRange: ${{ env.COVERAGE_PCT }}
53
+ minColorRange: 50
54
+ maxColorRange: 100
@@ -0,0 +1,58 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+ id-token: write # Required for trusted publishing (OIDC)
10
+
11
+ jobs:
12
+ build:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - name: Install uv
18
+ uses: astral-sh/setup-uv@v4
19
+
20
+ - name: Set up Python 3.12
21
+ run: uv python install 3.12
22
+
23
+ - name: Install dependencies
24
+ run: uv sync --extra dev
25
+
26
+ - name: Lint
27
+ run: uv run ruff check src/ tests/
28
+
29
+ - name: Type check
30
+ run: uv run mypy src/
31
+
32
+ - name: Test
33
+ run: uv run pytest --cov=actionable_errors --cov-report=term-missing
34
+
35
+ - name: Build
36
+ run: uv build
37
+
38
+ - name: Upload dist artifacts
39
+ uses: actions/upload-artifact@v4
40
+ with:
41
+ name: dist
42
+ path: dist/
43
+
44
+ publish:
45
+ needs: build
46
+ runs-on: ubuntu-latest
47
+ environment: pypi # GitHub Environment for deployment protection
48
+ permissions:
49
+ id-token: write # OIDC token for PyPI trusted publishing
50
+ steps:
51
+ - name: Download dist artifacts
52
+ uses: actions/download-artifact@v4
53
+ with:
54
+ name: dist
55
+ path: dist/
56
+
57
+ - name: Publish to PyPI
58
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,36 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .eggs/
8
+
9
+ # Virtual environments
10
+ .venv/
11
+ venv/
12
+
13
+ # IDE
14
+ .vscode/
15
+ .idea/
16
+
17
+ # direnv
18
+ .direnv/
19
+ .envrc.local
20
+
21
+ # OS
22
+ .DS_Store
23
+ Thumbs.db
24
+
25
+ # Coverage
26
+ .coverage
27
+ coverage.json
28
+ htmlcov/
29
+
30
+ # mypy / ruff / pytest caches
31
+ .mypy_cache/
32
+ .ruff_cache/
33
+ .pytest_cache/
34
+
35
+ # Direnv local environment variables
36
+ .envrc.local
@@ -0,0 +1,13 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.8.6
4
+ hooks:
5
+ - id: ruff
6
+ args: [--fix]
7
+ - id: ruff-format
8
+ - repo: https://github.com/pre-commit/mirrors-mypy
9
+ rev: v1.13.0
10
+ hooks:
11
+ - id: mypy
12
+ additional_dependencies: []
13
+ args: [--strict]
@@ -0,0 +1,40 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to a positive environment:
15
+
16
+ - Using welcoming and inclusive language
17
+ - Being respectful of differing viewpoints and experiences
18
+ - Gracefully accepting constructive criticism
19
+ - Focusing on what is best for the community
20
+ - Showing empathy towards other community members
21
+
22
+ Examples of unacceptable behavior:
23
+
24
+ - The use of sexualized language or imagery and unwelcome sexual attention
25
+ - Trolling, insulting/derogatory comments, and personal or political attacks
26
+ - Public or private harassment
27
+ - Publishing others' private information without explicit permission
28
+ - Other conduct which could reasonably be considered inappropriate
29
+
30
+ ## Enforcement
31
+
32
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
33
+ reported to the project maintainer. All complaints will be reviewed and
34
+ investigated and will result in a response that is deemed necessary and
35
+ appropriate to the circumstances.
36
+
37
+ ## Attribution
38
+
39
+ This Code of Conduct is adapted from the
40
+ [Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
@@ -0,0 +1,142 @@
1
+ # Contributing
2
+
3
+ Thanks for your interest in contributing to actionable-errors. This document
4
+ covers the development setup, coding standards, testing philosophy, and
5
+ PR process.
6
+
7
+ ---
8
+
9
+ ## Development Setup
10
+
11
+ ```bash
12
+ # Clone
13
+ git clone https://github.com/grimlor/actionable-errors.git
14
+ cd actionable-errors
15
+
16
+ # Install with dev dependencies (creates .venv automatically)
17
+ uv sync --extra dev
18
+
19
+ # Optional: auto-activate venv
20
+ direnv allow
21
+ ```
22
+
23
+ ## Running Checks
24
+
25
+ All checks must pass before submitting a PR:
26
+
27
+ ```bash
28
+ task check # runs lint → type → test
29
+ ```
30
+
31
+ Or individually:
32
+
33
+ ```bash
34
+ task lint # ruff check src/ tests/
35
+ task format # ruff format src/ tests/
36
+ task type # mypy strict mode
37
+ task test # pytest -v
38
+ ```
39
+
40
+ ## Code Style
41
+
42
+ - **Python 3.12+** — use modern syntax (`X | Y` unions, `@dataclass`).
43
+ - **`from __future__ import annotations`** at the top of every module.
44
+ - **ruff** handles formatting and import sorting. Don't fight it.
45
+ - **mypy strict** — all functions need type annotations. No `Any` unless
46
+ you have a good reason and document it.
47
+ - **Line length:** 99 characters (configured in `pyproject.toml`).
48
+ - **Quote style:** double quotes.
49
+
50
+ ## Zero-Dependency Constraint
51
+
52
+ This package has **zero runtime dependencies** — stdlib only. This is a
53
+ hard architectural constraint, not a preference. `actionable-errors` sits
54
+ at the bottom of every dependency tree. Adding a runtime dependency would
55
+ transitively infect every consumer.
56
+
57
+ Dev dependencies (ruff, mypy, pytest, etc.) are fine — they don't ship.
58
+
59
+ ## Testing Standards
60
+
61
+ Tests are the living specification. Every test class documents a behavioral
62
+ requirement, not a code structure.
63
+
64
+ ### Test Class Structure
65
+
66
+ ```python
67
+ class TestYourFeature:
68
+ """
69
+ REQUIREMENT: One-sentence summary of the behavioral contract.
70
+
71
+ WHO: Who depends on this behavior (calling code, operator, AI agent)
72
+ WHAT: What the behavior is, including failure modes
73
+ WHY: What breaks if this contract is violated
74
+
75
+ MOCK BOUNDARY:
76
+ Mock: nothing — this package is pure computation
77
+ Real: all classes and functions under test
78
+ Never: construct expected output and assert on the construction
79
+ """
80
+
81
+ def test_descriptive_name_of_scenario(self) -> None:
82
+ """
83
+ Given some precondition
84
+ When an action is taken
85
+ Then an observable outcome occurs
86
+ """
87
+ ...
88
+ ```
89
+
90
+ ### Key Principles
91
+
92
+ 1. **Mock I/O boundaries, not implementation.** Since this package has no
93
+ I/O (stdlib only, no network, no filesystem), most tests will have
94
+ `Mock: nothing` in their mock boundary contract.
95
+
96
+ 2. **Failure specs matter.** For every happy path, ask: what goes wrong?
97
+ Write specs for those failure modes. An unspecified failure is an
98
+ unhandled failure.
99
+
100
+ 3. **Missing spec = missing requirement.** If you find a bug, the first
101
+ step is always adding the test that should have caught it, then fixing
102
+ the code to pass that test.
103
+
104
+ 4. **Every assertion includes a diagnostic message.** Bare assertions are
105
+ not permitted.
106
+
107
+ ## Architecture
108
+
109
+ See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the three-audience
110
+ error design philosophy and module responsibilities.
111
+
112
+ ## Commit Messages
113
+
114
+ Use clear, imperative commit messages:
115
+
116
+ ```
117
+ Add credential sanitizer with configurable patterns
118
+
119
+ - Eight built-in regex patterns for common credential formats
120
+ - Consumer-extensible pattern registration
121
+ - 15 tests covering all built-in patterns and custom registration
122
+ ```
123
+
124
+ ## Pull Requests
125
+
126
+ 1. **Branch from `main`.**
127
+ 2. **All checks must pass** — `task check` (lint + type + test).
128
+ 3. **Include tests** for any new behavior or bug fix.
129
+ 4. **One concern per PR** — don't mix a new feature with unrelated refactoring.
130
+ 5. **No runtime dependencies** — this is a hard constraint.
131
+ 6. **Describe what and why** in the PR description.
132
+
133
+ ## Reporting Issues
134
+
135
+ When filing an issue:
136
+
137
+ - **Bug:** Include the error message, what you expected, and steps to
138
+ reproduce. Include the Python version and how actionable-errors was
139
+ installed.
140
+ - **Feature request:** Describe the problem you're trying to solve, not
141
+ just the solution you have in mind. Note whether it can be done with
142
+ stdlib only.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 grimlor
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,131 @@
1
+ Metadata-Version: 2.4
2
+ Name: actionable-errors
3
+ Version: 0.1.0
4
+ Summary: Three-audience error framework: typed errors for code, actionable suggestions for humans, tool guidance for AI agents
5
+ Project-URL: Repository, https://github.com/grimlor/actionable-errors
6
+ Author: grimlor
7
+ License: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Typing :: Typed
16
+ Requires-Python: >=3.12
17
+ Provides-Extra: dev
18
+ Requires-Dist: mypy<2,>=1.13; extra == 'dev'
19
+ Requires-Dist: pre-commit<5,>=4; extra == 'dev'
20
+ Requires-Dist: pytest-asyncio<1,>=0.24; extra == 'dev'
21
+ Requires-Dist: pytest-cov<7,>=6; extra == 'dev'
22
+ Requires-Dist: pytest<9,>=8; extra == 'dev'
23
+ Requires-Dist: ruff<1,>=0.8; extra == 'dev'
24
+ Requires-Dist: taskipy<2,>=1.14; extra == 'dev'
25
+ Description-Content-Type: text/markdown
26
+
27
+ # actionable-errors
28
+
29
+ Three-audience error framework for Python.
30
+
31
+ Every `ActionableError` speaks to three audiences simultaneously:
32
+
33
+ | Audience | Uses | Provided by |
34
+ |----------|------|-------------|
35
+ | **Calling code** | Typed `ErrorType` for routing (retry? escalate? ignore?) | `ErrorType(StrEnum)` — 8 base categories |
36
+ | **Human operator** | `suggestion` + `Troubleshooting` steps | Frozen dataclasses with actionable text |
37
+ | **AI agent** | `AIGuidance` with concrete next tool calls | Frozen dataclass with tool suggestions |
38
+
39
+ ## Install
40
+
41
+ ```bash
42
+ pip install actionable-errors
43
+ ```
44
+
45
+ **Zero runtime dependencies** — stdlib only. Sits at the bottom of every dependency tree.
46
+
47
+ ## Quick Start
48
+
49
+ ```python
50
+ from actionable_errors import (
51
+ ActionableError,
52
+ AIGuidance,
53
+ ToolResult,
54
+ from_exception,
55
+ sanitize,
56
+ )
57
+
58
+ # Domain-specific factory with built-in defaults
59
+ error = ActionableError.authentication(
60
+ service="Azure DevOps",
61
+ raw_error="401 Unauthorized",
62
+ )
63
+ print(error.suggestion) # "Check your credentials and try again."
64
+ print(error.ai_guidance) # AIGuidance(action_required="Re-authenticate", command="az login")
65
+
66
+ # Auto-classify any exception
67
+ try:
68
+ raise ConnectionError("Connection refused")
69
+ except Exception as exc:
70
+ ae = from_exception(exc, service="Kusto", operation="query")
71
+ print(ae.error_type) # "connection"
72
+
73
+ # Typed result envelope for MCP tool responses
74
+ result = ToolResult.ok(data={"items": 42})
75
+ result = ToolResult.fail(error=error) # extracts error_type + suggestion
76
+
77
+ # Credential sanitization
78
+ clean = sanitize('password="hunter2" token=abc123')
79
+ # → 'password="***" token=***'
80
+ ```
81
+
82
+ ## Extending ErrorType
83
+
84
+ Python `StrEnum` can't be subclassed once it has members, so extend via composition:
85
+
86
+ ```python
87
+ from enum import StrEnum
88
+ from actionable_errors import ActionableError
89
+
90
+ class RAGErrorType(StrEnum):
91
+ EMBEDDING = "embedding"
92
+ INDEX = "index"
93
+
94
+ error = ActionableError(
95
+ error="Vector store unavailable",
96
+ error_type=RAGErrorType.INDEX,
97
+ service="pinecone",
98
+ suggestion="Check Pinecone cluster status.",
99
+ )
100
+ ```
101
+
102
+ ## Factories
103
+
104
+ Eight domain-specific factories with sensible defaults:
105
+
106
+ | Factory | Key Parameters |
107
+ |---------|---------------|
108
+ | `.authentication(service, raw_error)` | Default suggestion + AI guidance |
109
+ | `.configuration(field_name, reason)` | — |
110
+ | `.connection(service, url, raw_error)` | — |
111
+ | `.timeout(service, operation, timeout_seconds)` | — |
112
+ | `.permission(service, resource, raw_error)` | — |
113
+ | `.validation(service, field_name, reason)` | — |
114
+ | `.not_found(service, resource_type, resource_id, raw_error)` | — |
115
+ | `.internal(service, operation, raw_error)` | — |
116
+
117
+ All factories accept optional `suggestion`, `ai_guidance`, and `troubleshooting` kwargs.
118
+
119
+ ## Development
120
+
121
+ ```bash
122
+ # Install dev dependencies
123
+ uv sync --extra dev
124
+
125
+ # Run all checks
126
+ task check # lint → type → test (90 tests, 100% coverage)
127
+ ```
128
+
129
+ ## License
130
+
131
+ MIT