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.
Files changed (38) hide show
  1. {constant_docs-0.6.0 → constant_docs-0.7.0}/PKG-INFO +98 -82
  2. {constant_docs-0.6.0 → constant_docs-0.7.0}/README.md +97 -81
  3. {constant_docs-0.6.0 → constant_docs-0.7.0}/pyproject.toml +1 -1
  4. {constant_docs-0.6.0 → constant_docs-0.7.0}/pyproject.toml.orig +1 -1
  5. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/checks.py +32 -5
  6. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/config.py +7 -0
  7. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/decisions.py +107 -2
  8. constant_docs-0.7.0/src/constant_docs/guides/quickstart.md +384 -0
  9. constant_docs-0.7.0/src/constant_docs/guides/readme.md +458 -0
  10. constant_docs-0.7.0/src/constant_docs/house-style.md +331 -0
  11. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/prompts/architecture.md +22 -12
  12. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/prompts/cli-reference.md +11 -1
  13. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/prompts/config-reference.md +13 -2
  14. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/prompts/errors.md +11 -1
  15. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/prompts/log.md +11 -1
  16. constant_docs-0.7.0/src/constant_docs/prompts/module.md +66 -0
  17. constant_docs-0.7.0/src/constant_docs/prompts/spec.md +90 -0
  18. constant_docs-0.6.0/src/constant_docs/guides/quickstart.md +0 -124
  19. constant_docs-0.6.0/src/constant_docs/guides/readme.md +0 -198
  20. constant_docs-0.6.0/src/constant_docs/house-style.md +0 -70
  21. constant_docs-0.6.0/src/constant_docs/prompts/module.md +0 -39
  22. constant_docs-0.6.0/src/constant_docs/prompts/spec.md +0 -68
  23. {constant_docs-0.6.0 → constant_docs-0.7.0}/LICENSE +0 -0
  24. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/__init__.py +0 -0
  25. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/__main__.py +0 -0
  26. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/api.py +0 -0
  27. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/auto.py +0 -0
  28. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/cli.py +0 -0
  29. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/completeness.py +0 -0
  30. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/coverage.py +0 -0
  31. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/document.py +0 -0
  32. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/fingerprint.py +0 -0
  33. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/globs.py +0 -0
  34. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/index.py +0 -0
  35. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/kinds.py +0 -0
  36. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/paths.py +0 -0
  37. {constant_docs-0.6.0 → constant_docs-0.7.0}/src/constant_docs/state.py +0 -0
  38. {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.6.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:eb69c561d4938e68d3e01c5d7fe41d051f8c2883b1097137d80b09f2cb0e692a
54
+ source_hash: sha256:99017f4b3440779659af6e13a51d12da0e37146fd449a934a42751f6f6f8d50b
55
55
  hash_method: sha256-over-sorted-path-and-content
56
56
  hash_covers: source_files
57
- timestamp: '2026-08-31T11:33:14Z'
58
- generator: constant-docs/0.6.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
- a model connection. constant-docs names the documents that moved and takes the
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, and a change
87
- that alters behaviour while leaving its documentation behind does not merge:
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); every
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 — and when nothing was recorded, it asks
153
- git what moved rather than reporting an empty list, so a harness that skips the
154
- marking step still gets real work back. Outside a git checkout it hashes every
155
- module instead: slower, and never a clean report on no evidence. `plan --json`
156
- considers every module regardless, and `verify` hashes everything and exits 1
157
- on drift whatever the marks say. Keep `verify` as the gate.
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
- **Claude Code gets a plugin.** Install it and two hooks run
160
- those commands for you: `PostToolUse` runs `mark`, `Stop` runs `settle --hook`.
161
- Edit a file, stop the turn, and its document is up to date. Nobody asked. Any
162
- harness that can run a command on write and at turn end does the same job.
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` — gitignored — so a crashed session is picked up on
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 **kind** decides which
174
- sections it must carry, which prompt its generator is handed, and whether a
175
- regeneration replaces the body or appends to it.
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` does one thing: a set difference. Declare the directories that
217
- should carry no document in configuration, **with a reason each**. An
218
- exclusion nobody justified is one nobody decided.
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 — a document that was never written cannot drift,
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 what is in the repository, never from the list of kinds a
236
- library with no command line is not offered a CLI reference, because a document
237
- nobody needs still has to be kept true and still refuses a commit when it
238
- drifts. Nothing warrants a changelog: every repository could keep one, so the
239
- signal fires everywhere and says nothing.
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 and never removes: dropping a module orphans its
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
- **Already have a `docs/` folder? Pointing the tool at it is safe.** Every
251
- document this tool writes carries a block naming the tool, and — when you
252
- declare a `project` id — the project that wrote it. A markdown file without
253
- that block belongs to somebody else, and three commands act on the difference:
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
- — an Obsidian vault, a shared folder, wherever your notes already live. A store
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
- writing into one vault clear of each other's documents.
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 — each refuses the configuration and
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 the whole syntax, because a default value would
302
- let a machine without the vault write documents into the repository and report
303
- success. The Stop hook blocks on the same failure, so a missing variable cannot
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, and **the command's success is checked**: `auto`
327
- re-runs `verify` afterwards. It exits 0 when everything is clean, 1 when
328
- something is still stale, and 2 when the command could not run. Three
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, and retiring one is
338
- deliberate and gets written into the document.
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 code
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 — and it is the
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.6.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
- WHEN a batch contains a duplicate event ID, the module SHALL discard the later
382
- event and record it in the report.
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` and `tags` stay yours, and unknown keys survive a write.
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, so fixing a typo in a comment reports the module stale and
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 one thing: a document
424
- falling behind its source. That is why the tool always moves a section and
425
- never copies it.
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
- And it cannot catch a decision reworded into something weaker. The guard draws
428
- one line, between present and deleted. Degradation is left to review, and
429
- said so here so that you learn it now.
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. The Claude Code plugin installs and runs.
432
- For any other agent you wire `mark` and `settle` to whatever signals it gives
433
- you, which takes a few lines of shell.
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
- **This file maintains itself.** It carries the tool's own block in an HTML
436
- comment, which GitHub renders as nothing. It goes stale whenever the CLI, the
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) — the full design and its 183 acceptance criteria
442
- - [Architecture](docs/ARCHITECTURE.md) — what the parts are and how they meet
443
- - [CLI reference](docs/CLI.md) and [configuration reference](docs/CONFIGURATION.md)
444
- - [Error catalogue](docs/ERRORS.md) — every message, its cause, and what to do
445
- - [examples/](examples/) — a worked repository with its documents committed
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 — see [LICENSE](LICENSE).
465
+ Apache 2.0. See [LICENSE](LICENSE).