opensaddle 1.0.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 (111) hide show
  1. opensaddle-1.0.0/.gitignore +9 -0
  2. opensaddle-1.0.0/CHANGELOG.md +32 -0
  3. opensaddle-1.0.0/CONTRIBUTING.md +56 -0
  4. opensaddle-1.0.0/LICENSE +21 -0
  5. opensaddle-1.0.0/PKG-INFO +318 -0
  6. opensaddle-1.0.0/README.md +284 -0
  7. opensaddle-1.0.0/SECURITY.md +17 -0
  8. opensaddle-1.0.0/examples/benchmark-opensaddle.yaml +44 -0
  9. opensaddle-1.0.0/examples/benchmark-suite.yaml +29 -0
  10. opensaddle-1.0.0/examples/opensaddle.yaml +156 -0
  11. opensaddle-1.0.0/pyproject.toml +75 -0
  12. opensaddle-1.0.0/specs/00-product-brief.md +104 -0
  13. opensaddle-1.0.0/specs/01-architecture.md +253 -0
  14. opensaddle-1.0.0/specs/02-configuration.md +281 -0
  15. opensaddle-1.0.0/specs/03-execution-and-audit.md +221 -0
  16. opensaddle-1.0.0/specs/04-benchmarking.md +136 -0
  17. opensaddle-1.0.0/specs/05-dashboard.md +136 -0
  18. opensaddle-1.0.0/specs/06-implementation-slices.md +201 -0
  19. opensaddle-1.0.0/specs/07-v1-release-gap-plan.md +321 -0
  20. opensaddle-1.0.0/specs/08-v1-public-contract.md +214 -0
  21. opensaddle-1.0.0/specs/09-v1.5-brokered-tools.md +7 -0
  22. opensaddle-1.0.0/specs/10-v2-enterprise-pilot-adr.md +153 -0
  23. opensaddle-1.0.0/specs/99-open-questions.md +86 -0
  24. opensaddle-1.0.0/specs/ARCHITECTURE_COMPLETED.md +502 -0
  25. opensaddle-1.0.0/src/opensaddle/__init__.py +5 -0
  26. opensaddle-1.0.0/src/opensaddle/__main__.py +7 -0
  27. opensaddle-1.0.0/src/opensaddle/adapters/__init__.py +1 -0
  28. opensaddle-1.0.0/src/opensaddle/adapters/base.py +85 -0
  29. opensaddle-1.0.0/src/opensaddle/adapters/cli.py +192 -0
  30. opensaddle-1.0.0/src/opensaddle/agent_presets.py +194 -0
  31. opensaddle-1.0.0/src/opensaddle/audit/__init__.py +1 -0
  32. opensaddle-1.0.0/src/opensaddle/audit/auditor.py +496 -0
  33. opensaddle-1.0.0/src/opensaddle/audit/stream.py +43 -0
  34. opensaddle-1.0.0/src/opensaddle/audit/types.py +68 -0
  35. opensaddle-1.0.0/src/opensaddle/benchmark/__init__.py +27 -0
  36. opensaddle-1.0.0/src/opensaddle/benchmark/metrics.py +92 -0
  37. opensaddle-1.0.0/src/opensaddle/benchmark/reports.py +56 -0
  38. opensaddle-1.0.0/src/opensaddle/benchmark/runner.py +683 -0
  39. opensaddle-1.0.0/src/opensaddle/benchmark/suite.py +217 -0
  40. opensaddle-1.0.0/src/opensaddle/cli/__init__.py +2 -0
  41. opensaddle-1.0.0/src/opensaddle/cli/main.py +1119 -0
  42. opensaddle-1.0.0/src/opensaddle/config.py +653 -0
  43. opensaddle-1.0.0/src/opensaddle/core/__init__.py +44 -0
  44. opensaddle-1.0.0/src/opensaddle/core/agent.py +16 -0
  45. opensaddle-1.0.0/src/opensaddle/core/episode.py +116 -0
  46. opensaddle-1.0.0/src/opensaddle/core/failure_attribution.py +211 -0
  47. opensaddle-1.0.0/src/opensaddle/core/model.py +28 -0
  48. opensaddle-1.0.0/src/opensaddle/core/route.py +126 -0
  49. opensaddle-1.0.0/src/opensaddle/core/stored_episode.py +197 -0
  50. opensaddle-1.0.0/src/opensaddle/core/task.py +49 -0
  51. opensaddle-1.0.0/src/opensaddle/dashboard.py +1797 -0
  52. opensaddle-1.0.0/src/opensaddle/dashboard_reviews.py +682 -0
  53. opensaddle-1.0.0/src/opensaddle/execution/__init__.py +1 -0
  54. opensaddle-1.0.0/src/opensaddle/execution/broker.py +242 -0
  55. opensaddle-1.0.0/src/opensaddle/execution/configured_routes.py +319 -0
  56. opensaddle-1.0.0/src/opensaddle/execution/policy_observer.py +71 -0
  57. opensaddle-1.0.0/src/opensaddle/execution/pty.py +140 -0
  58. opensaddle-1.0.0/src/opensaddle/execution/runner.py +333 -0
  59. opensaddle-1.0.0/src/opensaddle/execution/stored_episodes.py +166 -0
  60. opensaddle-1.0.0/src/opensaddle/execution/worktree.py +80 -0
  61. opensaddle-1.0.0/src/opensaddle/governance.py +67 -0
  62. opensaddle-1.0.0/src/opensaddle/history.py +737 -0
  63. opensaddle-1.0.0/src/opensaddle/model_registry.py +168 -0
  64. opensaddle-1.0.0/src/opensaddle/openrouter.py +425 -0
  65. opensaddle-1.0.0/src/opensaddle/optimize/__init__.py +6 -0
  66. opensaddle-1.0.0/src/opensaddle/optimize/estimator.py +128 -0
  67. opensaddle-1.0.0/src/opensaddle/optimize/route_materialization.py +328 -0
  68. opensaddle-1.0.0/src/opensaddle/optimize/route_scoring.py +286 -0
  69. opensaddle-1.0.0/src/opensaddle/optimize/task_profile.py +65 -0
  70. opensaddle-1.0.0/src/opensaddle/privacy.py +125 -0
  71. opensaddle-1.0.0/src/opensaddle/storage/__init__.py +3 -0
  72. opensaddle-1.0.0/src/opensaddle/storage/migrations.py +137 -0
  73. opensaddle-1.0.0/src/opensaddle/storage/sqlite.py +923 -0
  74. opensaddle-1.0.0/src/opensaddle/templates.py +170 -0
  75. opensaddle-1.0.0/src/opensaddle/verification/__init__.py +1 -0
  76. opensaddle-1.0.0/src/opensaddle/verification/checks.py +115 -0
  77. opensaddle-1.0.0/src/opensaddle/verification/diff_risk.py +50 -0
  78. opensaddle-1.0.0/tests/conftest.py +12 -0
  79. opensaddle-1.0.0/tests/fixtures/benchmark_suite.yaml +33 -0
  80. opensaddle-1.0.0/tests/fixtures/history_transcripts/claude_root/.claude/projects/demo/session.jsonl +3 -0
  81. opensaddle-1.0.0/tests/fixtures/history_transcripts/codex_root/.codex/history.jsonl +4 -0
  82. opensaddle-1.0.0/tests/fixtures/history_transcripts/copilot_root/.github/copilot/logs/secrets/session.jsonl +4 -0
  83. opensaddle-1.0.0/tests/fixtures/mock_cli.py +43 -0
  84. opensaddle-1.0.0/tests/fixtures/mock_tui_cli.py +35 -0
  85. opensaddle-1.0.0/tests/fixtures/repo_seed/README.md +1 -0
  86. opensaddle-1.0.0/tests/fixtures/repo_seed/app.txt +1 -0
  87. opensaddle-1.0.0/tests/fixtures/verify_fail.py +3 -0
  88. opensaddle-1.0.0/tests/fixtures/verify_pass.py +3 -0
  89. opensaddle-1.0.0/tests/test_agent_presets.py +67 -0
  90. opensaddle-1.0.0/tests/test_auditor.py +187 -0
  91. opensaddle-1.0.0/tests/test_benchmark_runner.py +382 -0
  92. opensaddle-1.0.0/tests/test_broker.py +95 -0
  93. opensaddle-1.0.0/tests/test_cli.py +310 -0
  94. opensaddle-1.0.0/tests/test_cli_adapter.py +89 -0
  95. opensaddle-1.0.0/tests/test_cli_main.py +981 -0
  96. opensaddle-1.0.0/tests/test_cli_smoke.py +309 -0
  97. opensaddle-1.0.0/tests/test_config.py +511 -0
  98. opensaddle-1.0.0/tests/test_core_schemas.py +173 -0
  99. opensaddle-1.0.0/tests/test_council_routes.py +213 -0
  100. opensaddle-1.0.0/tests/test_dashboard.py +958 -0
  101. opensaddle-1.0.0/tests/test_episode_store.py +433 -0
  102. opensaddle-1.0.0/tests/test_estimator_domains.py +87 -0
  103. opensaddle-1.0.0/tests/test_failure_attribution.py +118 -0
  104. opensaddle-1.0.0/tests/test_governance.py +25 -0
  105. opensaddle-1.0.0/tests/test_history.py +98 -0
  106. opensaddle-1.0.0/tests/test_openrouter.py +114 -0
  107. opensaddle-1.0.0/tests/test_policy_observer.py +70 -0
  108. opensaddle-1.0.0/tests/test_route_fallbacks.py +80 -0
  109. opensaddle-1.0.0/tests/test_route_stats.py +277 -0
  110. opensaddle-1.0.0/tests/test_runner.py +254 -0
  111. opensaddle-1.0.0/tests/test_verification.py +34 -0
@@ -0,0 +1,9 @@
1
+ .opensaddle/
2
+ .pytest_cache/
3
+ __pycache__/
4
+ *.pyc
5
+ .venv/
6
+ .DS_Store
7
+ dist/
8
+ build/
9
+ *.egg-info/
@@ -0,0 +1,32 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on Keep a Changelog, and the project follows Semantic Versioning.
6
+
7
+ ## [1.0.0] - 2026-07-06
8
+
9
+ ### Added
10
+
11
+ - Stable V1 public contract covering the `opensaddle` CLI, YAML configuration, route forms, SQLite episode storage, benchmark suites, dashboard, and history importers.
12
+ - Configured route execution, route fallback handling, policy observation/enforcement, contextual routing signals, and council route primitives.
13
+ - Failure attribution, benchmark aggregate metrics, route outcome statistics, dashboard route metrics, and provider/model health signals.
14
+ - Release checklist, release notes, security policy, contributor guide, GitHub CI, and release build workflow.
15
+
16
+ ### Changed
17
+
18
+ - Promoted package metadata and user-visible version strings to `1.0.0`.
19
+ - Expanded README guidance for install, smoke workflows, V1 scope, public contract, and known non-goals.
20
+
21
+ ### Security
22
+
23
+ - Documented the V1 local-first security boundary, including the absence of container sandboxing, network isolation, and secret isolation.
24
+
25
+ ## [0.1.0] - 2026-07-05
26
+
27
+ ### Added
28
+
29
+ - Initial `opensaddle` CLI and Python package release.
30
+ - Local-first route execution, verification, auditing, and history import helpers.
31
+ - SQLite-backed episode storage, benchmark tooling, and local dashboard support.
32
+ - Packaging metadata, contributor documentation, security policy, and GitHub Actions workflows for CI and release preparation.
@@ -0,0 +1,56 @@
1
+ # Contributing
2
+
3
+ Thanks for helping improve OpenSaddle.
4
+
5
+ ## Local setup
6
+
7
+ For day-to-day development:
8
+
9
+ ```bash
10
+ uv sync --dev
11
+ uv run opensaddle --help
12
+ ```
13
+
14
+ If you prefer an editable install:
15
+
16
+ ```bash
17
+ uv pip install -e ".[dev]"
18
+ ```
19
+
20
+ ## Validation
21
+
22
+ Run the core validation steps before opening a pull request:
23
+
24
+ ```bash
25
+ uv run pytest
26
+ uv build
27
+ ```
28
+
29
+ When packaging changes are involved, inspect the built wheel metadata as well:
30
+
31
+ ```bash
32
+ python -m zipfile -l dist/*.whl
33
+ python - <<'PY'
34
+ from email import message_from_string
35
+ from pathlib import Path
36
+ import zipfile
37
+
38
+ wheel = sorted(Path("dist").glob("*.whl"))[-1]
39
+ with zipfile.ZipFile(wheel) as zf:
40
+ metadata_name = next(name for name in zf.namelist() if name.endswith("METADATA"))
41
+ metadata = message_from_string(zf.read(metadata_name).decode("utf-8"))
42
+ print(metadata["Name"])
43
+ print(metadata["Version"])
44
+ PY
45
+ ```
46
+
47
+ ## Pull requests
48
+
49
+ - Keep changes focused and explain the user-facing or maintainer-facing impact.
50
+ - Add or update tests when behavior changes.
51
+ - Update `README.md`, `CHANGELOG.md`, or specs when the documented behavior changes.
52
+
53
+ ## Releases
54
+
55
+ GitHub Actions builds artifacts in CI and includes a guarded release workflow prepared for PyPI Trusted Publishing.
56
+ Before enabling publish steps for a release, configure PyPI Trusted Publishing for this repository and verify the package metadata from the generated artifacts.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OpenSaddle contributors
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,318 @@
1
+ Metadata-Version: 2.4
2
+ Name: opensaddle
3
+ Version: 1.0.0
4
+ Summary: Local-first control plane for routing work across coding agents.
5
+ Project-URL: Homepage, https://github.com/AkeBoss-tech/opensaddle
6
+ Project-URL: Repository, https://github.com/AkeBoss-tech/opensaddle
7
+ Project-URL: Issues, https://github.com/AkeBoss-tech/opensaddle/issues
8
+ Project-URL: Changelog, https://github.com/AkeBoss-tech/opensaddle/blob/main/CHANGELOG.md
9
+ Author: OpenSaddle contributors
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: agents,automation,cli,llm,routing
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Software Development :: Build Tools
23
+ Classifier: Topic :: Software Development :: Testing
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.11
26
+ Requires-Dist: fastapi<1.0,>=0.110
27
+ Requires-Dist: pydantic<3.0,>=2.8
28
+ Requires-Dist: pyyaml<7.0,>=6.0
29
+ Requires-Dist: typer<1.0,>=0.16
30
+ Requires-Dist: uvicorn<1.0,>=0.27
31
+ Provides-Extra: dev
32
+ Requires-Dist: pytest<10.0,>=8.3; extra == 'dev'
33
+ Description-Content-Type: text/markdown
34
+
35
+ # opensaddle
36
+
37
+ `opensaddle` is a local-first control plane for routing work across coding agents, models, councils, auditors, and verification loops.
38
+
39
+ It ships with a Typer CLI, a SQLite episode store, benchmark tooling, history import helpers, and a local dashboard.
40
+
41
+ V1.1 supports optional declarative governance profiles for route planning and decision receipts. They do not provide a tool broker, network controls, credential isolation, IAM, OAuth, or provider enforcement.
42
+
43
+ Permission policies default to `mode: observe`, which logs violations without stopping the run. You can also opt into `mode: enforce` for local policy enforcement that fails a run after diff capture when observed command or path rules are violated. This is not OS sandboxing or container isolation: the agent subprocess still runs locally, and enforcement happens from captured local evidence after execution.
44
+
45
+ ## V1 Boundary And Safety Posture
46
+
47
+ V1 is a single-user, local developer tool. It runs user-configured subprocesses against local repositories and stores run data in local SQLite/JSON files. Only run commands and benchmark suites you trust.
48
+
49
+ It does **not** provide sandboxing, credential or secret isolation, network isolation, enterprise IAM, a hosted or multi-user service, or a corporate tool broker. Local `mode: enforce` is post-run evidence-based policy handling, not a preventative security control.
50
+
51
+ ## Install
52
+
53
+ ### User install
54
+
55
+ When the package is published, install the CLI for normal use with one of:
56
+
57
+ ```bash
58
+ uv tool install opensaddle
59
+ # or
60
+ pip install opensaddle
61
+ ```
62
+
63
+ ### Local development install
64
+
65
+ For local development in this repository:
66
+
67
+ ```bash
68
+ uv sync --dev
69
+ ```
70
+
71
+ If you want an editable environment instead of a synced project environment:
72
+
73
+ ```bash
74
+ uv pip install -e ".[dev]"
75
+ ```
76
+
77
+ ## Dev And Test
78
+
79
+ ```bash
80
+ uv run opensaddle --help
81
+ uv run pytest
82
+ uv build
83
+ ```
84
+
85
+ ## Manual Smoke Checklist
86
+
87
+ These commands mirror the automated CLI smoke suite. The direct run uses inline Python, and the configured-route run uses a temporary local-only config so the checklist never depends on paid agent CLIs being installed.
88
+
89
+ ```bash
90
+ uv run opensaddle init --root .
91
+ uv run opensaddle config validate --config ./opensaddle.yaml
92
+ uv run opensaddle estimate --repo . --config ./opensaddle.yaml --task "Fix a small UI bug in the settings panel"
93
+ cat > ./opensaddle.smoke.yaml <<'YAML'
94
+ project:
95
+ name: smoke
96
+ default_mode: balanced
97
+ storage:
98
+ backend: sqlite
99
+ path: .opensaddle/smoke.db
100
+ providers:
101
+ openrouter:
102
+ enabled: false
103
+ agents:
104
+ smoke_agent:
105
+ adapter: cli
106
+ command: python
107
+ modes:
108
+ exec:
109
+ argv:
110
+ - python
111
+ - -c
112
+ - "from pathlib import Path; Path('SMOKE.txt').write_text('configured\\n')"
113
+ models:
114
+ - id: smoke-model
115
+ success_prior: strong
116
+ success_priors:
117
+ strong:
118
+ p_success: 0.95
119
+ expected_minutes: 3
120
+ uncertainty: 0.1
121
+ permissions:
122
+ observe_all:
123
+ mode: observe
124
+ allowed_paths: ["**"]
125
+ verification:
126
+ pass:
127
+ checks:
128
+ - name: smoke
129
+ command: "python -c \"raise SystemExit(0)\""
130
+ routes:
131
+ smoke_route:
132
+ kind: single_agent
133
+ agent: smoke_agent
134
+ model: smoke-model
135
+ permissions: observe_all
136
+ verification: pass
137
+ artifact_target: patch
138
+ YAML
139
+ uv run opensaddle run --repo . --instruction "Update README.md with a smoke note" --agent-arg python --agent-arg -c --agent-arg "from pathlib import Path; Path('SMOKE.txt').write_text('ok\\n')" --verify "smoke=python -c \"raise SystemExit(0)\""
140
+ uv run opensaddle config validate --config ./opensaddle.smoke.yaml
141
+ uv run opensaddle run --repo . --config ./opensaddle.smoke.yaml --instruction "Run the configured smoke route"
142
+ uv run opensaddle episodes list --db ./.opensaddle/smoke.db --json
143
+ uv run opensaddle routes stats --db ./.opensaddle/smoke.db --json
144
+ uv run opensaddle bench run ./examples/benchmark-suite.yaml --db ./.opensaddle/smoke.db --json
145
+ uv run opensaddle dashboard --root . --config ./opensaddle.smoke.yaml
146
+ ```
147
+
148
+ ## Quickstart
149
+
150
+ The repository includes ready-to-use examples in [`examples/`](examples/):
151
+
152
+ - [`examples/opensaddle.yaml`](examples/opensaddle.yaml)
153
+ - [`examples/benchmark-suite.yaml`](examples/benchmark-suite.yaml)
154
+
155
+ 1. Initialize a new project layout:
156
+
157
+ ```bash
158
+ uv run opensaddle init --root .
159
+ ```
160
+
161
+ The generated `opensaddle.yaml` now includes built-in CLI presets for `codex`, `claude_code`, `github_copilot`, `aider`, `cursor`, `amp`, `antigravity`, `claw`, `hermes`, and `pi`, while still using the same generic `adapter: cli` execution path.
162
+
163
+ Config compatibility in v1 stays intentionally lightweight: additive fields may land without migrations, but breaking config-shape changes will require an explicit new config version plus documented migration notes. `opensaddle` does not silently rewrite user configs.
164
+
165
+ 2. Estimate a task against the sample config:
166
+
167
+ ```bash
168
+ uv run opensaddle estimate \
169
+ --repo . \
170
+ --config examples/opensaddle.yaml \
171
+ --task "Improve the README command examples"
172
+ ```
173
+
174
+ 3. Run an agent command inside a worktree:
175
+
176
+ ```bash
177
+ uv run opensaddle run \
178
+ --repo . \
179
+ --instruction "Add a short status note to README.md" \
180
+ --agent-arg codex \
181
+ --agent-arg exec \
182
+ --agent-arg=--model \
183
+ --agent-arg gpt-5 \
184
+ --agent-arg "{instruction}" \
185
+ --verify "test=pytest"
186
+ ```
187
+
188
+ For interactive or TUI-first CLIs, you can switch the direct runner onto a pseudo-terminal:
189
+
190
+ ```bash
191
+ uv run opensaddle run \
192
+ --repo . \
193
+ --instruction "Inspect the repo and propose a plan" \
194
+ --execution-mode pty \
195
+ --agent-arg codex \
196
+ --agent-arg "{instruction}"
197
+ ```
198
+
199
+ Configured agents can also opt into PTY mode with `modes.pty.enabled: true`, an optional `modes.pty.argv`, and terminal settings such as `rows`, `columns`, and `term`. When `modes.pty.argv` is omitted, PTY mode falls back to the configured `modes.exec.argv`.
200
+
201
+ If you want to copy a built-in preset into another config, the example file shows practical command templates for Codex CLI, Claude Code, GitHub Copilot CLI, Aider, Cursor, Amp, Antigravity, OpenClaw, Hermes, and Pi under `agents:`.
202
+
203
+ Configured routes may also define ordered `fallbacks:`. These are route-level retries, not provider-request retries: if a route times out, exits non-zero, fails verification, trips enforced policy, or returns a non-pass audit status, `opensaddle run` can retry the task on the next configured route in the chain.
204
+
205
+ You can also export the current config schema for documentation or editor integration:
206
+
207
+ ```bash
208
+ uv run opensaddle config schema > opensaddle.schema.json
209
+ ```
210
+
211
+ 4. Run a benchmark suite:
212
+
213
+ ```bash
214
+ uv run opensaddle bench run examples/benchmark-suite.yaml --db .opensaddle/opensaddle.db
215
+ ```
216
+
217
+ The checked-in suite exercises both deterministic mock output and a configured local route. Configured routes use the normal local worktree execution path and evaluate their declared `success_checks`. The legacy `existing` route form instead runs its explicit argv directly in its configured working directory and reports command exit status; keep those commands non-destructive or use a disposable repository.
218
+
219
+ 5. Inspect local route outcome stats:
220
+
221
+ ```bash
222
+ uv run opensaddle routes stats --db .opensaddle/opensaddle.db
223
+ ```
224
+
225
+ 6. Sync and inspect model metadata if you have an OpenRouter API key:
226
+
227
+ ```bash
228
+ uv run opensaddle models sync openrouter --config examples/opensaddle.yaml
229
+ uv run opensaddle models list --config examples/opensaddle.yaml
230
+ uv run opensaddle models health set --config examples/opensaddle.yaml --model openai/gpt-4o --status degraded --reason "local retry spikes"
231
+ uv run opensaddle models health list --config examples/opensaddle.yaml
232
+ ```
233
+
234
+ `opensaddle estimate` now carries those local provider/model health signals into route scoring as conservative penalties with explicit rationale lines, but it does not block runtime execution.
235
+
236
+ 7. Inspect prior agent history as dry-run proposals:
237
+
238
+ ```bash
239
+ uv run opensaddle import-history codex --source ~/.codex
240
+ uv run opensaddle import-history claude --source ~/.claude
241
+ uv run opensaddle import-history copilot --source ~/.github/copilot
242
+ ```
243
+
244
+ `opensaddle import-history` is proposal-only in v1. It scans local transcript files, redacts secret-like values before emitting summaries, and prints structured hints such as observed agents, task patterns, estimated usage, and candidate route suggestions. It does not rewrite `opensaddle.yaml`, and it does not persist raw transcript contents into SQLite by default.
245
+
246
+ 8. Launch the dashboard:
247
+
248
+ ```bash
249
+ uv run opensaddle dashboard --root .
250
+ ```
251
+
252
+ When `.opensaddle/opensaddle.db` exists, the dashboard reads episode data from the SQLite `EpisodeStore`. If the database is absent, it falls back to JSON snapshots under `.opensaddle/episodes/`.
253
+ The dashboard also surfaces lightweight environment and audit review signals through the HTML view and `/api/reviews`.
254
+ The V1 operational view answers five questions directly: what ran, what failed, which routes perform best, which failures are recurring, and what route to try next. Route ops visibility now includes per-route aggregates, task-family routing stats, route comparison, recurring failures, benchmark summaries, and rolled-up verification/audit pass rates. Health is derived from local outcomes, and fallback-specific fields stay `n/a` until those slices are recorded in stored episode schemas.
255
+ The dashboard API is local-first and intentionally has no authentication when bound to localhost. Do not expose it on a hosted or multi-user interface in V1.
256
+
257
+ ## Auditor Visibility
258
+
259
+ Auditors only receive the packet inputs explicitly granted by `can_see`, minus anything removed by `cannot_see`.
260
+
261
+ - `trajectory` includes only `auditor_visible` events.
262
+ - `internal_only` events are never forwarded to auditors.
263
+ - visible command output, diff patches, and trajectory text are redacted for secret-like values and bounded before they are persisted or sent to auditors.
264
+ - auditors do not receive full repo contents or hidden execution state unless you explicitly model and expose those inputs in a future slice.
265
+
266
+ ## What It Does
267
+
268
+ `opensaddle` sits above agent runtimes and chooses, runs, observes, verifies, audits, and learns from execution strategies.
269
+
270
+ The main conceptual object is `AgentRouter`.
271
+
272
+ An `AgentRouter` chooses a route such as:
273
+
274
+ ```text
275
+ agent runtime + model + context strategy + permission policy + audit policy + verification plan + fallback policy
276
+ ```
277
+
278
+ `ModelRouter` is a lower-level primitive used inside routes, councils, and adapters when a model choice is needed.
279
+
280
+ ## First Target
281
+
282
+ V1 is local-first:
283
+
284
+ - Python package named `opensaddle`
285
+ - CLI named `opensaddle`
286
+ - SQLite episode store
287
+ - YAML configuration
288
+ - local git worktree execution
289
+ - observe-only and local policy enforcement modes
290
+ - configurable CLI adapters for Codex, Claude Code, GitHub Copilot, Aider, Cursor, Amp, Antigravity, OpenClaw, Hermes, Pi, and other agent CLIs
291
+ - built-in YAML presets for those CLIs on top of the generic CLI adapter
292
+ - optional OpenRouter model registry integration
293
+ - lightweight local web dashboard
294
+
295
+ ## V1 Support Matrix
296
+
297
+ | Surface | V1 status | Notes |
298
+ | --- | --- | --- |
299
+ | `opensaddle` CLI | supported | Stable command groups: `init`, `config validate`, `estimate`, `run`, `episodes`, `routes stats`, `bench run`, `models sync/list/health`, `dashboard`, and `import-history`. |
300
+ | `opensaddle.yaml` | supported | Strict user-owned YAML schema with stable top-level sections and route forms documented in [`specs/08-v1-public-contract.md`](specs/08-v1-public-contract.md). |
301
+ | Route kinds for `opensaddle run` | supported | `single_agent`, `agent_with_auditors`, and `review_council`. |
302
+ | Additional route kinds | planned/experimental | `diverse_generation`, `diverse_generation_council`, `leader_veto_council`, `loop_council`, and `planner_worker_verifier_loop` are recognized for config/planning but are not executable in v1. |
303
+ | Episode persistence | supported | Raw run JSON artifacts live under `.opensaddle/episodes/`; summarized episodes live in SQLite at `.opensaddle/opensaddle.db`. |
304
+ | Python imports | provisional | No stable Python API is promised for v1 beyond `opensaddle.__version__`; prefer the CLI, YAML, and storage formats as the public contract. |
305
+ | Explicitly post-v1 | not included yet | Container sandboxing, network/secret isolation, learned routing, and full execution for non-`review_council` council families. |
306
+
307
+ ## Specs
308
+
309
+ - [`specs/00-product-brief.md`](specs/00-product-brief.md)
310
+ - [`specs/01-architecture.md`](specs/01-architecture.md)
311
+ - [`specs/02-configuration.md`](specs/02-configuration.md)
312
+ - [`specs/03-execution-and-audit.md`](specs/03-execution-and-audit.md)
313
+ - [`specs/04-benchmarking.md`](specs/04-benchmarking.md)
314
+ - [`specs/05-dashboard.md`](specs/05-dashboard.md)
315
+ - [`specs/06-implementation-slices.md`](specs/06-implementation-slices.md)
316
+ - [`specs/08-v1-public-contract.md`](specs/08-v1-public-contract.md)
317
+ - [`specs/10-v2-enterprise-pilot-adr.md`](specs/10-v2-enterprise-pilot-adr.md)
318
+ - [`specs/99-open-questions.md`](specs/99-open-questions.md)