frontface-proofit 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.
- frontface_proofit-1.0.0/LICENSE +21 -0
- frontface_proofit-1.0.0/PKG-INFO +163 -0
- frontface_proofit-1.0.0/README.md +147 -0
- frontface_proofit-1.0.0/pyproject.toml +30 -0
- frontface_proofit-1.0.0/setup.cfg +4 -0
- frontface_proofit-1.0.0/src/frontface_proofit.egg-info/PKG-INFO +163 -0
- frontface_proofit-1.0.0/src/frontface_proofit.egg-info/SOURCES.txt +20 -0
- frontface_proofit-1.0.0/src/frontface_proofit.egg-info/dependency_links.txt +1 -0
- frontface_proofit-1.0.0/src/frontface_proofit.egg-info/entry_points.txt +2 -0
- frontface_proofit-1.0.0/src/frontface_proofit.egg-info/top_level.txt +1 -0
- frontface_proofit-1.0.0/src/proofit/__init__.py +16 -0
- frontface_proofit-1.0.0/src/proofit/__main__.py +130 -0
- frontface_proofit-1.0.0/src/proofit/checks.py +490 -0
- frontface_proofit-1.0.0/src/proofit/ledger.py +132 -0
- frontface_proofit-1.0.0/src/proofit/linter.py +21 -0
- frontface_proofit-1.0.0/src/proofit/model.py +104 -0
- frontface_proofit-1.0.0/src/proofit/parser.py +153 -0
- frontface_proofit-1.0.0/tests/test_cli.py +135 -0
- frontface_proofit-1.0.0/tests/test_ledger.py +74 -0
- frontface_proofit-1.0.0/tests/test_linter.py +98 -0
- frontface_proofit-1.0.0/tests/test_parser.py +113 -0
- frontface_proofit-1.0.0/tests/test_rules.py +301 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ryan Wesley Hitchcock
|
|
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,163 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: frontface-proofit
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Fail-closed linter for locked-down runbooks: ten rules of workflow discipline as code
|
|
5
|
+
Author: Ryan Wesley Hitchcock
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ohpersonperson/frontface
|
|
8
|
+
Keywords: linter,runbook,workflow,technical-writing,validation
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# proofit — fail-closed linter for locked-down runbooks
|
|
18
|
+
|
|
19
|
+
A runbook is a step-by-step procedure written so tightly that two different
|
|
20
|
+
people following it do the same thing and get the same result. proofit checks
|
|
21
|
+
a runbook draft against ten rules of workflow discipline — and **rejects the
|
|
22
|
+
draft if any rule is violated**. No warnings, no advisories. Fail-closed:
|
|
23
|
+
exit 1 until it's fixed, exit 0 when it's locked down.
|
|
24
|
+
|
|
25
|
+
It checks the runbook's **shape, not its truth**: it can tell you a step is
|
|
26
|
+
missing its verification block, not whether the verification would actually
|
|
27
|
+
work. A fully-specified unwise workflow passes; a wise but sloppy one doesn't.
|
|
28
|
+
That distinction is the whole product — see the FAQ.
|
|
29
|
+
|
|
30
|
+
No prerequisites. You don't need to know anything about this project's
|
|
31
|
+
history or any framework. The mechanism is the whole product.
|
|
32
|
+
|
|
33
|
+
*(Lineage: this is the front-facing conversion of the `proofit` skill from
|
|
34
|
+
Ryan's skill-suite, MIT. What changed in the conversion is documented in
|
|
35
|
+
CHANGELOG.md.)*
|
|
36
|
+
|
|
37
|
+
## What it does
|
|
38
|
+
|
|
39
|
+
You write the runbook as Markdown in the template shape
|
|
40
|
+
(`templates/runbook-template.md` — or print it with `python -m proofit
|
|
41
|
+
template`):
|
|
42
|
+
|
|
43
|
+
- **Ground Rules & Prerequisites** — baseline, routing specs per service,
|
|
44
|
+
tools/items, expected outcome
|
|
45
|
+
- **Step-by-Step Directives** — numbered steps, each with **Do:** /
|
|
46
|
+
**Look for:** / **Verify:** / **If wrong:** / **Fix:**
|
|
47
|
+
- **Validation & Closure** — success condition plus consistency checkpoints
|
|
48
|
+
|
|
49
|
+
The linter parses that shape and runs ten checks:
|
|
50
|
+
|
|
51
|
+
| # | Rule | How it's checked |
|
|
52
|
+
|---|------|------------------|
|
|
53
|
+
| 1 | Remove every assumption | Heuristic: flags bare unlabeled directives ("Press Continue") and claims of automatic behavior ("is done automatically"). Narrow by design — see below. |
|
|
54
|
+
| 2 | All prerequisites upfront | Structure: the section must exist before step 1 and cover all eight fields (hardware, software, accounts, permissions, files, internet access, free-tier limits, starting state). |
|
|
55
|
+
| 3 | One action per step | Structure: each `Do:` holds exactly one verb-led action; steps numbered 1..N. |
|
|
56
|
+
| 4 | Concrete references | Flags bare UI nouns ("the button") and vague references ("open the file") with no visible label. Qualified nouns ("a confirmation dialog") don't fire. |
|
|
57
|
+
| 5 | Explain technical terms | Convention: where a glossary exists, every entry must match `Term (definition)`. Detecting undefined jargon *without* a glossary is out of scope — stated, not pretended. |
|
|
58
|
+
| 6 | Verification after every milestone | Structure: every step ends with a `Verify:` block in observable language ("displays", "exists", "contains"). |
|
|
59
|
+
| 7 | Immediate recovery | Structure: every step carries `If wrong:` + `Fix:`. |
|
|
60
|
+
| 8 | Never skip state changes | Heuristic: a `Do:` starting with a state-changing verb (open, close, install, …) must describe the resulting state in transition language ("a dialog appears"). Narrow by design. |
|
|
61
|
+
| 9 | Routing decisions upfront | Schema: per named service — when to use, free-tier limits, verbatim prompt, expected output format — plus rate-limit handling and error paths. |
|
|
62
|
+
| 10 | Finish with full validation | Structure: the document ends with Validation & Closure (never a step), with a success condition and a checklist. |
|
|
63
|
+
|
|
64
|
+
## Why it works
|
|
65
|
+
|
|
66
|
+
Most workflow failures are specification failures, not execution failures.
|
|
67
|
+
Someone improvises mid-stream because the routing decision was never written
|
|
68
|
+
down. Someone skips a prerequisite because it was revealed in step 9. Someone
|
|
69
|
+
combines two actions in one step and does them in the wrong order. The ten
|
|
70
|
+
rules attack exactly these failure modes, and the linter makes them
|
|
71
|
+
mechanical: the rules that can be checked as structure *are* checked as
|
|
72
|
+
structure, so compliance isn't a matter of discipline — it's a matter of
|
|
73
|
+
passing the gate.
|
|
74
|
+
|
|
75
|
+
Two rules (1 and 8) are phrase-list heuristics — tripwires, not proof. Under
|
|
76
|
+
fail-closed they still block, so they are deliberately narrow: they fire only
|
|
77
|
+
on clear hits. A foolproof gate that misfires on good drafts isn't foolproof,
|
|
78
|
+
it's a tax. The narrowness is documented in the code and tested.
|
|
79
|
+
|
|
80
|
+
## Quickstart
|
|
81
|
+
|
|
82
|
+
Zero dependencies beyond Python 3.10+.
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
cd proofit
|
|
86
|
+
PYTHONPATH=src python3 -m unittest discover -s tests # 65 offline tests
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Lint a draft:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
PYTHONPATH=src python3 -m proofit lint my-runbook.md
|
|
93
|
+
# violations listed as RULE n [location] ERROR — message + fix
|
|
94
|
+
# exit 1: REJECTED — fix and re-run
|
|
95
|
+
# exit 0: PASS — the runbook is locked down
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Print the blank template:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
PYTHONPATH=src python3 -m proofit template > my-runbook.md
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Or use it as a library:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from proofit import lint_text
|
|
108
|
+
|
|
109
|
+
result = lint_text(open("my-runbook.md").read())
|
|
110
|
+
if not result.passed:
|
|
111
|
+
for v in result.violations:
|
|
112
|
+
print(v.render())
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
See `examples/run_local.py` for the full cycle (broken draft rejected,
|
|
116
|
+
fixed draft passes).
|
|
117
|
+
|
|
118
|
+
## Inputs, outputs
|
|
119
|
+
|
|
120
|
+
- **Input:** a runbook draft in Markdown following the template shape
|
|
121
|
+
(`templates/runbook-template.md`, or print it with `python -m proofit
|
|
122
|
+
template`). Freeform Markdown is out of scope **by design, not by
|
|
123
|
+
accident**: the linter parses the template's sections, so a document that
|
|
124
|
+
isn't in the template shape fails wholesale on missing sections rather
|
|
125
|
+
than producing meaningful per-rule violations. Write it in the template,
|
|
126
|
+
get it checked — anything else isn't a bad runbook to proofit, it's not
|
|
127
|
+
a runbook at all.
|
|
128
|
+
- **Output:** a list of violations (`RULE n [location] ERROR — message`
|
|
129
|
+
plus a `fix:` line), and an exit code: 1 if any violation exists, 0 if
|
|
130
|
+
clean. Fail-closed — there is no warning level.
|
|
131
|
+
|
|
132
|
+
## Worked example
|
|
133
|
+
|
|
134
|
+
`examples/example-1-lint-cycle.md` — the same workflow as a broken draft
|
|
135
|
+
(18 violations, rejected) and a fixed draft (passes). The fixture files
|
|
136
|
+
`examples/broken-runbook.md` and `examples/fixed-runbook.md` are the
|
|
137
|
+
actual inputs; the test suite asserts the broken one yields exactly 18
|
|
138
|
+
violations across all ten rules and the fixed one yields zero.
|
|
139
|
+
|
|
140
|
+
## FAQ
|
|
141
|
+
|
|
142
|
+
**Does it check whether the runbook is *correct*?** No. It checks the
|
|
143
|
+
runbook's shape, not its truth — it can't tell whether a stated rate limit
|
|
144
|
+
is right or a recovery path works. It guarantees the workflow is fully
|
|
145
|
+
specified, not that it is wise.
|
|
146
|
+
|
|
147
|
+
**What if a heuristic fires on a good draft?** Rules 1 and 8 are narrow by
|
|
148
|
+
design, but false positives are possible (documented in `checks.py`). If
|
|
149
|
+
one blocks a draft you believe is correct, the honest move is to reword past
|
|
150
|
+
the tripwire — the rewording is usually clearer anyway — not to fork the
|
|
151
|
+
linter.
|
|
152
|
+
|
|
153
|
+
**Why fail-closed instead of warnings?** Because the skill's promise is
|
|
154
|
+
"unbreakable, zero-assumption directive." A warning you can ignore is the
|
|
155
|
+
opposite of idiot-proof. The gate is the product.
|
|
156
|
+
|
|
157
|
+
**Single-service workflow — do I need the routing section?** No. Rule 9 is
|
|
158
|
+
vacuous when no services are named. Delete the Routing Architecture section
|
|
159
|
+
and the check passes silently.
|
|
160
|
+
|
|
161
|
+
## License
|
|
162
|
+
|
|
163
|
+
MIT. See LICENSE.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# proofit — fail-closed linter for locked-down runbooks
|
|
2
|
+
|
|
3
|
+
A runbook is a step-by-step procedure written so tightly that two different
|
|
4
|
+
people following it do the same thing and get the same result. proofit checks
|
|
5
|
+
a runbook draft against ten rules of workflow discipline — and **rejects the
|
|
6
|
+
draft if any rule is violated**. No warnings, no advisories. Fail-closed:
|
|
7
|
+
exit 1 until it's fixed, exit 0 when it's locked down.
|
|
8
|
+
|
|
9
|
+
It checks the runbook's **shape, not its truth**: it can tell you a step is
|
|
10
|
+
missing its verification block, not whether the verification would actually
|
|
11
|
+
work. A fully-specified unwise workflow passes; a wise but sloppy one doesn't.
|
|
12
|
+
That distinction is the whole product — see the FAQ.
|
|
13
|
+
|
|
14
|
+
No prerequisites. You don't need to know anything about this project's
|
|
15
|
+
history or any framework. The mechanism is the whole product.
|
|
16
|
+
|
|
17
|
+
*(Lineage: this is the front-facing conversion of the `proofit` skill from
|
|
18
|
+
Ryan's skill-suite, MIT. What changed in the conversion is documented in
|
|
19
|
+
CHANGELOG.md.)*
|
|
20
|
+
|
|
21
|
+
## What it does
|
|
22
|
+
|
|
23
|
+
You write the runbook as Markdown in the template shape
|
|
24
|
+
(`templates/runbook-template.md` — or print it with `python -m proofit
|
|
25
|
+
template`):
|
|
26
|
+
|
|
27
|
+
- **Ground Rules & Prerequisites** — baseline, routing specs per service,
|
|
28
|
+
tools/items, expected outcome
|
|
29
|
+
- **Step-by-Step Directives** — numbered steps, each with **Do:** /
|
|
30
|
+
**Look for:** / **Verify:** / **If wrong:** / **Fix:**
|
|
31
|
+
- **Validation & Closure** — success condition plus consistency checkpoints
|
|
32
|
+
|
|
33
|
+
The linter parses that shape and runs ten checks:
|
|
34
|
+
|
|
35
|
+
| # | Rule | How it's checked |
|
|
36
|
+
|---|------|------------------|
|
|
37
|
+
| 1 | Remove every assumption | Heuristic: flags bare unlabeled directives ("Press Continue") and claims of automatic behavior ("is done automatically"). Narrow by design — see below. |
|
|
38
|
+
| 2 | All prerequisites upfront | Structure: the section must exist before step 1 and cover all eight fields (hardware, software, accounts, permissions, files, internet access, free-tier limits, starting state). |
|
|
39
|
+
| 3 | One action per step | Structure: each `Do:` holds exactly one verb-led action; steps numbered 1..N. |
|
|
40
|
+
| 4 | Concrete references | Flags bare UI nouns ("the button") and vague references ("open the file") with no visible label. Qualified nouns ("a confirmation dialog") don't fire. |
|
|
41
|
+
| 5 | Explain technical terms | Convention: where a glossary exists, every entry must match `Term (definition)`. Detecting undefined jargon *without* a glossary is out of scope — stated, not pretended. |
|
|
42
|
+
| 6 | Verification after every milestone | Structure: every step ends with a `Verify:` block in observable language ("displays", "exists", "contains"). |
|
|
43
|
+
| 7 | Immediate recovery | Structure: every step carries `If wrong:` + `Fix:`. |
|
|
44
|
+
| 8 | Never skip state changes | Heuristic: a `Do:` starting with a state-changing verb (open, close, install, …) must describe the resulting state in transition language ("a dialog appears"). Narrow by design. |
|
|
45
|
+
| 9 | Routing decisions upfront | Schema: per named service — when to use, free-tier limits, verbatim prompt, expected output format — plus rate-limit handling and error paths. |
|
|
46
|
+
| 10 | Finish with full validation | Structure: the document ends with Validation & Closure (never a step), with a success condition and a checklist. |
|
|
47
|
+
|
|
48
|
+
## Why it works
|
|
49
|
+
|
|
50
|
+
Most workflow failures are specification failures, not execution failures.
|
|
51
|
+
Someone improvises mid-stream because the routing decision was never written
|
|
52
|
+
down. Someone skips a prerequisite because it was revealed in step 9. Someone
|
|
53
|
+
combines two actions in one step and does them in the wrong order. The ten
|
|
54
|
+
rules attack exactly these failure modes, and the linter makes them
|
|
55
|
+
mechanical: the rules that can be checked as structure *are* checked as
|
|
56
|
+
structure, so compliance isn't a matter of discipline — it's a matter of
|
|
57
|
+
passing the gate.
|
|
58
|
+
|
|
59
|
+
Two rules (1 and 8) are phrase-list heuristics — tripwires, not proof. Under
|
|
60
|
+
fail-closed they still block, so they are deliberately narrow: they fire only
|
|
61
|
+
on clear hits. A foolproof gate that misfires on good drafts isn't foolproof,
|
|
62
|
+
it's a tax. The narrowness is documented in the code and tested.
|
|
63
|
+
|
|
64
|
+
## Quickstart
|
|
65
|
+
|
|
66
|
+
Zero dependencies beyond Python 3.10+.
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
cd proofit
|
|
70
|
+
PYTHONPATH=src python3 -m unittest discover -s tests # 65 offline tests
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Lint a draft:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
PYTHONPATH=src python3 -m proofit lint my-runbook.md
|
|
77
|
+
# violations listed as RULE n [location] ERROR — message + fix
|
|
78
|
+
# exit 1: REJECTED — fix and re-run
|
|
79
|
+
# exit 0: PASS — the runbook is locked down
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Print the blank template:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
PYTHONPATH=src python3 -m proofit template > my-runbook.md
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Or use it as a library:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from proofit import lint_text
|
|
92
|
+
|
|
93
|
+
result = lint_text(open("my-runbook.md").read())
|
|
94
|
+
if not result.passed:
|
|
95
|
+
for v in result.violations:
|
|
96
|
+
print(v.render())
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
See `examples/run_local.py` for the full cycle (broken draft rejected,
|
|
100
|
+
fixed draft passes).
|
|
101
|
+
|
|
102
|
+
## Inputs, outputs
|
|
103
|
+
|
|
104
|
+
- **Input:** a runbook draft in Markdown following the template shape
|
|
105
|
+
(`templates/runbook-template.md`, or print it with `python -m proofit
|
|
106
|
+
template`). Freeform Markdown is out of scope **by design, not by
|
|
107
|
+
accident**: the linter parses the template's sections, so a document that
|
|
108
|
+
isn't in the template shape fails wholesale on missing sections rather
|
|
109
|
+
than producing meaningful per-rule violations. Write it in the template,
|
|
110
|
+
get it checked — anything else isn't a bad runbook to proofit, it's not
|
|
111
|
+
a runbook at all.
|
|
112
|
+
- **Output:** a list of violations (`RULE n [location] ERROR — message`
|
|
113
|
+
plus a `fix:` line), and an exit code: 1 if any violation exists, 0 if
|
|
114
|
+
clean. Fail-closed — there is no warning level.
|
|
115
|
+
|
|
116
|
+
## Worked example
|
|
117
|
+
|
|
118
|
+
`examples/example-1-lint-cycle.md` — the same workflow as a broken draft
|
|
119
|
+
(18 violations, rejected) and a fixed draft (passes). The fixture files
|
|
120
|
+
`examples/broken-runbook.md` and `examples/fixed-runbook.md` are the
|
|
121
|
+
actual inputs; the test suite asserts the broken one yields exactly 18
|
|
122
|
+
violations across all ten rules and the fixed one yields zero.
|
|
123
|
+
|
|
124
|
+
## FAQ
|
|
125
|
+
|
|
126
|
+
**Does it check whether the runbook is *correct*?** No. It checks the
|
|
127
|
+
runbook's shape, not its truth — it can't tell whether a stated rate limit
|
|
128
|
+
is right or a recovery path works. It guarantees the workflow is fully
|
|
129
|
+
specified, not that it is wise.
|
|
130
|
+
|
|
131
|
+
**What if a heuristic fires on a good draft?** Rules 1 and 8 are narrow by
|
|
132
|
+
design, but false positives are possible (documented in `checks.py`). If
|
|
133
|
+
one blocks a draft you believe is correct, the honest move is to reword past
|
|
134
|
+
the tripwire — the rewording is usually clearer anyway — not to fork the
|
|
135
|
+
linter.
|
|
136
|
+
|
|
137
|
+
**Why fail-closed instead of warnings?** Because the skill's promise is
|
|
138
|
+
"unbreakable, zero-assumption directive." A warning you can ignore is the
|
|
139
|
+
opposite of idiot-proof. The gate is the product.
|
|
140
|
+
|
|
141
|
+
**Single-service workflow — do I need the routing section?** No. Rule 9 is
|
|
142
|
+
vacuous when no services are named. Delete the Routing Architecture section
|
|
143
|
+
and the check passes silently.
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
MIT. See LICENSE.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "frontface-proofit"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Fail-closed linter for locked-down runbooks: ten rules of workflow discipline as code"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [{ name = "Ryan Wesley Hitchcock" }]
|
|
13
|
+
keywords = ["linter", "runbook", "workflow", "technical-writing", "validation"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://github.com/ohpersonperson/frontface"
|
|
22
|
+
|
|
23
|
+
[project.scripts]
|
|
24
|
+
proofit = "proofit.__main__:main"
|
|
25
|
+
|
|
26
|
+
[tool.setuptools.packages.find]
|
|
27
|
+
where = ["src"]
|
|
28
|
+
|
|
29
|
+
[tool.pytest.ini_options]
|
|
30
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: frontface-proofit
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Fail-closed linter for locked-down runbooks: ten rules of workflow discipline as code
|
|
5
|
+
Author: Ryan Wesley Hitchcock
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ohpersonperson/frontface
|
|
8
|
+
Keywords: linter,runbook,workflow,technical-writing,validation
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# proofit — fail-closed linter for locked-down runbooks
|
|
18
|
+
|
|
19
|
+
A runbook is a step-by-step procedure written so tightly that two different
|
|
20
|
+
people following it do the same thing and get the same result. proofit checks
|
|
21
|
+
a runbook draft against ten rules of workflow discipline — and **rejects the
|
|
22
|
+
draft if any rule is violated**. No warnings, no advisories. Fail-closed:
|
|
23
|
+
exit 1 until it's fixed, exit 0 when it's locked down.
|
|
24
|
+
|
|
25
|
+
It checks the runbook's **shape, not its truth**: it can tell you a step is
|
|
26
|
+
missing its verification block, not whether the verification would actually
|
|
27
|
+
work. A fully-specified unwise workflow passes; a wise but sloppy one doesn't.
|
|
28
|
+
That distinction is the whole product — see the FAQ.
|
|
29
|
+
|
|
30
|
+
No prerequisites. You don't need to know anything about this project's
|
|
31
|
+
history or any framework. The mechanism is the whole product.
|
|
32
|
+
|
|
33
|
+
*(Lineage: this is the front-facing conversion of the `proofit` skill from
|
|
34
|
+
Ryan's skill-suite, MIT. What changed in the conversion is documented in
|
|
35
|
+
CHANGELOG.md.)*
|
|
36
|
+
|
|
37
|
+
## What it does
|
|
38
|
+
|
|
39
|
+
You write the runbook as Markdown in the template shape
|
|
40
|
+
(`templates/runbook-template.md` — or print it with `python -m proofit
|
|
41
|
+
template`):
|
|
42
|
+
|
|
43
|
+
- **Ground Rules & Prerequisites** — baseline, routing specs per service,
|
|
44
|
+
tools/items, expected outcome
|
|
45
|
+
- **Step-by-Step Directives** — numbered steps, each with **Do:** /
|
|
46
|
+
**Look for:** / **Verify:** / **If wrong:** / **Fix:**
|
|
47
|
+
- **Validation & Closure** — success condition plus consistency checkpoints
|
|
48
|
+
|
|
49
|
+
The linter parses that shape and runs ten checks:
|
|
50
|
+
|
|
51
|
+
| # | Rule | How it's checked |
|
|
52
|
+
|---|------|------------------|
|
|
53
|
+
| 1 | Remove every assumption | Heuristic: flags bare unlabeled directives ("Press Continue") and claims of automatic behavior ("is done automatically"). Narrow by design — see below. |
|
|
54
|
+
| 2 | All prerequisites upfront | Structure: the section must exist before step 1 and cover all eight fields (hardware, software, accounts, permissions, files, internet access, free-tier limits, starting state). |
|
|
55
|
+
| 3 | One action per step | Structure: each `Do:` holds exactly one verb-led action; steps numbered 1..N. |
|
|
56
|
+
| 4 | Concrete references | Flags bare UI nouns ("the button") and vague references ("open the file") with no visible label. Qualified nouns ("a confirmation dialog") don't fire. |
|
|
57
|
+
| 5 | Explain technical terms | Convention: where a glossary exists, every entry must match `Term (definition)`. Detecting undefined jargon *without* a glossary is out of scope — stated, not pretended. |
|
|
58
|
+
| 6 | Verification after every milestone | Structure: every step ends with a `Verify:` block in observable language ("displays", "exists", "contains"). |
|
|
59
|
+
| 7 | Immediate recovery | Structure: every step carries `If wrong:` + `Fix:`. |
|
|
60
|
+
| 8 | Never skip state changes | Heuristic: a `Do:` starting with a state-changing verb (open, close, install, …) must describe the resulting state in transition language ("a dialog appears"). Narrow by design. |
|
|
61
|
+
| 9 | Routing decisions upfront | Schema: per named service — when to use, free-tier limits, verbatim prompt, expected output format — plus rate-limit handling and error paths. |
|
|
62
|
+
| 10 | Finish with full validation | Structure: the document ends with Validation & Closure (never a step), with a success condition and a checklist. |
|
|
63
|
+
|
|
64
|
+
## Why it works
|
|
65
|
+
|
|
66
|
+
Most workflow failures are specification failures, not execution failures.
|
|
67
|
+
Someone improvises mid-stream because the routing decision was never written
|
|
68
|
+
down. Someone skips a prerequisite because it was revealed in step 9. Someone
|
|
69
|
+
combines two actions in one step and does them in the wrong order. The ten
|
|
70
|
+
rules attack exactly these failure modes, and the linter makes them
|
|
71
|
+
mechanical: the rules that can be checked as structure *are* checked as
|
|
72
|
+
structure, so compliance isn't a matter of discipline — it's a matter of
|
|
73
|
+
passing the gate.
|
|
74
|
+
|
|
75
|
+
Two rules (1 and 8) are phrase-list heuristics — tripwires, not proof. Under
|
|
76
|
+
fail-closed they still block, so they are deliberately narrow: they fire only
|
|
77
|
+
on clear hits. A foolproof gate that misfires on good drafts isn't foolproof,
|
|
78
|
+
it's a tax. The narrowness is documented in the code and tested.
|
|
79
|
+
|
|
80
|
+
## Quickstart
|
|
81
|
+
|
|
82
|
+
Zero dependencies beyond Python 3.10+.
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
cd proofit
|
|
86
|
+
PYTHONPATH=src python3 -m unittest discover -s tests # 65 offline tests
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Lint a draft:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
PYTHONPATH=src python3 -m proofit lint my-runbook.md
|
|
93
|
+
# violations listed as RULE n [location] ERROR — message + fix
|
|
94
|
+
# exit 1: REJECTED — fix and re-run
|
|
95
|
+
# exit 0: PASS — the runbook is locked down
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Print the blank template:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
PYTHONPATH=src python3 -m proofit template > my-runbook.md
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Or use it as a library:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from proofit import lint_text
|
|
108
|
+
|
|
109
|
+
result = lint_text(open("my-runbook.md").read())
|
|
110
|
+
if not result.passed:
|
|
111
|
+
for v in result.violations:
|
|
112
|
+
print(v.render())
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
See `examples/run_local.py` for the full cycle (broken draft rejected,
|
|
116
|
+
fixed draft passes).
|
|
117
|
+
|
|
118
|
+
## Inputs, outputs
|
|
119
|
+
|
|
120
|
+
- **Input:** a runbook draft in Markdown following the template shape
|
|
121
|
+
(`templates/runbook-template.md`, or print it with `python -m proofit
|
|
122
|
+
template`). Freeform Markdown is out of scope **by design, not by
|
|
123
|
+
accident**: the linter parses the template's sections, so a document that
|
|
124
|
+
isn't in the template shape fails wholesale on missing sections rather
|
|
125
|
+
than producing meaningful per-rule violations. Write it in the template,
|
|
126
|
+
get it checked — anything else isn't a bad runbook to proofit, it's not
|
|
127
|
+
a runbook at all.
|
|
128
|
+
- **Output:** a list of violations (`RULE n [location] ERROR — message`
|
|
129
|
+
plus a `fix:` line), and an exit code: 1 if any violation exists, 0 if
|
|
130
|
+
clean. Fail-closed — there is no warning level.
|
|
131
|
+
|
|
132
|
+
## Worked example
|
|
133
|
+
|
|
134
|
+
`examples/example-1-lint-cycle.md` — the same workflow as a broken draft
|
|
135
|
+
(18 violations, rejected) and a fixed draft (passes). The fixture files
|
|
136
|
+
`examples/broken-runbook.md` and `examples/fixed-runbook.md` are the
|
|
137
|
+
actual inputs; the test suite asserts the broken one yields exactly 18
|
|
138
|
+
violations across all ten rules and the fixed one yields zero.
|
|
139
|
+
|
|
140
|
+
## FAQ
|
|
141
|
+
|
|
142
|
+
**Does it check whether the runbook is *correct*?** No. It checks the
|
|
143
|
+
runbook's shape, not its truth — it can't tell whether a stated rate limit
|
|
144
|
+
is right or a recovery path works. It guarantees the workflow is fully
|
|
145
|
+
specified, not that it is wise.
|
|
146
|
+
|
|
147
|
+
**What if a heuristic fires on a good draft?** Rules 1 and 8 are narrow by
|
|
148
|
+
design, but false positives are possible (documented in `checks.py`). If
|
|
149
|
+
one blocks a draft you believe is correct, the honest move is to reword past
|
|
150
|
+
the tripwire — the rewording is usually clearer anyway — not to fork the
|
|
151
|
+
linter.
|
|
152
|
+
|
|
153
|
+
**Why fail-closed instead of warnings?** Because the skill's promise is
|
|
154
|
+
"unbreakable, zero-assumption directive." A warning you can ignore is the
|
|
155
|
+
opposite of idiot-proof. The gate is the product.
|
|
156
|
+
|
|
157
|
+
**Single-service workflow — do I need the routing section?** No. Rule 9 is
|
|
158
|
+
vacuous when no services are named. Delete the Routing Architecture section
|
|
159
|
+
and the check passes silently.
|
|
160
|
+
|
|
161
|
+
## License
|
|
162
|
+
|
|
163
|
+
MIT. See LICENSE.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/frontface_proofit.egg-info/PKG-INFO
|
|
5
|
+
src/frontface_proofit.egg-info/SOURCES.txt
|
|
6
|
+
src/frontface_proofit.egg-info/dependency_links.txt
|
|
7
|
+
src/frontface_proofit.egg-info/entry_points.txt
|
|
8
|
+
src/frontface_proofit.egg-info/top_level.txt
|
|
9
|
+
src/proofit/__init__.py
|
|
10
|
+
src/proofit/__main__.py
|
|
11
|
+
src/proofit/checks.py
|
|
12
|
+
src/proofit/ledger.py
|
|
13
|
+
src/proofit/linter.py
|
|
14
|
+
src/proofit/model.py
|
|
15
|
+
src/proofit/parser.py
|
|
16
|
+
tests/test_cli.py
|
|
17
|
+
tests/test_ledger.py
|
|
18
|
+
tests/test_linter.py
|
|
19
|
+
tests/test_parser.py
|
|
20
|
+
tests/test_rules.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
proofit
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""proofit — fail-closed linter for locked-down runbooks."""
|
|
2
|
+
from .linter import lint_file, lint_text
|
|
3
|
+
from .model import LintResult, Runbook, Step, Violation
|
|
4
|
+
from .parser import parse
|
|
5
|
+
|
|
6
|
+
__version__ = "1.0.0"
|
|
7
|
+
__all__ = [
|
|
8
|
+
"lint_file",
|
|
9
|
+
"lint_text",
|
|
10
|
+
"parse",
|
|
11
|
+
"LintResult",
|
|
12
|
+
"Runbook",
|
|
13
|
+
"Step",
|
|
14
|
+
"Violation",
|
|
15
|
+
"__version__",
|
|
16
|
+
]
|