readiness-as-code 0.7.0__py3-none-any.whl

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 (43) hide show
  1. readiness_as_code-0.7.0.dist-info/METADATA +272 -0
  2. readiness_as_code-0.7.0.dist-info/RECORD +43 -0
  3. readiness_as_code-0.7.0.dist-info/WHEEL +5 -0
  4. readiness_as_code-0.7.0.dist-info/entry_points.txt +3 -0
  5. readiness_as_code-0.7.0.dist-info/licenses/LICENSE +21 -0
  6. readiness_as_code-0.7.0.dist-info/top_level.txt +1 -0
  7. ready/__init__.py +0 -0
  8. ready/__main__.py +4 -0
  9. ready/adapters/__init__.py +61 -0
  10. ready/adapters/ado.py +174 -0
  11. ready/adapters/github_issues.py +154 -0
  12. ready/adapters/jira.py +283 -0
  13. ready/engine.py +460 -0
  14. ready/examples/engineering-review/checkpoint-definitions.json +522 -0
  15. ready/examples/governance/checkpoint-definitions.json +303 -0
  16. ready/examples/operational-review/checkpoint-definitions.json +273 -0
  17. ready/examples/security-baseline/checkpoint-definitions.json +177 -0
  18. ready/examples/service-migration/checkpoint-definitions.json +148 -0
  19. ready/examples/starter-pack/checkpoint-definitions.json +220 -0
  20. ready/examples/starter-pack/exceptions.json +4 -0
  21. ready/examples/starter-pack/external-evidence.json +4 -0
  22. ready/examples/telemetry/checkpoint-definitions.json +157 -0
  23. ready/examples/web-api/README.md +45 -0
  24. ready/examples/web-api/checkpoint-definitions.json +305 -0
  25. ready/formatters/__init__.py +6 -0
  26. ready/formatters/json_formatter.py +10 -0
  27. ready/formatters/markdown.py +175 -0
  28. ready/formatters/terminal.py +216 -0
  29. ready/mcp_server.py +318 -0
  30. ready/plugins/__init__.py +7 -0
  31. ready/plugins/base.py +60 -0
  32. ready/plugins/external_plugin.py +41 -0
  33. ready/plugins/file_count_plugin.py +15 -0
  34. ready/plugins/file_exists_plugin.py +19 -0
  35. ready/plugins/glob_plugin.py +45 -0
  36. ready/plugins/grep_plugin.py +62 -0
  37. ready/plugins/hybrid_plugin.py +68 -0
  38. ready/plugins/json_path_plugin.py +52 -0
  39. ready/plugins/registry.py +61 -0
  40. ready/plugins/utils.py +91 -0
  41. ready/ready.py +2717 -0
  42. ready/validators.py +52 -0
  43. ready/watch.py +256 -0
@@ -0,0 +1,272 @@
1
+ Metadata-Version: 2.4
2
+ Name: readiness-as-code
3
+ Version: 0.7.0
4
+ Summary: Know before you ship — continuous readiness as a folder in your repo.
5
+ Author: Jeremiah Walters
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/jtwalters25/readiness-as-code
8
+ Project-URL: Documentation, https://github.com/jtwalters25/readiness-as-code/tree/main/docs
9
+ Project-URL: Repository, https://github.com/jtwalters25/readiness-as-code
10
+ Project-URL: Issues, https://github.com/jtwalters25/readiness-as-code/issues
11
+ Keywords: compliance,code-review,quality-gates,operational-readiness,devops,ci-cd,policy-as-code
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Quality Assurance
20
+ Classifier: Topic :: Software Development :: Testing
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Provides-Extra: mcp
25
+ Requires-Dist: mcp>=1.0; extra == "mcp"
26
+ Dynamic: license-file
27
+
28
+ <p align="center">
29
+ <img src="docs/assets/banner.svg" alt="ready" width="600" />
30
+ </p>
31
+
32
+ <h3 align="center">
33
+ AI tools made your team faster. They didn't make your team safer.
34
+ </h3>
35
+
36
+ <p align="center">
37
+ <strong>ready</strong> is the discipline layer that keeps pace with AI velocity —<br/>
38
+ review criteria as committed definitions, evaluated on every change,<br/>
39
+ with drift detected automatically before it becomes an incident.
40
+ </p>
41
+
42
+ <p align="center">
43
+ No infrastructure. No SaaS. No subscription.<br/>
44
+ JSON definitions + Python scanner + CI template.
45
+ </p>
46
+
47
+ <p align="center">
48
+ <a href="#quickstart">Quickstart</a> •
49
+ <a href="docs/getting-started.md">Docs</a> •
50
+ <a href="docs/architecture-and-tradeoffs.md">Architecture</a> •
51
+ <a href="docs/verification-types.md">Verification Types</a> •
52
+ <a href="docs/ci-integration.md">CI Integration</a>
53
+ </p>
54
+
55
+ <p align="center">
56
+ <a href=".readiness/review-baseline.json">
57
+ <img src="https://img.shields.io/badge/ready-100%25-brightgreen" alt="ready: 100%" />
58
+ </a>
59
+ <img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT License" />
60
+ </p>
61
+
62
+ ---
63
+
64
+ ## Connect with Jeremiah Walters
65
+
66
+ [![LinkedIn](https://img.shields.io/badge/linkedin-jeremiahwalters-blue?style=for-the-badge&logo=linkedin)](https://www.linkedin.com/in/jeremiahwalters)
67
+ [![Twitter](https://img.shields.io/badge/twitter-@Afrotechnician-1da1f2?style=for-the-badge&logo=twitter)](https://x.com/Afrotechnician)
68
+
69
+ ## The Problem: Velocity Outran Discipline
70
+
71
+ AI coding tools removed the friction from writing and shipping code. They did not remove the friction from the discipline work — health checks, secrets hygiene, on-call registration, telemetry coverage, auth on every endpoint. Velocity is now AI-speed. Discipline is still person-speed. The gap between the two is where incidents live.
72
+
73
+ Production breakdowns keep hitting strong engineering orgs — not because they lack talent, but because velocity outran the scaffolding. Smart teams moved fast, skipped the prep work, and found out later that the checks weren't there. `ready` is the tool that implements this practice. It replaces **prep work**, not judgment.
74
+
75
+ ## Before / After
76
+
77
+ | Before | After |
78
+ |--------|-------|
79
+ | AI writes code in seconds; readiness checks take hours | Readiness checks run in the same pipeline, same pace |
80
+ | Manual checklists that can't keep up with AI velocity | Automated scan on every PR — no human bottleneck |
81
+ | Point-in-time review ceremonies | Continuous compliance, not a quarterly ritual |
82
+ | Drift detected by incidents | Drift detected before merge |
83
+ | Tribal knowledge of what's missing | Structured gap list with file-path evidence |
84
+ | "Are we ready?" is a subjective question | "Are we ready?" has a deterministic answer |
85
+ | Accepted risks forgotten over time | Accepted risks expire and re-surface automatically |
86
+
87
+ ## Quickstart
88
+
89
+ ```bash
90
+ pip install readiness-as-code
91
+
92
+ cd your-repo
93
+ ready scan
94
+ ```
95
+
96
+ > **Windows / PATH issues?** Use `python -m ready scan` — works anywhere Python is installed.
97
+
98
+ ```
99
+ ready? — your-service 80% 1 blocking · 2 warnings
100
+
101
+ ✗ No secrets in code
102
+ src/config.py:14
103
+ → Remove hardcoded keys. Use environment variables or a secrets manager.
104
+
105
+ + 2 warnings (ready scan --verbose)
106
+ ```
107
+
108
+ **No config. No accounts. No init required.** `ready scan` auto-detects your project type and runs immediately. When you're ready to customize, run `ready init`.
109
+
110
+ When everything is passing:
111
+
112
+ ```
113
+ ready? — your-service 100% ✓ ▲ +12%
114
+ ```
115
+
116
+ One line. The drift indicator appears automatically whenever a committed baseline exists.
117
+
118
+ ## Origin
119
+
120
+ Built from production use managing 86+ readiness checkpoints across enterprise reliability engineering at scale. This is the vendor-neutral, portable version of a system that enforces production readiness across real services handling real incidents — not a weekend experiment.
121
+
122
+ ## How It Works
123
+
124
+ ```
125
+ your-repo/
126
+ └── .readiness/
127
+ ├── checkpoint-definitions.json # What to check
128
+ ├── exceptions.json # Accepted risks + expiry
129
+ ├── external-evidence.json # Human attestations for non-code artifacts
130
+ └── review-baseline.json # Last scan snapshot (committed = audit trail)
131
+ ```
132
+
133
+ Four JSON files and a scanner. Checkpoints resolve through three verification types: **code checks** (grep/glob/file_exists against the repo), **external checks** (human attestations for artifacts outside the repo), and **hybrid checks** (both must pass). → [Architecture details](docs/architecture-and-tradeoffs.md) · [Verification types](docs/verification-types.md)
134
+
135
+ ## Checkpoint Packs
136
+
137
+ Start with a curated pack, then customize:
138
+
139
+ ```bash
140
+ ready init # Universal starter (default)
141
+ ready init --pack web-api # REST/HTTP API checks
142
+ ready init --pack security-baseline # Secrets, dependency hygiene, security policy
143
+ ready init --pack telemetry # Logging, tracing, metrics, dashboards
144
+ ready init --pack engineering-review # Full engineering review (arch, security, testing, AI/RAI)
145
+ ready init --pack operational-review # Operational readiness (SLOs, on-call, data, capacity)
146
+ ready init --pack governance # SDLC gates + external review attestations
147
+ ready init --pack service-migration # Service identity migration, auth provisioning, cutover
148
+ ready init --list-packs # Show all available packs
149
+ ```
150
+
151
+ | Pack | Checks | Best for |
152
+ |------|--------|----------|
153
+ | `starter` | 11 | Any repo |
154
+ | `web-api` | 17 | REST/HTTP services |
155
+ | `security-baseline` | 8 | Any repo with sensitive data |
156
+ | `telemetry` | 8 | Production services |
157
+ | `engineering-review` | 26 | Pre-launch engineering review |
158
+ | `operational-review` | 14 | Pre-launch operational review |
159
+ | `governance` | 15 | SDLC compliance + sign-off tracking |
160
+ | `service-migration` | 9 | Service identity migration + cutover |
161
+
162
+ ## Proven in Production
163
+
164
+ ready is based on a system used in enterprise reliability engineering environments,
165
+ enforcing 80+ readiness checkpoints across real services.
166
+
167
+ It has been used to:
168
+ - continuously evaluate production readiness across services
169
+ - detect regression before deployment
170
+ - reduce reliance on manual review preparation
171
+
172
+ This open-source version is the portable, vendor-neutral implementation.
173
+
174
+ ## Key Capabilities
175
+
176
+ - **Auto-drift detection.** If a committed baseline exists, every scan shows a delta: `▲ +12%` or `▼ -5%`. No flags, no extra commands.
177
+ - **Closed-loop work item tracking.** Gaps become tracked work items (GitHub, Azure DevOps, Jira); ticket-closed-but-code-failing flags as **regression**, code-fixed-but-ticket-open as **stale**.
178
+ - **Cross-repo aggregation.** `ready aggregate` turns multiple baselines into an HTML heatmap — *"telemetry gaps in 4 of 5 services"* is a platform problem, not a team problem.
179
+ - **Expiring accepted risks.** Acknowledge a gap with a justification and expiry date; the scanner re-flags it when the expiry passes.
180
+ - **Readiness audit.** `ready audit` reports the health of the readiness system itself — exception age, definition staleness, review_by coverage, score trend.
181
+ - **Codebase-aware inference.** `ready infer` analyzes stack, frameworks, dependencies, ADRs, and auth patterns to propose tailored checkpoints you approve one at a time.
182
+ - **AI-assisted authoring.** `ready author --from guidelines.md` generates a paste-ready prompt for any model (Claude, ChatGPT, Copilot, Cursor, Gemini).
183
+ - **README badge.** `ready badge` generates a shields.io badge from the committed score.
184
+ - **CI gating.** Non-zero exit on red failures; templates for GitHub Actions, Azure Pipelines, GitLab CI. → [Details](docs/ci-integration.md)
185
+ - **Azure DevOps extension.** Pipeline task publishes each checkpoint as a test case, plus a dashboard widget for score, trend, and blocking count. → [Details](ado-extension/README.md)
186
+
187
+ ## What This Is Not
188
+
189
+ - **Not a static analysis tool.** SonarQube checks code quality. This checks whether your service meets its review requirements.
190
+ - **Not a policy engine.** OPA/Sentinel enforce infra policies at deploy time. This tracks operational and engineering readiness across code and non-code artifacts.
191
+ - **Not a compliance SaaS.** Drata/RegScale automate regulatory frameworks. This enforces your team's own internal review standards.
192
+ - **Not a replacement for AI coding tools.** It's the complement to them — the discipline layer that keeps pace with the velocity they enable.
193
+ - **Not a replacement for review meetings.** This replaces the prep work so the meeting can focus on judgment calls the scanner can't make.
194
+
195
+ ## Commands
196
+
197
+ ```bash
198
+ # Scanning
199
+ ready scan # Score + blocking items
200
+ ready scan --verbose # Full detail — all checks, evidence, fix hints
201
+ ready scan --calibrate # Report-only (no exit code failure)
202
+ ready scan --json # Machine-readable output
203
+ ready scan --baseline FILE # Write baseline snapshot (enables drift tracking)
204
+ ready scan --suggest-tuning # Show pattern tuning suggestions after scan
205
+
206
+ # Setup
207
+ ready init # Scaffold .readiness/ with starter pack
208
+ ready init --pack web-api # Scaffold with a specific pack
209
+ ready init --list-packs # List available packs
210
+
211
+ # Authoring & inference
212
+ ready infer # Analyze codebase → propose tailored checkpoints (human approves each)
213
+ ready author --from FILE # Generate AI prompt from a guideline document
214
+
215
+ # Audit trail
216
+ ready badge # Generate README badge from current score
217
+ ready decisions # Show all active, expiring, and expired exceptions
218
+ ready history [BASELINES...] # Show readiness trend from baseline snapshots
219
+ ready audit # Audit exception health, definition staleness, and score health
220
+
221
+ # Work items
222
+ ready items --create # Propose + create work items (human approves each)
223
+ ready items --verify # Cross-check work items vs code
224
+
225
+ # Cross-repo
226
+ ready aggregate PATHS... # Cross-repo heatmap from multiple baselines
227
+ ready aggregate PATHS... --html # Generate self-contained HTML heatmap report
228
+ ```
229
+
230
+ ## AI Integration
231
+
232
+ ready ships a [Model Context Protocol](https://modelcontextprotocol.io) server (`ready-mcp`) so any MCP client — Claude, Cursor, Copilot — can run scans and inspect checkpoints directly.
233
+
234
+ ```bash
235
+ pip install "readiness-as-code[mcp]"
236
+ ready-mcp
237
+ ```
238
+
239
+ | Tool | Description |
240
+ |------|-------------|
241
+ | `scan_repo` | Full readiness scan with results |
242
+ | `list_checkpoints` | View all checkpoint definitions |
243
+ | `explain_checkpoint` | Deep-dive on a specific check |
244
+ | `aggregate_baselines` | Cross-repo heatmap for systemic gap detection |
245
+
246
+ For prompt-based authoring with any model:
247
+
248
+ ```bash
249
+ ready author --from docs/ops-review.md # generates author-prompt.md, paste into any AI
250
+ ```
251
+
252
+ **→ [MCP setup for Claude Desktop, Cursor, VS Code](mcp/README.md)**
253
+
254
+ ## Design Principles
255
+
256
+ These principles define what *readiness as code* means in practice. They are not implementation choices — they are the practice itself.
257
+
258
+ 1. **Detection, not decisions.** The scanner finds gaps. Humans decide what to do.
259
+ 2. **Continuous, not ceremonial.** Checked on every PR, not once a quarter.
260
+ 3. **Velocity-aware, not velocity-hostile.** Designed to run at the speed of AI-assisted development — no human bottleneck in the loop.
261
+ 4. **Portable, not hosted.** Files in your repo. No infrastructure.
262
+ 5. **Evidence-backed, not trust-based.** Every assertion has a file path, attestation, or work item.
263
+ 6. **Expiring, not permanent.** Accepted risks have expiry dates. Nothing is forever.
264
+ 7. **Score-first, not report-first.** The answer to "are we ready?" is one line. Detail is on demand.
265
+
266
+ ## Contributing
267
+
268
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
269
+
270
+ ## License
271
+
272
+ [MIT](LICENSE)
@@ -0,0 +1,43 @@
1
+ readiness_as_code-0.7.0.dist-info/licenses/LICENSE,sha256=E4kPWhAHNj0N_P0mcA_JARQCIu92VsuJQ9ThV5vrd0o,1073
2
+ ready/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ ready/__main__.py,sha256=3ptnlLI5lhgJA96Gp-kv6_3xTbRZaQwp_kWNQB1vxEA,68
4
+ ready/engine.py,sha256=tJ5cchXYhq8DKb13fZC9GArfDVO58ybKZj-m1Lcdsj0,14916
5
+ ready/mcp_server.py,sha256=T0rXuf5VyoiSXv-pe3KXJQoOk-gGuMyv7mYFEwkvNhQ,11146
6
+ ready/ready.py,sha256=HKTdEnO4JsiebZK24EL49q7Tpt9FO0rInoRJw_610tM,113708
7
+ ready/validators.py,sha256=7PYvZvzadbQ04IojixpbivcDiRmf8rGBV2YPoSg557s,1241
8
+ ready/watch.py,sha256=lD2bcCEB18FmbHjfef7DDnnH6A66uBQXiIpUMtKcc3Y,9270
9
+ ready/adapters/__init__.py,sha256=4T0mxm9Ho70UuPhgLWNaqWR5gILvgaxmhStYVBA_Nr8,1488
10
+ ready/adapters/ado.py,sha256=MZbR6AgXj-p-jkMGjpLZzLxJwCpf8cENDoZYimvdtog,6477
11
+ ready/adapters/github_issues.py,sha256=qYLj1cuUwK7suh6vGFdL2GmsW7EFt28S6BNAIUcZfiI,5382
12
+ ready/adapters/jira.py,sha256=8e7mMOcHp8IfYYQiSUFtjbTtOIUfWPj1tXF6B2R50vQ,10523
13
+ ready/examples/engineering-review/checkpoint-definitions.json,sha256=Rihp_KbVIiXcpSQbiQjdmrneWv-YD6-mw05l98m8EBs,20433
14
+ ready/examples/governance/checkpoint-definitions.json,sha256=IMIcd8rB662DLSyNbsI2s5FAQfE5vpSu59NfvJih2bM,11420
15
+ ready/examples/operational-review/checkpoint-definitions.json,sha256=cM6Vl4U3YtLgPbMJtqSVak2zh0j3DtqjBlcz3PkZYxs,10793
16
+ ready/examples/security-baseline/checkpoint-definitions.json,sha256=Y7YMEET4rPCRroYk58Q3eBS3U5s0azBHOKORq98PuxY,7901
17
+ ready/examples/service-migration/checkpoint-definitions.json,sha256=C5I0oqmhmxg6LloaUpMNeNJuxFjxKTXNJuJonvcAsxg,7506
18
+ ready/examples/starter-pack/checkpoint-definitions.json,sha256=VY8exptIN84jN3L9fW3Awf2jo_U7NY79UnXXMNUZp-g,8884
19
+ ready/examples/starter-pack/exceptions.json,sha256=QMuE-XJFXaztARMzVaVMrrk9TlWNbcbeqMe2dJQU764,43
20
+ ready/examples/starter-pack/external-evidence.json,sha256=zi6Akhc2l0zvtOfS5uko2jvMPzPYr-SB1OINbXVQJjM,45
21
+ ready/examples/telemetry/checkpoint-definitions.json,sha256=mELRe15GYMmE1KeBNUdGHIkGBXikcFDpgnoQoVhb1yM,7840
22
+ ready/examples/web-api/README.md,sha256=G9FbBzON0hBMdDJKDjoPqvIcSqw4G9ZKrDaqvVmeKas,1262
23
+ ready/examples/web-api/checkpoint-definitions.json,sha256=e0Pth930uPCwghV0PfsbOT41Ws6ljs9lfcQrFs5VqXQ,14036
24
+ ready/formatters/__init__.py,sha256=aehkxsKbVE57uQB2Q2hbw3GR_ysCOTPHnhmMcTtmJCc,199
25
+ ready/formatters/json_formatter.py,sha256=TPClaNznx1O2Y6qaQuhVQKio0RpOFAn0NKZ-UfXbpe8,249
26
+ ready/formatters/markdown.py,sha256=297CSFtNL0lJDhIItYd6FOhzl03PCAz_jm-X-X58aJo,6215
27
+ ready/formatters/terminal.py,sha256=gXg0GpXtXpnatxGC9pBv3wof5siaSFjAtu67sE8N-2E,6951
28
+ ready/plugins/__init__.py,sha256=OEjJELtk4RLU5fLgv-uVV6Ol-fDzCkCH4WmePpwedRQ,297
29
+ ready/plugins/base.py,sha256=i1j1-HlLEyMepP0_Z3mbXFmoDqk5qfDoU6tn9I5eihc,1689
30
+ ready/plugins/external_plugin.py,sha256=YUaAaPmkefDdWZJSh32ex18T6J4OJPi9qLlPxCc7Goc,1755
31
+ ready/plugins/file_count_plugin.py,sha256=Qzb47S5d4vf9zYBXLoCCy_pRxI5RFyrS1cdOBxxAxvc,556
32
+ ready/plugins/file_exists_plugin.py,sha256=1wwKcxWWY6zkS4BthKdfZy2_s14boXeJi7QOJlDB11I,728
33
+ ready/plugins/glob_plugin.py,sha256=UAOYzpQssHsCcN2wZtr_GIdwYjtUo-vIlo8y6vF-ckg,1686
34
+ ready/plugins/grep_plugin.py,sha256=zIpA9YoaWkcS15iKiK2tu3JSM0YmmoUzoiiwM-lz5uI,2560
35
+ ready/plugins/hybrid_plugin.py,sha256=CD3fipyguSUuYDfmIO_cMyvWl5WbuNukt_w0LbqG5_A,2934
36
+ ready/plugins/json_path_plugin.py,sha256=Tx2nmUX25jEtTttVEnNEjHCnCqynxZe7BvXVRH46uD8,2017
37
+ ready/plugins/registry.py,sha256=S8AR7eVZ59kiDzRUUmj2JZq8ktSMeIwKHgW8suCYpdk,2062
38
+ ready/plugins/utils.py,sha256=eddbB6elBm-ukQ7IIn1QS-k-ZPvai4jTMZLjJFfV1MI,3273
39
+ readiness_as_code-0.7.0.dist-info/METADATA,sha256=d30PPzzATyRf1cdMf-VLZBZXJYjYa_ogzevE5Dit88Q,13298
40
+ readiness_as_code-0.7.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
41
+ readiness_as_code-0.7.0.dist-info/entry_points.txt,sha256=85h9s35y4PzxPjUOqq2oNbVsmfiT06A-oV4zKwlKQBI,77
42
+ readiness_as_code-0.7.0.dist-info/top_level.txt,sha256=7RpUW7heVYFrv5VmsCiyoLxFa4j0n28mbAQBBIgkGUs,6
43
+ readiness_as_code-0.7.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ ready = ready.ready:main
3
+ ready-mcp = ready.mcp_server:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jeremiah Walters
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 @@
1
+ ready
ready/__init__.py ADDED
File without changes
ready/__main__.py ADDED
@@ -0,0 +1,4 @@
1
+ from ready.ready import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -0,0 +1,61 @@
1
+ """
2
+ Abstract work item adapter interface.
3
+
4
+ Implement this to integrate with your project tracker.
5
+ Ships with GitHub Issues and Azure DevOps adapters.
6
+ """
7
+
8
+ from abc import ABC, abstractmethod
9
+ from dataclasses import dataclass
10
+ from typing import Optional
11
+
12
+
13
+ @dataclass
14
+ class WorkItemDraft:
15
+ checkpoint_id: str
16
+ title: str
17
+ description: str
18
+ severity: str
19
+ evidence: list[str]
20
+ fix_hint: str
21
+ doc_link: str
22
+ guideline: str
23
+ guideline_section: str
24
+ labels: list[str]
25
+
26
+
27
+ @dataclass
28
+ class WorkItemResult:
29
+ id: str
30
+ url: str
31
+ status: str
32
+ checkpoint_id: str
33
+
34
+
35
+ class WorkItemAdapter(ABC):
36
+ """Interface for work item integrations."""
37
+
38
+ @abstractmethod
39
+ def create_draft(self, draft: WorkItemDraft) -> WorkItemResult:
40
+ """Create a work item from a draft. Returns the created item."""
41
+ ...
42
+
43
+ @abstractmethod
44
+ def get_status(self, item_id: str) -> Optional[WorkItemResult]:
45
+ """Get the current status of a work item."""
46
+ ...
47
+
48
+ @abstractmethod
49
+ def list_open(self, label: str | None = None) -> list[WorkItemResult]:
50
+ """List open work items, optionally filtered by label."""
51
+ ...
52
+
53
+ @abstractmethod
54
+ def close(self, item_id: str, reason: str = "Resolved by scan") -> bool:
55
+ """Close a work item."""
56
+ ...
57
+
58
+ @abstractmethod
59
+ def reopen(self, item_id: str, reason: str = "Regression detected by scan") -> bool:
60
+ """Reopen a previously closed work item."""
61
+ ...
ready/adapters/ado.py ADDED
@@ -0,0 +1,174 @@
1
+ """
2
+ Azure DevOps adapter for ready work item tracking.
3
+
4
+ Requires: AZURE_DEVOPS_PAT and AZURE_DEVOPS_ORG environment variables.
5
+ """
6
+
7
+ import json
8
+ import os
9
+ import base64
10
+ import urllib.request
11
+ import urllib.error
12
+ from typing import Optional
13
+
14
+ from . import WorkItemAdapter, WorkItemDraft, WorkItemResult
15
+
16
+
17
+ class AzureDevOpsAdapter(WorkItemAdapter):
18
+ """Create and track readiness gaps as Azure DevOps work items (PBIs/Bugs)."""
19
+
20
+ def __init__(
21
+ self,
22
+ org: str | None = None,
23
+ project: str | None = None,
24
+ pat: str | None = None,
25
+ work_item_type: str = "Product Backlog Item",
26
+ ):
27
+ self.org = org or os.environ.get("AZURE_DEVOPS_ORG", "")
28
+ self.project = project or os.environ.get("AZURE_DEVOPS_PROJECT", "")
29
+ self.pat = pat or os.environ.get("AZURE_DEVOPS_PAT", "")
30
+ self.work_item_type = work_item_type
31
+ self.api_base = f"https://dev.azure.com/{self.org}/{self.project}/_apis"
32
+
33
+ if not self.org or not self.project:
34
+ raise ValueError(
35
+ "Azure DevOps org and project required. "
36
+ "Set AZURE_DEVOPS_ORG and AZURE_DEVOPS_PROJECT env vars."
37
+ )
38
+
39
+ def _headers(self) -> dict:
40
+ creds = base64.b64encode(f":{self.pat}".encode()).decode()
41
+ return {
42
+ "Content-Type": "application/json-patch+json",
43
+ "Authorization": f"Basic {creds}",
44
+ }
45
+
46
+ def _request(self, method: str, path: str, data=None) -> dict:
47
+ url = f"{self.api_base}{path}"
48
+ if "?" in url:
49
+ url += "&api-version=7.1"
50
+ else:
51
+ url += "?api-version=7.1"
52
+
53
+ body = json.dumps(data).encode("utf-8") if data else None
54
+ req = urllib.request.Request(
55
+ url, data=body, headers=self._headers(), method=method
56
+ )
57
+ try:
58
+ with urllib.request.urlopen(req) as resp:
59
+ return json.loads(resp.read().decode("utf-8"))
60
+ except urllib.error.HTTPError as e:
61
+ error_body = e.read().decode("utf-8") if e.fp else ""
62
+ raise RuntimeError(f"ADO API error {e.code}: {error_body}") from e
63
+
64
+ def create_draft(self, draft: WorkItemDraft) -> WorkItemResult:
65
+ description = (
66
+ f"<b>Checkpoint:</b> {draft.checkpoint_id}<br>"
67
+ f"<b>Severity:</b> {draft.severity.upper()}<br>"
68
+ f"<b>Guideline:</b> {draft.guideline} — {draft.guideline_section}<br><br>"
69
+ f"{draft.description}<br><br>"
70
+ f"<b>Evidence:</b><ul>{''.join(f'<li>{e}</li>' for e in draft.evidence)}</ul>"
71
+ f"<b>Fix:</b> {draft.fix_hint}"
72
+ )
73
+ if draft.doc_link:
74
+ description += f'<br><br><a href="{draft.doc_link}">Documentation</a>'
75
+
76
+ patch_doc = [
77
+ {"op": "add", "path": "/fields/System.Title", "value": f"[{draft.severity.upper()}] {draft.title}"},
78
+ {"op": "add", "path": "/fields/System.Description", "value": description},
79
+ {"op": "add", "path": "/fields/System.Tags", "value": f"readiness-gap; {draft.checkpoint_id}; severity:{draft.severity}"},
80
+ ]
81
+
82
+ result = self._request(
83
+ "POST",
84
+ f"/wit/workitems/${self.work_item_type}",
85
+ patch_doc,
86
+ )
87
+
88
+ wi_id = str(result["id"])
89
+ url = result.get("_links", {}).get("html", {}).get("href", "")
90
+
91
+ return WorkItemResult(
92
+ id=wi_id,
93
+ url=url,
94
+ status=result.get("fields", {}).get("System.State", "New"),
95
+ checkpoint_id=draft.checkpoint_id,
96
+ )
97
+
98
+ def get_status(self, item_id: str) -> Optional[WorkItemResult]:
99
+ try:
100
+ result = self._request("GET", f"/wit/workitems/{item_id}")
101
+ fields = result.get("fields", {})
102
+ tags = fields.get("System.Tags", "")
103
+ cp_id = ""
104
+ for tag in tags.split(";"):
105
+ tag = tag.strip()
106
+ if tag.startswith("cp:") or (len(tag) > 3 and "-" in tag and tag[0].isalpha()):
107
+ cp_id = tag
108
+ break
109
+
110
+ return WorkItemResult(
111
+ id=str(result["id"]),
112
+ url=result.get("_links", {}).get("html", {}).get("href", ""),
113
+ status=fields.get("System.State", "Unknown"),
114
+ checkpoint_id=cp_id,
115
+ )
116
+ except RuntimeError:
117
+ return None
118
+
119
+ def list_open(self, label: str | None = None) -> list[WorkItemResult]:
120
+ tag_filter = "readiness-gap"
121
+ if label:
122
+ tag_filter += f" AND {label}"
123
+
124
+ wiql = {
125
+ "query": (
126
+ f"SELECT [System.Id] FROM WorkItems "
127
+ f"WHERE [System.Tags] CONTAINS '{tag_filter}' "
128
+ f"AND [System.State] <> 'Closed' AND [System.State] <> 'Done' "
129
+ f"ORDER BY [System.CreatedDate] DESC"
130
+ )
131
+ }
132
+
133
+ # WIQL uses regular JSON content type
134
+ url = f"{self.api_base}/wit/wiql?api-version=7.1"
135
+ body = json.dumps(wiql).encode("utf-8")
136
+ headers = self._headers()
137
+ headers["Content-Type"] = "application/json"
138
+ req = urllib.request.Request(url, data=body, headers=headers, method="POST")
139
+
140
+ try:
141
+ with urllib.request.urlopen(req) as resp:
142
+ data = json.loads(resp.read().decode("utf-8"))
143
+ except urllib.error.HTTPError:
144
+ return []
145
+
146
+ items = []
147
+ for wi in data.get("workItems", []):
148
+ status = self.get_status(str(wi["id"]))
149
+ if status:
150
+ items.append(status)
151
+
152
+ return items
153
+
154
+ def close(self, item_id: str, reason: str = "Resolved by scan") -> bool:
155
+ try:
156
+ patch_doc = [
157
+ {"op": "add", "path": "/fields/System.State", "value": "Closed"},
158
+ {"op": "add", "path": "/fields/System.History", "value": reason},
159
+ ]
160
+ self._request("PATCH", f"/wit/workitems/{item_id}", patch_doc)
161
+ return True
162
+ except RuntimeError:
163
+ return False
164
+
165
+ def reopen(self, item_id: str, reason: str = "Regression detected by scan") -> bool:
166
+ try:
167
+ patch_doc = [
168
+ {"op": "add", "path": "/fields/System.State", "value": "New"},
169
+ {"op": "add", "path": "/fields/System.History", "value": reason},
170
+ ]
171
+ self._request("PATCH", f"/wit/workitems/{item_id}", patch_doc)
172
+ return True
173
+ except RuntimeError:
174
+ return False