antemortem 0.2.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.
- antemortem-0.2.0/.gitignore +75 -0
- antemortem-0.2.0/CHANGELOG.md +35 -0
- antemortem-0.2.0/LICENSE +21 -0
- antemortem-0.2.0/PKG-INFO +158 -0
- antemortem-0.2.0/README.md +107 -0
- antemortem-0.2.0/pyproject.toml +80 -0
- antemortem-0.2.0/src/antemortem/__init__.py +6 -0
- antemortem-0.2.0/src/antemortem/__main__.py +6 -0
- antemortem-0.2.0/src/antemortem/api.py +159 -0
- antemortem-0.2.0/src/antemortem/citations.py +135 -0
- antemortem-0.2.0/src/antemortem/cli.py +44 -0
- antemortem-0.2.0/src/antemortem/commands/__init__.py +1 -0
- antemortem-0.2.0/src/antemortem/commands/init.py +84 -0
- antemortem-0.2.0/src/antemortem/commands/lint.py +160 -0
- antemortem-0.2.0/src/antemortem/commands/run.py +214 -0
- antemortem-0.2.0/src/antemortem/parser.py +179 -0
- antemortem-0.2.0/src/antemortem/prompts.py +156 -0
- antemortem-0.2.0/src/antemortem/schema.py +126 -0
- antemortem-0.2.0/src/antemortem/templates.py +352 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# ── Local prototype workspace (design notes, scratch, not for distribution) ──
|
|
2
|
+
.design/
|
|
3
|
+
.gstack/
|
|
4
|
+
drafts/
|
|
5
|
+
scratch/
|
|
6
|
+
*.draft.md
|
|
7
|
+
|
|
8
|
+
# ── Python ──
|
|
9
|
+
__pycache__/
|
|
10
|
+
*.py[cod]
|
|
11
|
+
*$py.class
|
|
12
|
+
*.so
|
|
13
|
+
.Python
|
|
14
|
+
|
|
15
|
+
# ── Build / dist ──
|
|
16
|
+
build/
|
|
17
|
+
develop-eggs/
|
|
18
|
+
dist/
|
|
19
|
+
eggs/
|
|
20
|
+
.eggs/
|
|
21
|
+
lib/
|
|
22
|
+
lib64/
|
|
23
|
+
parts/
|
|
24
|
+
sdist/
|
|
25
|
+
var/
|
|
26
|
+
wheels/
|
|
27
|
+
share/python-wheels/
|
|
28
|
+
*.egg-info/
|
|
29
|
+
.installed.cfg
|
|
30
|
+
*.egg
|
|
31
|
+
MANIFEST
|
|
32
|
+
|
|
33
|
+
# ── Virtual environments ──
|
|
34
|
+
.venv/
|
|
35
|
+
venv/
|
|
36
|
+
env/
|
|
37
|
+
ENV/
|
|
38
|
+
|
|
39
|
+
# ── Packaging / tooling ──
|
|
40
|
+
.uv/
|
|
41
|
+
pip-log.txt
|
|
42
|
+
pip-delete-this-directory.txt
|
|
43
|
+
|
|
44
|
+
# ── Testing ──
|
|
45
|
+
.pytest_cache/
|
|
46
|
+
.coverage
|
|
47
|
+
.coverage.*
|
|
48
|
+
htmlcov/
|
|
49
|
+
.tox/
|
|
50
|
+
.nox/
|
|
51
|
+
coverage.xml
|
|
52
|
+
*.cover
|
|
53
|
+
.cache
|
|
54
|
+
|
|
55
|
+
# ── Type checking ──
|
|
56
|
+
.mypy_cache/
|
|
57
|
+
.pyre/
|
|
58
|
+
.pytype/
|
|
59
|
+
.ruff_cache/
|
|
60
|
+
|
|
61
|
+
# ── IDE ──
|
|
62
|
+
.idea/
|
|
63
|
+
.vscode/
|
|
64
|
+
*.swp
|
|
65
|
+
*.swo
|
|
66
|
+
|
|
67
|
+
# ── OS ──
|
|
68
|
+
.DS_Store
|
|
69
|
+
Thumbs.db
|
|
70
|
+
|
|
71
|
+
# ── Secrets / local config ──
|
|
72
|
+
.env
|
|
73
|
+
.env.local
|
|
74
|
+
.env.*.local
|
|
75
|
+
*.pem
|
|
@@ -0,0 +1,35 @@
|
|
|
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](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.2.0] - 2026-04-21
|
|
10
|
+
|
|
11
|
+
Initial public release of the Antemortem CLI.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `antemortem init <name>` — scaffold a new antemortem document from the basic template, or `--enhanced` for the enhanced template (calibration dimensions, fine-grained classification, skeptic pass, decision-first output).
|
|
16
|
+
- `antemortem run <doc>` — run LLM-assisted classification on an antemortem document using Claude Opus 4.7. Reads the spec, traps, and referenced files; returns REAL / GHOST / NEW labels with `file:line` citations; writes a JSON audit artifact.
|
|
17
|
+
- `antemortem lint <doc>` — validate an antemortem document's structured schema and verify all `file:line` citations exist in the current repository. Exit 0 on pass, 1 on fail. Suitable for CI.
|
|
18
|
+
- Embedded templates (basic + enhanced) vendored from [hibou04-ops/Antemortem](https://github.com/hibou04-ops/Antemortem) v0.1.1 under MIT.
|
|
19
|
+
- Pydantic v2 schemas for classifications, new traps, and spec mutations — shared end-to-end across `run` and `lint`.
|
|
20
|
+
- Prompt caching on the system prompt for Claude API calls, targeting the Opus 4.7 4096-token minimum to ensure cache hits.
|
|
21
|
+
|
|
22
|
+
### Scope boundary — not in v0.2
|
|
23
|
+
|
|
24
|
+
- Multi-model support (GPT, Gemini, etc.) — intentional, Claude Opus 4.7 only.
|
|
25
|
+
- GitHub Action / CI integration templates — follow in v0.3.
|
|
26
|
+
- HTML report rendering — follow in v0.3.
|
|
27
|
+
- Web dashboard / viewer — out of scope.
|
|
28
|
+
- Database-backed history — out of scope; files only.
|
|
29
|
+
|
|
30
|
+
### Rationale
|
|
31
|
+
|
|
32
|
+
Antemortem as a discipline was released as methodology-only in [Antemortem v0.1 / v0.1.1](https://github.com/hibou04-ops/Antemortem). The CLI operationalizes the protocol: scaffold, run, lint — three commands, one week to a disciplined antemortem doc. v0.2 ships the CLI; the methodology repo stays the source of truth for the protocol itself.
|
|
33
|
+
|
|
34
|
+
[Unreleased]: https://github.com/hibou04-ops/antemortem-cli/compare/v0.2.0...HEAD
|
|
35
|
+
[0.2.0]: https://github.com/hibou04-ops/antemortem-cli/releases/tag/v0.2.0
|
antemortem-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 hibou04-ops
|
|
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,158 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: antemortem
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: CLI for the Antemortem pre-implementation reconnaissance discipline — scaffold, run, and lint antemortem documents with Claude.
|
|
5
|
+
Project-URL: Homepage, https://github.com/hibou04-ops/antemortem-cli
|
|
6
|
+
Project-URL: Repository, https://github.com/hibou04-ops/antemortem-cli
|
|
7
|
+
Project-URL: Methodology, https://github.com/hibou04-ops/Antemortem
|
|
8
|
+
Project-URL: Issues, https://github.com/hibou04-ops/antemortem-cli/issues
|
|
9
|
+
Author: hibou04-ops
|
|
10
|
+
License: MIT License
|
|
11
|
+
|
|
12
|
+
Copyright (c) 2026 hibou04-ops
|
|
13
|
+
|
|
14
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
15
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
16
|
+
in the Software without restriction, including without limitation the rights
|
|
17
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
18
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
19
|
+
furnished to do so, subject to the following conditions:
|
|
20
|
+
|
|
21
|
+
The above copyright notice and this permission notice shall be included in all
|
|
22
|
+
copies or substantial portions of the Software.
|
|
23
|
+
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
25
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
26
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
27
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
28
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
29
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
30
|
+
SOFTWARE.
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Keywords: antemortem,anthropic,claude,code-review,pre-mortem,reconnaissance,risk-classification
|
|
33
|
+
Classifier: Development Status :: 3 - Alpha
|
|
34
|
+
Classifier: Environment :: Console
|
|
35
|
+
Classifier: Intended Audience :: Developers
|
|
36
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
37
|
+
Classifier: Operating System :: OS Independent
|
|
38
|
+
Classifier: Programming Language :: Python :: 3
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
41
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
42
|
+
Requires-Python: >=3.11
|
|
43
|
+
Requires-Dist: anthropic>=0.40.0
|
|
44
|
+
Requires-Dist: pydantic>=2.6.0
|
|
45
|
+
Requires-Dist: python-frontmatter>=1.1.0
|
|
46
|
+
Requires-Dist: typer>=0.12.0
|
|
47
|
+
Provides-Extra: dev
|
|
48
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
|
|
49
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
50
|
+
Description-Content-Type: text/markdown
|
|
51
|
+
|
|
52
|
+
# antemortem-cli
|
|
53
|
+
|
|
54
|
+

|
|
55
|
+

|
|
56
|
+

|
|
57
|
+

|
|
58
|
+
|
|
59
|
+
*CLI for the [Antemortem](https://github.com/hibou04-ops/Antemortem) pre-implementation reconnaissance discipline.*
|
|
60
|
+
|
|
61
|
+
An antemortem is what you do before you build. You put the planned change under stress *on paper*, use an LLM to read the existing code thoroughly, enumerate traps, classify each as REAL / GHOST / NEW with primary-source `file:line` citations, and revise your risk and your spec before writing a single line. This CLI automates the scaffolding, the classification pass, and the schema lint — so the discipline is a single command, not a workflow you have to remember.
|
|
62
|
+
|
|
63
|
+
The methodology lives at [hibou04-ops/Antemortem](https://github.com/hibou04-ops/Antemortem). This repo is the tooling that runs it.
|
|
64
|
+
|
|
65
|
+
## Install
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install antemortem
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Requires Python 3.11+ and an `ANTHROPIC_API_KEY` environment variable for `run`.
|
|
72
|
+
|
|
73
|
+
## Three commands
|
|
74
|
+
|
|
75
|
+
### `antemortem init` — scaffold a document
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
antemortem init my-feature
|
|
79
|
+
# → Created antemortem/my-feature.md (basic template)
|
|
80
|
+
|
|
81
|
+
antemortem init my-migration --enhanced
|
|
82
|
+
# → Created antemortem/my-migration.md (enhanced template)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Copies the official Antemortem template with YAML frontmatter (`name`, `date`, `scope`, `reversibility`, `status: draft`). Output path: `./antemortem/<name>.md`.
|
|
86
|
+
|
|
87
|
+
### `antemortem run` — LLM-assisted classification
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
antemortem run antemortem/my-feature.md --repo ../target-repo
|
|
91
|
+
# Reading 6 files from ../target-repo ...
|
|
92
|
+
# Calling claude-opus-4-7 (cached system prompt, 4.2k input / 1.1k output) ...
|
|
93
|
+
# Classified 7 traps: 3 GHOST, 3 REAL, 1 NEW
|
|
94
|
+
# Citations written with file:line references.
|
|
95
|
+
# Updated: antemortem/my-feature.md
|
|
96
|
+
# Artifact: antemortem/my-feature.json
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Reads the doc, extracts spec + traps + `files_to_read`, loads files from `--repo`, calls Claude Opus 4.7 with prompt caching on the frozen system prompt (~90% cost reduction on repeated runs), writes classifications back with `file:line` citations. Also emits a JSON artifact for `lint` and downstream tooling.
|
|
100
|
+
|
|
101
|
+
### `antemortem lint` — validate the schema and citations
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
antemortem lint antemortem/my-feature.md
|
|
105
|
+
# PASS — 7/7 traps classified, 7/7 citations present, 7/7 citations verify file:line.
|
|
106
|
+
|
|
107
|
+
antemortem lint antemortem/broken.md
|
|
108
|
+
# FAIL:
|
|
109
|
+
# - trap#3: classification missing
|
|
110
|
+
# - trap#5: citation format invalid ("see walk_forward.py" → expected "path:line")
|
|
111
|
+
# - trap#6: cited file antemortem/ghost.py does not exist in --repo
|
|
112
|
+
# exit 1
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Validates structured schema (all sections present, classifications complete) and verifies every `file:line` exists on disk. Exit 0 on pass, 1 on fail. Use in CI to block PRs with missing or fabricated citations.
|
|
116
|
+
|
|
117
|
+
## Model and cost
|
|
118
|
+
|
|
119
|
+
Uses Claude Opus 4.7 via the Anthropic SDK. Typical cost per `run`:
|
|
120
|
+
|
|
121
|
+
| Scenario | Cost |
|
|
122
|
+
|---|---|
|
|
123
|
+
| First run (writes system prompt to cache) | ~$0.18 |
|
|
124
|
+
| Cached run (system prompt + files reused within 5 min) | ~$0.11 |
|
|
125
|
+
| 100 iterations during development | $11–18 |
|
|
126
|
+
|
|
127
|
+
Cache miss indicators are surfaced in the CLI output so silent invalidators (e.g. non-deterministic prompt state) fail loud, not silent.
|
|
128
|
+
|
|
129
|
+
## Why a CLI (and not just "ask Claude to review my plan")
|
|
130
|
+
|
|
131
|
+
The discipline is two guardrails:
|
|
132
|
+
|
|
133
|
+
1. **Enumerate traps before the LLM sees them.** Prevents anchoring on the model's framing. The CLI surfaces this as a required section in the scaffolded document.
|
|
134
|
+
2. **Require `file:line` citations.** Prevents accepting the model's vibes. The CLI enforces this as a Pydantic schema field, and `lint` verifies the citations exist on disk (no hallucinated line numbers).
|
|
135
|
+
|
|
136
|
+
Without these guardrails, you have traded one form of hand-waving for another.
|
|
137
|
+
|
|
138
|
+
## Project status
|
|
139
|
+
|
|
140
|
+
v0.2.0 is the **initial release**. Alpha — API and output formats may change in v0.2.x as prompts iterate on real repos. Semver applies after v1.0.
|
|
141
|
+
|
|
142
|
+
Full changelog: [CHANGELOG.md](CHANGELOG.md).
|
|
143
|
+
|
|
144
|
+
## Relation to other projects
|
|
145
|
+
|
|
146
|
+
- [`Antemortem`](https://github.com/hibou04-ops/Antemortem) — the methodology, templates, and case studies. This CLI implements the `docs/methodology.md` protocol.
|
|
147
|
+
- [`omega-lock`](https://github.com/hibou04-ops/omega-lock) — first shipped case study of the discipline (audit submodule built with a 15-minute antemortem recon). Cited in Antemortem v0.1's case studies.
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
MIT. See [LICENSE](LICENSE).
|
|
152
|
+
|
|
153
|
+
## Citing
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
Antemortem CLI v0.2 — tooling for the Antemortem pre-implementation reconnaissance discipline.
|
|
157
|
+
https://github.com/hibou04-ops/antemortem-cli, 2026.
|
|
158
|
+
```
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# antemortem-cli
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
*CLI for the [Antemortem](https://github.com/hibou04-ops/Antemortem) pre-implementation reconnaissance discipline.*
|
|
9
|
+
|
|
10
|
+
An antemortem is what you do before you build. You put the planned change under stress *on paper*, use an LLM to read the existing code thoroughly, enumerate traps, classify each as REAL / GHOST / NEW with primary-source `file:line` citations, and revise your risk and your spec before writing a single line. This CLI automates the scaffolding, the classification pass, and the schema lint — so the discipline is a single command, not a workflow you have to remember.
|
|
11
|
+
|
|
12
|
+
The methodology lives at [hibou04-ops/Antemortem](https://github.com/hibou04-ops/Antemortem). This repo is the tooling that runs it.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install antemortem
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Requires Python 3.11+ and an `ANTHROPIC_API_KEY` environment variable for `run`.
|
|
21
|
+
|
|
22
|
+
## Three commands
|
|
23
|
+
|
|
24
|
+
### `antemortem init` — scaffold a document
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
antemortem init my-feature
|
|
28
|
+
# → Created antemortem/my-feature.md (basic template)
|
|
29
|
+
|
|
30
|
+
antemortem init my-migration --enhanced
|
|
31
|
+
# → Created antemortem/my-migration.md (enhanced template)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Copies the official Antemortem template with YAML frontmatter (`name`, `date`, `scope`, `reversibility`, `status: draft`). Output path: `./antemortem/<name>.md`.
|
|
35
|
+
|
|
36
|
+
### `antemortem run` — LLM-assisted classification
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
antemortem run antemortem/my-feature.md --repo ../target-repo
|
|
40
|
+
# Reading 6 files from ../target-repo ...
|
|
41
|
+
# Calling claude-opus-4-7 (cached system prompt, 4.2k input / 1.1k output) ...
|
|
42
|
+
# Classified 7 traps: 3 GHOST, 3 REAL, 1 NEW
|
|
43
|
+
# Citations written with file:line references.
|
|
44
|
+
# Updated: antemortem/my-feature.md
|
|
45
|
+
# Artifact: antemortem/my-feature.json
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Reads the doc, extracts spec + traps + `files_to_read`, loads files from `--repo`, calls Claude Opus 4.7 with prompt caching on the frozen system prompt (~90% cost reduction on repeated runs), writes classifications back with `file:line` citations. Also emits a JSON artifact for `lint` and downstream tooling.
|
|
49
|
+
|
|
50
|
+
### `antemortem lint` — validate the schema and citations
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
antemortem lint antemortem/my-feature.md
|
|
54
|
+
# PASS — 7/7 traps classified, 7/7 citations present, 7/7 citations verify file:line.
|
|
55
|
+
|
|
56
|
+
antemortem lint antemortem/broken.md
|
|
57
|
+
# FAIL:
|
|
58
|
+
# - trap#3: classification missing
|
|
59
|
+
# - trap#5: citation format invalid ("see walk_forward.py" → expected "path:line")
|
|
60
|
+
# - trap#6: cited file antemortem/ghost.py does not exist in --repo
|
|
61
|
+
# exit 1
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Validates structured schema (all sections present, classifications complete) and verifies every `file:line` exists on disk. Exit 0 on pass, 1 on fail. Use in CI to block PRs with missing or fabricated citations.
|
|
65
|
+
|
|
66
|
+
## Model and cost
|
|
67
|
+
|
|
68
|
+
Uses Claude Opus 4.7 via the Anthropic SDK. Typical cost per `run`:
|
|
69
|
+
|
|
70
|
+
| Scenario | Cost |
|
|
71
|
+
|---|---|
|
|
72
|
+
| First run (writes system prompt to cache) | ~$0.18 |
|
|
73
|
+
| Cached run (system prompt + files reused within 5 min) | ~$0.11 |
|
|
74
|
+
| 100 iterations during development | $11–18 |
|
|
75
|
+
|
|
76
|
+
Cache miss indicators are surfaced in the CLI output so silent invalidators (e.g. non-deterministic prompt state) fail loud, not silent.
|
|
77
|
+
|
|
78
|
+
## Why a CLI (and not just "ask Claude to review my plan")
|
|
79
|
+
|
|
80
|
+
The discipline is two guardrails:
|
|
81
|
+
|
|
82
|
+
1. **Enumerate traps before the LLM sees them.** Prevents anchoring on the model's framing. The CLI surfaces this as a required section in the scaffolded document.
|
|
83
|
+
2. **Require `file:line` citations.** Prevents accepting the model's vibes. The CLI enforces this as a Pydantic schema field, and `lint` verifies the citations exist on disk (no hallucinated line numbers).
|
|
84
|
+
|
|
85
|
+
Without these guardrails, you have traded one form of hand-waving for another.
|
|
86
|
+
|
|
87
|
+
## Project status
|
|
88
|
+
|
|
89
|
+
v0.2.0 is the **initial release**. Alpha — API and output formats may change in v0.2.x as prompts iterate on real repos. Semver applies after v1.0.
|
|
90
|
+
|
|
91
|
+
Full changelog: [CHANGELOG.md](CHANGELOG.md).
|
|
92
|
+
|
|
93
|
+
## Relation to other projects
|
|
94
|
+
|
|
95
|
+
- [`Antemortem`](https://github.com/hibou04-ops/Antemortem) — the methodology, templates, and case studies. This CLI implements the `docs/methodology.md` protocol.
|
|
96
|
+
- [`omega-lock`](https://github.com/hibou04-ops/omega-lock) — first shipped case study of the discipline (audit submodule built with a 15-minute antemortem recon). Cited in Antemortem v0.1's case studies.
|
|
97
|
+
|
|
98
|
+
## License
|
|
99
|
+
|
|
100
|
+
MIT. See [LICENSE](LICENSE).
|
|
101
|
+
|
|
102
|
+
## Citing
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
Antemortem CLI v0.2 — tooling for the Antemortem pre-implementation reconnaissance discipline.
|
|
106
|
+
https://github.com/hibou04-ops/antemortem-cli, 2026.
|
|
107
|
+
```
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "antemortem"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "CLI for the Antemortem pre-implementation reconnaissance discipline — scaffold, run, and lint antemortem documents with Claude."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = { file = "LICENSE" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "hibou04-ops" },
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"antemortem",
|
|
17
|
+
"reconnaissance",
|
|
18
|
+
"pre-mortem",
|
|
19
|
+
"risk-classification",
|
|
20
|
+
"claude",
|
|
21
|
+
"anthropic",
|
|
22
|
+
"code-review",
|
|
23
|
+
]
|
|
24
|
+
classifiers = [
|
|
25
|
+
"Development Status :: 3 - Alpha",
|
|
26
|
+
"Environment :: Console",
|
|
27
|
+
"Intended Audience :: Developers",
|
|
28
|
+
"License :: OSI Approved :: MIT License",
|
|
29
|
+
"Operating System :: OS Independent",
|
|
30
|
+
"Programming Language :: Python :: 3",
|
|
31
|
+
"Programming Language :: Python :: 3.11",
|
|
32
|
+
"Programming Language :: Python :: 3.12",
|
|
33
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
34
|
+
]
|
|
35
|
+
dependencies = [
|
|
36
|
+
"typer>=0.12.0",
|
|
37
|
+
"pydantic>=2.6.0",
|
|
38
|
+
"anthropic>=0.40.0",
|
|
39
|
+
"python-frontmatter>=1.1.0",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[project.optional-dependencies]
|
|
43
|
+
dev = [
|
|
44
|
+
"pytest>=8.0.0",
|
|
45
|
+
"pytest-cov>=4.1.0",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[project.urls]
|
|
49
|
+
Homepage = "https://github.com/hibou04-ops/antemortem-cli"
|
|
50
|
+
Repository = "https://github.com/hibou04-ops/antemortem-cli"
|
|
51
|
+
Methodology = "https://github.com/hibou04-ops/Antemortem"
|
|
52
|
+
Issues = "https://github.com/hibou04-ops/antemortem-cli/issues"
|
|
53
|
+
|
|
54
|
+
[project.scripts]
|
|
55
|
+
antemortem = "antemortem.cli:app"
|
|
56
|
+
|
|
57
|
+
[tool.hatch.build.targets.wheel]
|
|
58
|
+
packages = ["src/antemortem"]
|
|
59
|
+
|
|
60
|
+
[tool.hatch.build.targets.sdist]
|
|
61
|
+
include = [
|
|
62
|
+
"src/antemortem",
|
|
63
|
+
"README.md",
|
|
64
|
+
"LICENSE",
|
|
65
|
+
"CHANGELOG.md",
|
|
66
|
+
"pyproject.toml",
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
[tool.pytest.ini_options]
|
|
70
|
+
testpaths = ["tests"]
|
|
71
|
+
python_files = ["test_*.py"]
|
|
72
|
+
addopts = "-ra -q"
|
|
73
|
+
|
|
74
|
+
[tool.ruff]
|
|
75
|
+
line-length = 100
|
|
76
|
+
target-version = "py311"
|
|
77
|
+
|
|
78
|
+
[tool.ruff.lint]
|
|
79
|
+
select = ["E", "F", "W", "I", "N", "UP", "B", "C4", "SIM"]
|
|
80
|
+
ignore = ["E501"]
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""Anthropic Claude API wrapper for the classification step.
|
|
2
|
+
|
|
3
|
+
The ``run_classification`` function is the single boundary between the CLI
|
|
4
|
+
and the Anthropic SDK. Everything else in the package is framework-free and
|
|
5
|
+
testable without the network. Tests mock the ``client`` argument.
|
|
6
|
+
|
|
7
|
+
Caching strategy:
|
|
8
|
+
- The system prompt is rendered with ``cache_control={"type": "ephemeral"}``
|
|
9
|
+
on a top-level system text block. On Opus 4.7 this requires the prompt to
|
|
10
|
+
exceed the 4096-token cacheable-prefix minimum; we size ``SYSTEM_PROMPT``
|
|
11
|
+
accordingly and verify via ``usage.cache_read_input_tokens`` on each call.
|
|
12
|
+
- The user payload carries no caching control — it's volatile (different
|
|
13
|
+
traps each run). A second breakpoint on the files block is a v0.2.1
|
|
14
|
+
optimization when we add iterative-run UX.
|
|
15
|
+
|
|
16
|
+
Model and sampling:
|
|
17
|
+
- ``claude-opus-4-7`` is the only supported model in v0.2 (matches Antemortem
|
|
18
|
+
discipline + enforces a known behavioral contract for the prompt).
|
|
19
|
+
- No ``temperature`` / ``top_p`` / ``top_k`` — removed on Opus 4.7.
|
|
20
|
+
- ``thinking={"type": "adaptive"}`` — off by default on 4.7; explicitly
|
|
21
|
+
enabled because classification benefits from multi-file chain tracing.
|
|
22
|
+
- ``output_config={"effort": "high"}`` — minimum recommended for
|
|
23
|
+
intelligence-sensitive work per Anthropic's migration guide.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
from typing import Any, Protocol
|
|
29
|
+
|
|
30
|
+
from antemortem.prompts import SYSTEM_PROMPT
|
|
31
|
+
from antemortem.schema import AntemortemOutput
|
|
32
|
+
|
|
33
|
+
MODEL = "claude-opus-4-7"
|
|
34
|
+
DEFAULT_MAX_TOKENS = 16000
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class _MessagesNamespace(Protocol):
|
|
38
|
+
"""Duck-typed interface for the subset of ``anthropic.messages`` we use.
|
|
39
|
+
|
|
40
|
+
Accepting a Protocol rather than the concrete class keeps the hot path
|
|
41
|
+
testable without importing ``anthropic`` in unit tests.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def parse(self, **kwargs: Any) -> Any: ... # noqa: D401
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class _AnthropicLike(Protocol):
|
|
48
|
+
"""Minimal interface we need from an Anthropic client instance."""
|
|
49
|
+
|
|
50
|
+
messages: _MessagesNamespace
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _build_user_content(
|
|
54
|
+
spec: str,
|
|
55
|
+
traps_table_md: str,
|
|
56
|
+
files: list[tuple[str, str]],
|
|
57
|
+
) -> str:
|
|
58
|
+
"""Render the user-turn payload as the prompt expects."""
|
|
59
|
+
file_blocks: list[str] = []
|
|
60
|
+
for path, content in sorted(files, key=lambda item: item[0]):
|
|
61
|
+
# Normalize path separators so cache keys don't drift on Windows.
|
|
62
|
+
normalized = path.replace("\\", "/")
|
|
63
|
+
file_blocks.append(f'<file path="{normalized}">\n{content}\n</file>')
|
|
64
|
+
files_section = "\n".join(file_blocks)
|
|
65
|
+
return (
|
|
66
|
+
f"<files>\n{files_section}\n</files>\n\n"
|
|
67
|
+
f"<spec>\n{spec.strip()}\n</spec>\n\n"
|
|
68
|
+
f"<traps>\n{traps_table_md.strip()}\n</traps>"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _usage_to_dict(usage: Any) -> dict[str, int]:
|
|
73
|
+
"""Extract token counts from the SDK's usage object into a plain dict."""
|
|
74
|
+
return {
|
|
75
|
+
"input_tokens": getattr(usage, "input_tokens", 0) or 0,
|
|
76
|
+
"output_tokens": getattr(usage, "output_tokens", 0) or 0,
|
|
77
|
+
"cache_creation_input_tokens": getattr(usage, "cache_creation_input_tokens", 0) or 0,
|
|
78
|
+
"cache_read_input_tokens": getattr(usage, "cache_read_input_tokens", 0) or 0,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def run_classification(
|
|
83
|
+
client: _AnthropicLike,
|
|
84
|
+
spec: str,
|
|
85
|
+
traps_table_md: str,
|
|
86
|
+
files: list[tuple[str, str]],
|
|
87
|
+
*,
|
|
88
|
+
max_tokens: int = DEFAULT_MAX_TOKENS,
|
|
89
|
+
) -> tuple[AntemortemOutput, dict[str, int]]:
|
|
90
|
+
"""Call Claude Opus 4.7 to classify traps against provided files.
|
|
91
|
+
|
|
92
|
+
Parameters
|
|
93
|
+
----------
|
|
94
|
+
client:
|
|
95
|
+
An ``anthropic.Anthropic`` instance (or duck-typed equivalent for
|
|
96
|
+
tests).
|
|
97
|
+
spec:
|
|
98
|
+
Text of the change description from the antemortem document.
|
|
99
|
+
traps_table_md:
|
|
100
|
+
The pre-recon Traps table as a markdown string (raw, including
|
|
101
|
+
header row).
|
|
102
|
+
files:
|
|
103
|
+
List of ``(path, content)`` pairs. Sorted internally so cache
|
|
104
|
+
behavior is deterministic regardless of caller ordering.
|
|
105
|
+
max_tokens:
|
|
106
|
+
Upper bound on output tokens. Defaults to 16000 — the ~8k-output
|
|
107
|
+
typical classification response has ample headroom.
|
|
108
|
+
|
|
109
|
+
Returns
|
|
110
|
+
-------
|
|
111
|
+
A tuple ``(output, usage)``:
|
|
112
|
+
|
|
113
|
+
- ``output``: a validated ``AntemortemOutput`` instance.
|
|
114
|
+
- ``usage``: ``{"input_tokens", "output_tokens", "cache_creation_input_tokens", "cache_read_input_tokens"}``.
|
|
115
|
+
"""
|
|
116
|
+
user_content = _build_user_content(spec, traps_table_md, files)
|
|
117
|
+
|
|
118
|
+
response = client.messages.parse(
|
|
119
|
+
model=MODEL,
|
|
120
|
+
max_tokens=max_tokens,
|
|
121
|
+
thinking={"type": "adaptive"},
|
|
122
|
+
output_config={"effort": "high"},
|
|
123
|
+
system=[
|
|
124
|
+
{
|
|
125
|
+
"type": "text",
|
|
126
|
+
"text": SYSTEM_PROMPT,
|
|
127
|
+
"cache_control": {"type": "ephemeral"},
|
|
128
|
+
}
|
|
129
|
+
],
|
|
130
|
+
messages=[{"role": "user", "content": user_content}],
|
|
131
|
+
output_format=AntemortemOutput,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
stop_reason = getattr(response, "stop_reason", None)
|
|
135
|
+
if stop_reason == "refusal":
|
|
136
|
+
text = ""
|
|
137
|
+
for block in getattr(response, "content", []) or []:
|
|
138
|
+
if getattr(block, "type", None) == "text":
|
|
139
|
+
text = getattr(block, "text", "")
|
|
140
|
+
break
|
|
141
|
+
raise RuntimeError(
|
|
142
|
+
"Claude refused the classification request. This usually means the "
|
|
143
|
+
"spec or traps contain content flagged by safety filters. "
|
|
144
|
+
f"Response text: {text!r}"
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
parsed: AntemortemOutput | None = getattr(response, "parsed_output", None)
|
|
148
|
+
if parsed is None:
|
|
149
|
+
raise RuntimeError(
|
|
150
|
+
"SDK returned no parsed_output. This indicates a schema mismatch or "
|
|
151
|
+
"a malformed response. Raw stop_reason: "
|
|
152
|
+
f"{stop_reason!r}"
|
|
153
|
+
)
|
|
154
|
+
if not isinstance(parsed, AntemortemOutput):
|
|
155
|
+
# Some SDK versions may pass through a dict — coerce defensively.
|
|
156
|
+
parsed = AntemortemOutput.model_validate(parsed)
|
|
157
|
+
|
|
158
|
+
usage = _usage_to_dict(getattr(response, "usage", None))
|
|
159
|
+
return parsed, usage
|