Keep a project's reasoning alive after its author has forgotten it. Three artefacts, one checker, no dependencies.
A decision log where every entry carries the condition that should reverse it. A risk register where every risk has a signal you could actually notice. A charter that says what the project refuses to do. And a checker that fails the build when any of those quietly stops being true.
pip install git+https://github.com/muhammad1502/engineering-kit
cd your-project
kit init # writes six starter files, overwrites nothing
kit check # fails until you have actually filled them inNot on PyPI yet, so the install is from git. Everything also works with no
install at all: clone the repo and run python -m kit ... from it.
Decisions lose their conditions. A log records what was chosen and stops. A year later nobody can tell whether the choice was load-bearing or arbitrary, so every revisit is argued from scratch. The fix is one required field: what observation should make a future reader reopen this.
Risks lose their signal. A register lists what could go wrong and rates it, but omits what you would notice first. A risk with no signal cannot be watched, only suffered. And a risk owned by "the team" is owned by nobody.
Prose drifts away from measurement. This one is the reason the kit has a checker at all, and the story is in D-005. A real project measured its implementation size, wrote the figure into its README next to a stated budget, then grew the code. The generated report was updated. The README was not, and the stale number was the first thing a reader saw. That project was scrupulous: it pre-registered its benchmarks, ran significance tests, and published results that went against its own thesis. It still shipped a stale headline number, because nothing failed when the prose and the artefact diverged. Care was not sufficient. That is what a pin is for.
Attach a claim in prose to the artefact it came from:
The parser is 658 lines. <!-- kit:verify file=bench/report.md contains="658" -->kit check fails when bench/report.md stops containing 658, and says the
prose has drifted rather than that a test broke. The opposite form asserts
something is gone:
The legacy path is removed. <!-- kit:verify file=src/app.py absent="legacy_handler" -->Pins inside fenced code blocks are ignored, which is why the two examples above are safe to ship. Pin figures a reader would repeat back to you: sizes, ratios, counts, latencies. Do not pin prose that a reasonable edit would change, or the check becomes noise people learn to ignore.
| Rule | Level | Catches |
|---|---|---|
decisions |
error | An entry missing context, options, decision, or reversal condition. Duplicate ids. Gaps warn. |
risks |
error | A row with no signal, no mitigation, or no owner. Likelihood, impact, or status outside the vocabulary. |
charter |
error | A charter with no mission, non-goals, operating rules, or kill criteria. |
pin |
error | A pinned figure whose source no longer contains it. |
placeholder |
error | Scaffolded template text nobody replaced. |
claim |
warn | A superlative with no citation, pin, or code reference on the same line. --strict promotes it. |
Three label styles are accepted for decision fields, so the rules bend to your formatting rather than the reverse:
**Context.** ... bold label
Context: ... plain label with a delimiter
#### Context a sub-headingkit init # scaffold; never overwrites without --force
kit check # validate; exit 1 on any error
kit check --strict # unsupported claims fail too
kit new decision "Short title" # append a numbered stub with all four fields
kit new risk "Short description" # append a row with the vocabulary pre-filledPaths are configurable if your layout differs:
kit --decisions docs/decisions.md --risks docs/risks.md checkEverything also works as python -m kit ..., with no install, straight from a
checkout.
kit init writes .github/workflows/discipline.yml. The whole job is:
- uses: actions/setup-python@v5
with: { python-version: "3.12" }
- run: pip install git+https://github.com/muhammad1502/engineering-kit
- run: kit checkNo services, no cache, no dependency resolution. It runs in seconds.
kit init, then delete any artefact you already have in another form and point the flags at yours instead.- Write the charter's non-goals first. It is the section that pays for itself, because each entry removes a category of future argument.
- Backfill the five decisions you would most hate to re-derive. Not all of them. Five.
- Sweep risks by category rather than by imagination. The register template lists eight, and the useful finding is usually that the lethal risks are not technical.
- Turn on
kit checkin CI. Leave--strictoff until the existing warnings are cleared, then turn it on so new ones fail.
An hour, and step 3 is most of it.
- Structure is not substance. The checker verifies that a reversal condition
exists. It cannot tell whether it says anything. "If it becomes a problem"
passes. This is the ceiling of the approach, it is recorded as
R5 with status
accepted, and no amount of additional rules would fix it. - The claims rule is a heuristic over English. It will flag a fine sentence and miss a careful overclaim. It warns rather than failing for that reason (D-006), and for non-English prose it reports nothing at all (R7).
- Markdown is parsed with regexes, not a parser. That is a deliberate consequence of taking no dependencies (D-003). Unusual documents may be misread; the validators have behavioural tests covering the forms encountered so far.
- Indented code blocks are not skipped, only fenced ones. Missing a real pin is worse than flagging an example, so the ambiguous form is not treated as code (D-008).
- The pre-release checklist is prose, not enforced. It ships with
initand nothing checks that you ran it (R9).
The kit's own charter, decision log, and risk register are the fixtures its CI validates. A decision added here without a reversal condition fails the build, the same as it would in your project.
CI also asserts that a fresh scaffold fails check, and fails specifically
with placeholder findings. A template that passed silently would let a project
adopt the kit, show a green check, and record nothing
(D-004).
Tested on Python 3.9 through 3.13.
No dependencies.
The method is borrowed, with thanks, from SyedSaribSultan/sarib-lang, which practises it at a level worth studying: a charter with kill criteria written before the work, decisions logged with reversal conditions, a scored risk register whose headline finding is that the lethal risks are human, and benchmarks with pre-registered grading that publish their negative results. The stale-number failure that motivated claim pinning was found in that repository, which is the least important thing about it.
MIT. See LICENSE.