constant-docs 0.6.0__tar.gz → 0.7.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.
- {constant_docs-0.6.0 → constant_docs-0.7.0}/PKG-INFO +98 -82
- {constant_docs-0.6.0 → constant_docs-0.7.0}/README.md +97 -81
- {constant_docs-0.6.0 → constant_docs-0.7.0}/pyproject.toml +1 -1
- {constant_docs-0.6.0 → constant_docs-0.7.0}/pyproject.toml.orig +1 -1
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/checks.py +32 -5
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/config.py +7 -0
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/decisions.py +107 -2
- constant_docs-0.7.0/src/constant_docs/guides/quickstart.md +384 -0
- constant_docs-0.7.0/src/constant_docs/guides/readme.md +458 -0
- constant_docs-0.7.0/src/constant_docs/house-style.md +331 -0
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/prompts/architecture.md +22 -12
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/prompts/cli-reference.md +11 -1
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/prompts/config-reference.md +13 -2
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/prompts/errors.md +11 -1
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/prompts/log.md +11 -1
- constant_docs-0.7.0/src/constant_docs/prompts/module.md +66 -0
- constant_docs-0.7.0/src/constant_docs/prompts/spec.md +90 -0
- constant_docs-0.6.0/src/constant_docs/guides/quickstart.md +0 -124
- constant_docs-0.6.0/src/constant_docs/guides/readme.md +0 -198
- constant_docs-0.6.0/src/constant_docs/house-style.md +0 -70
- constant_docs-0.6.0/src/constant_docs/prompts/module.md +0 -39
- constant_docs-0.6.0/src/constant_docs/prompts/spec.md +0 -68
- {constant_docs-0.6.0 → constant_docs-0.7.0}/LICENSE +0 -0
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/__init__.py +0 -0
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/__main__.py +0 -0
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/api.py +0 -0
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/auto.py +0 -0
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/cli.py +0 -0
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/completeness.py +0 -0
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/coverage.py +0 -0
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/document.py +0 -0
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/fingerprint.py +0 -0
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/globs.py +0 -0
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/index.py +0 -0
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/kinds.py +0 -0
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/paths.py +0 -0
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/state.py +0 -0
- {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/vcs.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: constant-docs
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Self-maintaining documentation for agentic codebases
|
|
5
5
|
Keywords: documentation,docs,staleness,drift,ci,codegen,agents,llm
|
|
6
6
|
Author: Ashborn Systems
|
|
@@ -51,14 +51,25 @@ constant_docs:
|
|
|
51
51
|
- src/constant_docs/api.py
|
|
52
52
|
- src/constant_docs/cli.py
|
|
53
53
|
- src/constant_docs/kinds.py
|
|
54
|
-
source_hash: sha256:
|
|
54
|
+
source_hash: sha256:99017f4b3440779659af6e13a51d12da0e37146fd449a934a42751f6f6f8d50b
|
|
55
55
|
hash_method: sha256-over-sorted-path-and-content
|
|
56
56
|
hash_covers: source_files
|
|
57
|
-
timestamp: '2026-
|
|
58
|
-
generator: constant-docs/0.
|
|
57
|
+
timestamp: '2026-09-05T13:18:40Z'
|
|
58
|
+
generator: constant-docs/0.7.0
|
|
59
59
|
generator_spec: https://github.com/ashborn-systems/constant-docs/blob/main/SPEC.md
|
|
60
60
|
-->
|
|
61
61
|
|
|
62
|
+
|
|
63
|
+
# constant-docs
|
|
64
|
+
|
|
65
|
+
Documentation that fails the build when it drifts.
|
|
66
|
+
|
|
67
|
+
A Python command-line tool for codebases written with a coding agent. Each
|
|
68
|
+
document records a hash of the source files it describes, and `constant-docs
|
|
69
|
+
verify` exits 1 the moment the two disagree. The check is a hash comparison, so
|
|
70
|
+
it needs no model client and no network. Your agent writes the documents. The
|
|
71
|
+
tool names the ones that fell behind.
|
|
72
|
+
|
|
62
73
|
## How the loop works
|
|
63
74
|
|
|
64
75
|
Edit a file, and the document describing it is already behind. The loop closes
|
|
@@ -79,12 +90,12 @@ $ constant-docs verify
|
|
|
79
90
|
All modules up to date.
|
|
80
91
|
```
|
|
81
92
|
|
|
82
|
-
Your coding agent writes every word, because it already holds the source and
|
|
83
|
-
|
|
93
|
+
Your coding agent writes every word, because it already holds the source and a
|
|
94
|
+
model connection. constant-docs names the documents that moved and takes the
|
|
84
95
|
new text back.
|
|
85
96
|
|
|
86
|
-
That one command is also the whole CI integration. It exits 1,
|
|
87
|
-
that alters behaviour
|
|
97
|
+
That one command is also the whole CI integration. It exits 1 on drift, so a
|
|
98
|
+
change that alters behaviour and leaves its document behind does not merge:
|
|
88
99
|
|
|
89
100
|
```yaml
|
|
90
101
|
- run: constant-docs verify
|
|
@@ -126,7 +137,7 @@ modules:
|
|
|
126
137
|
|
|
127
138
|
`constant-docs verify` now names what is missing.
|
|
128
139
|
|
|
129
|
-
Every command and flag is in the [CLI reference](docs/CLI.md)
|
|
140
|
+
Every command and flag is in the [CLI reference](docs/CLI.md). Every
|
|
130
141
|
configuration key is in the [configuration reference](docs/CONFIGURATION.md).
|
|
131
142
|
Neither is restated here, because a second copy of the thing that changes most
|
|
132
143
|
is the copy that goes stale.
|
|
@@ -149,30 +160,30 @@ $ git diff --name-only | xargs constant-docs mark
|
|
|
149
160
|
$ constant-docs settle --json
|
|
150
161
|
```
|
|
151
162
|
|
|
152
|
-
`settle` reports what `mark` recorded
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
163
|
+
`settle` reports what `mark` recorded. When nothing was recorded it asks git
|
|
164
|
+
what moved, so a harness that skips the marking step still gets real work back.
|
|
165
|
+
Outside a git checkout it hashes every module instead: slower, and never a
|
|
166
|
+
clean report on no evidence. `plan --json` considers every module whatever the
|
|
167
|
+
marks say. `verify` hashes everything and exits 1 on drift. Keep `verify` as
|
|
168
|
+
the gate.
|
|
158
169
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
170
|
+
Claude Code gets a plugin. Install it and two hooks run those commands for you:
|
|
171
|
+
`PostToolUse` runs `mark`, `Stop` runs `settle --hook`. Edit a file, stop the
|
|
172
|
+
turn, and its document is up to date. Any harness that can run a command on
|
|
173
|
+
write and at turn end does the same job.
|
|
163
174
|
|
|
164
175
|
Regeneration happens at quiescence, never mid-edit, so the generator and the
|
|
165
176
|
coding loop never write the same file at once. The dirty set lives in
|
|
166
|
-
`.constant-docs/dirty.json
|
|
167
|
-
the next run.
|
|
177
|
+
`.constant-docs/dirty.json`, which is gitignored, so a crashed session is
|
|
178
|
+
picked up on the next run.
|
|
168
179
|
|
|
169
180
|
The [harness guide](guides/harness-integration.md) walks through each route.
|
|
170
181
|
|
|
171
182
|
## Kinds
|
|
172
183
|
|
|
173
|
-
A document is not always a module explainer. Its
|
|
174
|
-
|
|
175
|
-
|
|
184
|
+
A document is not always a module explainer. Its kind decides which sections it
|
|
185
|
+
must carry, which prompt its generator is handed, and whether a regeneration
|
|
186
|
+
replaces the body or appends to it.
|
|
176
187
|
|
|
177
188
|
| Kind | For | Mode |
|
|
178
189
|
|---|---|---|
|
|
@@ -202,7 +213,7 @@ to a log, or a single entry to a module document, and the tool refuses it. A
|
|
|
202
213
|
configuration that has never heard of kinds behaves exactly as it did.
|
|
203
214
|
|
|
204
215
|
One house style governs every generated document. It lives in a single file
|
|
205
|
-
that all seven prompts receive, so the rules cannot drift apart.
|
|
216
|
+
that all seven built-in prompts receive, so the rules cannot drift apart.
|
|
206
217
|
|
|
207
218
|
## Finding what is undocumented
|
|
208
219
|
|
|
@@ -213,12 +224,13 @@ constant-docs completeness # kinds of document the contents warrant
|
|
|
213
224
|
```
|
|
214
225
|
|
|
215
226
|
`verify` can only check what has been declared, so a repository could otherwise
|
|
216
|
-
drift to a large fraction undocumented while every check passed. `coverage`
|
|
217
|
-
|
|
218
|
-
|
|
227
|
+
drift to a large fraction undocumented while every check passed. `coverage`
|
|
228
|
+
does one thing: a set difference. Declare the directories that should carry no
|
|
229
|
+
document in the configuration, with a reason each. An exclusion nobody
|
|
230
|
+
justified is one nobody decided.
|
|
219
231
|
|
|
220
232
|
Coverage answers whether a file has a document. It cannot answer whether the
|
|
221
|
-
set of documents is complete
|
|
233
|
+
set of documents is complete: a document that was never written cannot drift,
|
|
222
234
|
and a module document covers its files, so a repository with a document per
|
|
223
235
|
module and no specification passes everything. `completeness` proposes the
|
|
224
236
|
kinds your contents warrant, each with the fact behind it:
|
|
@@ -232,25 +244,26 @@ Warranted and not written (2):
|
|
|
232
244
|
because declares the console script `yourtool` (pyproject.toml)
|
|
233
245
|
```
|
|
234
246
|
|
|
235
|
-
It proposes from
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
247
|
+
It proposes from evidence in the repository, such as a console script or an
|
|
248
|
+
exception hierarchy. The existence of a kind is not itself evidence. A library
|
|
249
|
+
with no command line is not offered a CLI reference, because a document nobody
|
|
250
|
+
needs still has to be kept true and still refuses a commit when it drifts.
|
|
251
|
+
Nothing warrants a changelog: every repository could keep one, so the signal
|
|
252
|
+
fires everywhere and says nothing.
|
|
240
253
|
|
|
241
254
|
Write the document, or decline the kind under `unwarranted:` with a reason and
|
|
242
255
|
it stops being proposed.
|
|
243
256
|
|
|
244
257
|
`verify --coverage` and `verify --completeness` fold the two into the gate,
|
|
245
|
-
both opt-in. `init` proposes
|
|
258
|
+
both opt-in. `init` proposes. It never removes: dropping a module orphans its
|
|
246
259
|
document, and `prune` deletes it.
|
|
247
260
|
|
|
248
261
|
## Files it will not touch
|
|
249
262
|
|
|
250
|
-
|
|
251
|
-
document this tool writes carries a block naming the tool, and
|
|
252
|
-
declare a `project` id
|
|
253
|
-
|
|
263
|
+
Pointing the tool at a `docs/` folder the repository already has is safe. Every
|
|
264
|
+
document this tool writes carries a block naming the tool, and, when you
|
|
265
|
+
declare a `project` id, the project that wrote it. A markdown file without that
|
|
266
|
+
block belongs to somebody else, and three commands act on the difference:
|
|
254
267
|
|
|
255
268
|
- `apply` refuses to write over one. It names the file and changes no byte
|
|
256
269
|
- `prune` refuses to delete one, and names every file it left alone
|
|
@@ -280,10 +293,10 @@ stays where it is. A mistyped glob costs you a failing check and nothing else.
|
|
|
280
293
|
|
|
281
294
|
## Documents outside the repository
|
|
282
295
|
|
|
283
|
-
Set `docs_store` and documents land in a directory the repository does not own
|
|
284
|
-
|
|
296
|
+
Set `docs_store` and documents land in a directory the repository does not own:
|
|
297
|
+
an Obsidian vault, a shared folder, wherever your notes already live. A store
|
|
285
298
|
may be shared, so it requires a `project` id. That id keeps two repositories
|
|
286
|
-
|
|
299
|
+
that write into one vault clear of each other's documents.
|
|
287
300
|
|
|
288
301
|
```yaml
|
|
289
302
|
project: payments-api
|
|
@@ -295,13 +308,13 @@ say `docs/src/payments.md`. The store answers the one question that wants a
|
|
|
295
308
|
real directory on this machine: where to open the file.
|
|
296
309
|
|
|
297
310
|
The variable must resolve when the configuration loads. Unset, empty, relative,
|
|
298
|
-
or naming a directory that is not there
|
|
311
|
+
or naming a directory that is not there, each refuses the configuration and
|
|
299
312
|
says which. A store landing inside `.ssh`, `Secret/`, a cache or any other
|
|
300
313
|
directory on the deny list is refused too. The tool declines to read those
|
|
301
|
-
directories, so it declines to write documents into them. Plain `${NAME}` is
|
|
302
|
-
let a machine without the vault write
|
|
303
|
-
success. The Stop hook blocks on the
|
|
304
|
-
switch the gate off quietly.
|
|
314
|
+
directories, so it declines to write documents into them. Plain `${NAME}` is
|
|
315
|
+
the whole syntax. A default value would let a machine without the vault write
|
|
316
|
+
documents into the repository and report success. The Stop hook blocks on the
|
|
317
|
+
same failure, so a missing variable cannot switch the gate off quietly.
|
|
305
318
|
|
|
306
319
|
Deletion changes as well. A repository's documents ride the branch, and `git
|
|
307
320
|
checkout` brings one back. A store stands still while the checkout moves, so
|
|
@@ -323,27 +336,26 @@ auto:
|
|
|
323
336
|
|
|
324
337
|
The tool still calls no model. constant-docs splits your command with `shlex`
|
|
325
338
|
and runs it directly, so an agent CLI, a script and a Makefile target all work.
|
|
326
|
-
No credential goes in it
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
outcomes, because a scheduler that confuses the last two retries the wrong
|
|
330
|
-
one.
|
|
339
|
+
No credential goes in it. The command's success is checked: `auto` re-runs
|
|
340
|
+
`verify` afterwards. It exits 0 when everything is clean, 1 when something is
|
|
341
|
+
still stale, and 2 when the command could not run or exited non-zero. Three
|
|
342
|
+
outcomes, because a scheduler that confuses the last two retries the wrong one.
|
|
331
343
|
|
|
332
344
|
## Three things it checks that a hash cannot
|
|
333
345
|
|
|
334
346
|
**A regeneration cannot drop a decision.** A document rewritten by a model can
|
|
335
347
|
lose the thing it existed to record. Give each recorded decision an identifier
|
|
336
348
|
and the tool refuses a rewrite that loses one, naming it. A rewrite may reword
|
|
337
|
-
every decision it holds. Losing one costs a refusal
|
|
338
|
-
deliberate and
|
|
349
|
+
every decision it holds. Losing one costs a refusal. Retiring one is
|
|
350
|
+
deliberate: name it with `--retire` and the retirement is recorded in the
|
|
351
|
+
document, with a date.
|
|
339
352
|
|
|
340
353
|
**A cross-cutting document follows what it covers.** `covers: [api, cli]` takes
|
|
341
|
-
in those modules' files by name, so it goes stale when their boundary
|
|
342
|
-
moves.
|
|
354
|
+
in those modules' files by name, so it goes stale when their boundary moves.
|
|
343
355
|
|
|
344
|
-
**An error catalogue agrees with the source, both ways.** Every message the
|
|
345
|
-
raises appears in the catalogue, and every entry still exists in the code.
|
|
346
|
-
Neither needs parsing beyond a string scan, so it costs nothing
|
|
356
|
+
**An error catalogue agrees with the source, both ways.** Every message the
|
|
357
|
+
code raises appears in the catalogue, and every entry still exists in the code.
|
|
358
|
+
Neither needs parsing beyond a string scan, so it costs nothing, and it is the
|
|
347
359
|
one document here the tool can actually guarantee. The rest are only fresh.
|
|
348
360
|
|
|
349
361
|
## What a document looks like
|
|
@@ -354,6 +366,8 @@ type: Code Module
|
|
|
354
366
|
title: Payments
|
|
355
367
|
description: Reconciles inbound payment events against ledger entries.
|
|
356
368
|
tags: [code-module]
|
|
369
|
+
created: '2026-08-19'
|
|
370
|
+
updated: '2026-08-19'
|
|
357
371
|
|
|
358
372
|
constant_docs:
|
|
359
373
|
project: payments-api
|
|
@@ -367,7 +381,7 @@ constant_docs:
|
|
|
367
381
|
hash_method: sha256-over-sorted-path-and-content
|
|
368
382
|
hash_covers: source_files
|
|
369
383
|
timestamp: '2026-08-19T09:14:00Z'
|
|
370
|
-
generator: constant-docs/0.
|
|
384
|
+
generator: constant-docs/0.7.0
|
|
371
385
|
generator_spec: https://github.com/ashborn-systems/constant-docs/blob/main/SPEC.md
|
|
372
386
|
---
|
|
373
387
|
## Purpose
|
|
@@ -378,8 +392,8 @@ and duplicate handling are internal.
|
|
|
378
392
|
|
|
379
393
|
## Correctness pillars
|
|
380
394
|
|
|
381
|
-
|
|
382
|
-
|
|
395
|
+
A batch carrying the same event ID twice has the later one discarded, and the
|
|
396
|
+
report records that it happened.
|
|
383
397
|
|
|
384
398
|
## Known failure modes
|
|
385
399
|
|
|
@@ -393,7 +407,7 @@ what the document describes, where its source lives, and how to recompute the
|
|
|
393
407
|
digest. `project` answers the question asked before every overwrite and every
|
|
394
408
|
deletion: whose document is this. The tool keeps everything it owns under one
|
|
395
409
|
`constant_docs` key, an Open Knowledge Format producer extension. `type`,
|
|
396
|
-
`title
|
|
410
|
+
`title`, `tags` and the two dates stay yours, and unknown keys survive a write.
|
|
397
411
|
|
|
398
412
|
## Python API
|
|
399
413
|
|
|
@@ -413,37 +427,39 @@ prune() # deletes orphans; under a store, lists them
|
|
|
413
427
|
A module names a glob, and the tool digests the bytes of the files that glob
|
|
414
428
|
matches. Nothing in that step knows a language, which is what makes yours work:
|
|
415
429
|
no model client, no network call, no AST, no import graph. The cost is that any
|
|
416
|
-
byte moves the hash
|
|
430
|
+
byte moves the hash. Fixing a typo in a comment reports the module stale, and
|
|
417
431
|
your agent rewrites a document the edit never touched.
|
|
418
432
|
|
|
419
433
|
It watches files and nothing else. A document quoting a queue depth or a
|
|
420
434
|
production URL stays fresh for ever, because nobody edited anything. Hash what
|
|
421
435
|
lives in files. Leave live values to something that owns them.
|
|
422
436
|
|
|
423
|
-
It cannot catch two documents contradicting each other. Content hashing sees
|
|
424
|
-
falling behind its source. That is why
|
|
425
|
-
|
|
437
|
+
It cannot catch two documents contradicting each other. Content hashing sees
|
|
438
|
+
one thing: a document falling behind its source. That is why a cross-cutting
|
|
439
|
+
document takes in the modules it talks about through `covers` and never
|
|
440
|
+
restates their facts.
|
|
426
441
|
|
|
427
|
-
|
|
428
|
-
one line, between present and deleted. Degradation
|
|
429
|
-
|
|
442
|
+
It cannot catch a decision reworded into something weaker. The decision guard
|
|
443
|
+
draws one line, between a decision present and a decision deleted. Degradation
|
|
444
|
+
is left to review.
|
|
430
445
|
|
|
431
|
-
It ships one ready-made integration
|
|
432
|
-
|
|
433
|
-
|
|
446
|
+
It ships one ready-made integration, the Claude Code plugin. For any other
|
|
447
|
+
agent you wire `mark` and `settle` to whatever signals it gives you, which
|
|
448
|
+
takes a few lines of shell.
|
|
434
449
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
kinds or the Python API move.
|
|
450
|
+
This README is not one of the tracked documents. The tool does not track prose
|
|
451
|
+
like this, so it is revised by hand, and nothing regenerates it.
|
|
438
452
|
|
|
439
453
|
## Reading further
|
|
440
454
|
|
|
441
|
-
- [Specification](docs/SPEC.md)
|
|
442
|
-
|
|
443
|
-
- [
|
|
444
|
-
- [
|
|
445
|
-
|
|
455
|
+
- [Specification](docs/SPEC.md): the full design and its 183 acceptance
|
|
456
|
+
criteria
|
|
457
|
+
- [Architecture](docs/ARCHITECTURE.md): what the parts are and how they meet
|
|
458
|
+
- [CLI reference](docs/CLI.md) and [configuration
|
|
459
|
+
reference](docs/CONFIGURATION.md)
|
|
460
|
+
- [Error catalogue](docs/ERRORS.md): every message, its cause, and what to do
|
|
461
|
+
- [examples/](examples/): a worked repository with its documents committed
|
|
446
462
|
|
|
447
463
|
## Licence
|
|
448
464
|
|
|
449
|
-
Apache 2.0
|
|
465
|
+
Apache 2.0. See [LICENSE](LICENSE).
|