constant-docs 0.7.0__tar.gz → 0.9.1__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.7.0 → constant_docs-0.9.1}/PKG-INFO +103 -66
- {constant_docs-0.7.0 → constant_docs-0.9.1}/README.md +95 -58
- {constant_docs-0.7.0 → constant_docs-0.9.1}/pyproject.toml +8 -8
- {constant_docs-0.7.0 → constant_docs-0.9.1}/pyproject.toml.orig +8 -8
- {constant_docs-0.7.0 → constant_docs-0.9.1}/src/constant_docs/api.py +161 -24
- {constant_docs-0.7.0 → constant_docs-0.9.1}/src/constant_docs/auto.py +51 -5
- constant_docs-0.9.1/src/constant_docs/budget.py +58 -0
- {constant_docs-0.7.0 → constant_docs-0.9.1}/src/constant_docs/cli.py +96 -16
- {constant_docs-0.7.0 → constant_docs-0.9.1}/src/constant_docs/config.py +218 -8
- constant_docs-0.9.1/src/constant_docs/guides/quickstart.md +178 -0
- constant_docs-0.9.1/src/constant_docs/guides/readme.md +184 -0
- constant_docs-0.9.1/src/constant_docs/house-style.md +143 -0
- {constant_docs-0.7.0 → constant_docs-0.9.1}/src/constant_docs/index.py +146 -33
- {constant_docs-0.7.0 → constant_docs-0.9.1}/src/constant_docs/kinds.py +30 -1
- {constant_docs-0.7.0 → constant_docs-0.9.1}/src/constant_docs/paths.py +20 -4
- constant_docs-0.9.1/src/constant_docs/prompts/architecture.md +36 -0
- constant_docs-0.9.1/src/constant_docs/prompts/cli-reference.md +27 -0
- constant_docs-0.9.1/src/constant_docs/prompts/config-reference.md +26 -0
- constant_docs-0.9.1/src/constant_docs/prompts/errors.md +34 -0
- constant_docs-0.9.1/src/constant_docs/prompts/log.md +11 -0
- constant_docs-0.9.1/src/constant_docs/prompts/manual-page.md +35 -0
- constant_docs-0.9.1/src/constant_docs/prompts/module.md +30 -0
- constant_docs-0.9.1/src/constant_docs/prompts/spec.md +46 -0
- constant_docs-0.9.1/src/constant_docs/writing-core.md +37 -0
- constant_docs-0.7.0/src/constant_docs/guides/quickstart.md +0 -384
- constant_docs-0.7.0/src/constant_docs/guides/readme.md +0 -458
- constant_docs-0.7.0/src/constant_docs/house-style.md +0 -331
- constant_docs-0.7.0/src/constant_docs/prompts/architecture.md +0 -71
- constant_docs-0.7.0/src/constant_docs/prompts/cli-reference.md +0 -50
- constant_docs-0.7.0/src/constant_docs/prompts/config-reference.md +0 -49
- constant_docs-0.7.0/src/constant_docs/prompts/errors.md +0 -60
- constant_docs-0.7.0/src/constant_docs/prompts/log.md +0 -32
- constant_docs-0.7.0/src/constant_docs/prompts/module.md +0 -66
- constant_docs-0.7.0/src/constant_docs/prompts/spec.md +0 -90
- {constant_docs-0.7.0 → constant_docs-0.9.1}/LICENSE +0 -0
- {constant_docs-0.7.0 → constant_docs-0.9.1}/src/constant_docs/__init__.py +0 -0
- {constant_docs-0.7.0 → constant_docs-0.9.1}/src/constant_docs/__main__.py +0 -0
- {constant_docs-0.7.0 → constant_docs-0.9.1}/src/constant_docs/checks.py +0 -0
- {constant_docs-0.7.0 → constant_docs-0.9.1}/src/constant_docs/completeness.py +0 -0
- {constant_docs-0.7.0 → constant_docs-0.9.1}/src/constant_docs/coverage.py +0 -0
- {constant_docs-0.7.0 → constant_docs-0.9.1}/src/constant_docs/decisions.py +0 -0
- {constant_docs-0.7.0 → constant_docs-0.9.1}/src/constant_docs/document.py +0 -0
- {constant_docs-0.7.0 → constant_docs-0.9.1}/src/constant_docs/fingerprint.py +0 -0
- {constant_docs-0.7.0 → constant_docs-0.9.1}/src/constant_docs/globs.py +0 -0
- {constant_docs-0.7.0 → constant_docs-0.9.1}/src/constant_docs/state.py +0 -0
- {constant_docs-0.7.0 → constant_docs-0.9.1}/src/constant_docs/vcs.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: constant-docs
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.1
|
|
4
4
|
Summary: Self-maintaining documentation for agentic codebases
|
|
5
5
|
Keywords: documentation,docs,staleness,drift,ci,codegen,agents,llm
|
|
6
|
-
Author:
|
|
6
|
+
Author: Daemon Agent
|
|
7
7
|
License-Expression: Apache-2.0
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Classifier: Development Status :: 3 - Alpha
|
|
@@ -19,12 +19,12 @@ Classifier: Operating System :: OS Independent
|
|
|
19
19
|
Classifier: Typing :: Typed
|
|
20
20
|
Requires-Dist: pyyaml>=6.0.3
|
|
21
21
|
Requires-Python: >=3.11
|
|
22
|
-
Project-URL: Homepage, https://github.com/
|
|
23
|
-
Project-URL: Repository, https://github.com/
|
|
24
|
-
Project-URL: Documentation, https://github.com/
|
|
25
|
-
Project-URL: Changelog, https://github.com/
|
|
26
|
-
Project-URL: Issues, https://github.com/
|
|
27
|
-
Project-URL: Specification, https://github.com/
|
|
22
|
+
Project-URL: Homepage, https://github.com/daemon-systems/constant-docs
|
|
23
|
+
Project-URL: Repository, https://github.com/daemon-systems/constant-docs
|
|
24
|
+
Project-URL: Documentation, https://github.com/daemon-systems/constant-docs/blob/main/README.md
|
|
25
|
+
Project-URL: Changelog, https://github.com/daemon-systems/constant-docs/blob/main/docs/CHANGELOG.md
|
|
26
|
+
Project-URL: Issues, https://github.com/daemon-systems/constant-docs/issues
|
|
27
|
+
Project-URL: Specification, https://github.com/daemon-systems/constant-docs/blob/main/docs/SPEC.md
|
|
28
28
|
Description-Content-Type: text/markdown
|
|
29
29
|
|
|
30
30
|
<!-- constant-docs
|
|
@@ -51,20 +51,29 @@ 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:aeb0c87c1ea90b23e657543f1eb6c39707733d7def1a6f05096158295d76cec8
|
|
55
55
|
hash_method: sha256-over-sorted-path-and-content
|
|
56
56
|
hash_covers: source_files
|
|
57
|
-
timestamp: '2026-09-
|
|
58
|
-
generator: constant-docs/0.
|
|
59
|
-
generator_spec: https://github.com/
|
|
57
|
+
timestamp: '2026-09-19T13:07:50Z'
|
|
58
|
+
generator: constant-docs/0.9.1
|
|
59
|
+
generator_spec: https://github.com/daemon-systems/constant-docs/blob/main/SPEC.md
|
|
60
60
|
-->
|
|
61
61
|
|
|
62
62
|
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
63
71
|
# constant-docs
|
|
64
72
|
|
|
65
73
|
Documentation that fails the build when it drifts.
|
|
66
74
|
|
|
67
|
-
A Python command-line tool
|
|
75
|
+
A Python command-line tool and coding-agent plugin for maintaining source
|
|
76
|
+
documents. Each
|
|
68
77
|
document records a hash of the source files it describes, and `constant-docs
|
|
69
78
|
verify` exits 1 the moment the two disagree. The check is a hash comparison, so
|
|
70
79
|
it needs no model client and no network. Your agent writes the documents. The
|
|
@@ -94,8 +103,9 @@ Your coding agent writes every word, because it already holds the source and a
|
|
|
94
103
|
model connection. constant-docs names the documents that moved and takes the
|
|
95
104
|
new text back.
|
|
96
105
|
|
|
97
|
-
That one command is also the whole
|
|
98
|
-
change that alters behaviour and leaves its document behind does
|
|
106
|
+
That one command is also the whole continuous-integration check. It exits 1 on
|
|
107
|
+
drift, so a change that alters behaviour and leaves its document behind does
|
|
108
|
+
not merge:
|
|
99
109
|
|
|
100
110
|
```yaml
|
|
101
111
|
- run: constant-docs verify
|
|
@@ -167,13 +177,15 @@ clean report on no evidence. `plan --json` considers every module whatever the
|
|
|
167
177
|
marks say. `verify` hashes everything and exits 1 on drift. Keep `verify` as
|
|
168
178
|
the gate.
|
|
169
179
|
|
|
170
|
-
Claude Code
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
180
|
+
Codex and Claude Code can load the plugin. Its two document hooks run these
|
|
181
|
+
commands:
|
|
182
|
+
`PostToolUse` runs `mark` after each edit, and `Stop` runs `settle --hook` when
|
|
183
|
+
the turn ends. The agent reads the returned plan and updates the affected documents before
|
|
184
|
+
verification. Any
|
|
185
|
+
harness that can run a command on write and at turn end does the same job.
|
|
174
186
|
|
|
175
|
-
Regeneration happens at
|
|
176
|
-
coding loop never write the same file at once. The dirty set lives in
|
|
187
|
+
Regeneration happens at the end of a turn, never mid-edit, so the generator and
|
|
188
|
+
the coding loop never write the same file at once. The dirty set lives in
|
|
177
189
|
`.constant-docs/dirty.json`, which is gitignored, so a crashed session is
|
|
178
190
|
picked up on the next run.
|
|
179
191
|
|
|
@@ -181,9 +193,9 @@ The [harness guide](guides/harness-integration.md) walks through each route.
|
|
|
181
193
|
|
|
182
194
|
## Kinds
|
|
183
195
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
196
|
+
Every document has a kind. The kind decides which sections its body must carry,
|
|
197
|
+
which prompt its generator is handed, and whether a regeneration replaces the
|
|
198
|
+
body or appends an entry.
|
|
187
199
|
|
|
188
200
|
| Kind | For | Mode |
|
|
189
201
|
|---|---|---|
|
|
@@ -191,9 +203,10 @@ replaces the body or appends to it.
|
|
|
191
203
|
| `spec` | What a subsystem promises, and why | replace |
|
|
192
204
|
| `log` | A changelog or build log | **append** |
|
|
193
205
|
| `architecture` | The module map, with a diagram | replace |
|
|
194
|
-
| `errors` | Every message the
|
|
206
|
+
| `errors` | Every message the covered code can raise | replace |
|
|
195
207
|
| `config-reference` | Every configuration key | replace |
|
|
196
208
|
| `cli-reference` | Every command and flag | replace |
|
|
209
|
+
| `manual-page` | One user task, written for the person doing it | replace |
|
|
197
210
|
|
|
198
211
|
```yaml
|
|
199
212
|
modules:
|
|
@@ -212,8 +225,25 @@ An append never rewrites or reorders what is already there. Pass a whole body
|
|
|
212
225
|
to a log, or a single entry to a module document, and the tool refuses it. A
|
|
213
226
|
configuration that has never heard of kinds behaves exactly as it did.
|
|
214
227
|
|
|
215
|
-
One
|
|
216
|
-
that all
|
|
228
|
+
One shared writing register governs documents written from built-in prompts. It
|
|
229
|
+
lives in a single file that all eight built-in prompts receive, and is checked
|
|
230
|
+
against the canonical technical-writing skill. The smaller shared core can also
|
|
231
|
+
be consumed by other agents through a pinned import.
|
|
232
|
+
The [writing-register guide](guides/writing-register.md) describes the exports.
|
|
233
|
+
|
|
234
|
+
## User manuals
|
|
235
|
+
|
|
236
|
+
A user manual is an ordered collection of small task pages. Each page is a
|
|
237
|
+
normal tracked document, so a product change makes only the affected pages
|
|
238
|
+
stale. Constant-Docs assembles the navigation without a model call.
|
|
239
|
+
|
|
240
|
+
Manuals declare input and output token ceilings. A page already above its input
|
|
241
|
+
ceiling appears under `blocked` and is never handed to the supplied generation
|
|
242
|
+
workflow. Split that page by user task, narrow its source, or change the budget
|
|
243
|
+
deliberately.
|
|
244
|
+
|
|
245
|
+
This repository uses the feature itself. Start with the [Constant-Docs user
|
|
246
|
+
manual](docs/user-manual/index.md).
|
|
217
247
|
|
|
218
248
|
## Finding what is undocumented
|
|
219
249
|
|
|
@@ -223,17 +253,18 @@ constant-docs coverage # source no module covers, by directory
|
|
|
223
253
|
constant-docs completeness # kinds of document the contents warrant
|
|
224
254
|
```
|
|
225
255
|
|
|
226
|
-
`verify` can only check what has been declared, so a repository
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
justified is one nobody
|
|
256
|
+
`verify` can only check what has been declared, so a repository can drift a
|
|
257
|
+
long way undocumented while every check passes. `coverage` does one thing: a
|
|
258
|
+
set difference. It reports the files no declared glob matches and no exclusion
|
|
259
|
+
names. Declare the directories that should carry no document in the
|
|
260
|
+
configuration, with a reason each. An exclusion nobody justified is one nobody
|
|
261
|
+
decided.
|
|
231
262
|
|
|
232
263
|
Coverage answers whether a file has a document. It cannot answer whether the
|
|
233
|
-
set of documents is complete
|
|
234
|
-
and a
|
|
235
|
-
|
|
236
|
-
|
|
264
|
+
set of documents is complete. A document that was never written cannot drift,
|
|
265
|
+
and a repository with a document per module and no specification passes every
|
|
266
|
+
coverage check. `completeness` proposes the kinds your contents warrant, each
|
|
267
|
+
with the fact behind it:
|
|
237
268
|
|
|
238
269
|
```
|
|
239
270
|
Warranted and not written (2):
|
|
@@ -247,11 +278,11 @@ Warranted and not written (2):
|
|
|
247
278
|
It proposes from evidence in the repository, such as a console script or an
|
|
248
279
|
exception hierarchy. The existence of a kind is not itself evidence. A library
|
|
249
280
|
with no command line is not offered a CLI reference, because a document nobody
|
|
250
|
-
needs still has to be kept true and still
|
|
281
|
+
needs still has to be kept true and still fails the build when it drifts.
|
|
251
282
|
Nothing warrants a changelog: every repository could keep one, so the signal
|
|
252
283
|
fires everywhere and says nothing.
|
|
253
284
|
|
|
254
|
-
Write the document, or decline the kind under `unwarranted:` with a reason and
|
|
285
|
+
Write the document, or decline the kind under `unwarranted:` with a reason, and
|
|
255
286
|
it stops being proposed.
|
|
256
287
|
|
|
257
288
|
`verify --coverage` and `verify --completeness` fold the two into the gate,
|
|
@@ -261,13 +292,14 @@ document, and `prune` deletes it.
|
|
|
261
292
|
## Files it will not touch
|
|
262
293
|
|
|
263
294
|
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
|
|
265
|
-
|
|
266
|
-
|
|
295
|
+
document this tool writes carries a block naming the tool and, when you declare
|
|
296
|
+
a `project` id, the project that wrote it. A markdown file without that block
|
|
297
|
+
belongs to somebody else, and three commands act on the difference:
|
|
267
298
|
|
|
268
299
|
- `apply` refuses to write over one. It names the file and changes no byte
|
|
269
300
|
- `prune` refuses to delete one, and names every file it left alone
|
|
270
|
-
- `verify` lists them under a heading of their own, and
|
|
301
|
+
- `verify` lists them under a heading of their own, and none of them fails the
|
|
302
|
+
check
|
|
271
303
|
|
|
272
304
|
Take one over when you want it maintained:
|
|
273
305
|
|
|
@@ -288,7 +320,7 @@ rewrite it. Every body your agent generates still carries those headings, and
|
|
|
288
320
|
`apply` checks before it writes a byte.
|
|
289
321
|
|
|
290
322
|
A glob that matches no file gets the same restraint. `verify` names the module
|
|
291
|
-
and exits 1, `apply` refuses to record a
|
|
323
|
+
and exits 1, `apply` refuses to record a hash over nothing, and the document
|
|
292
324
|
stays where it is. A mistyped glob costs you a failing check and nothing else.
|
|
293
325
|
|
|
294
326
|
## Documents outside the repository
|
|
@@ -309,12 +341,14 @@ real directory on this machine: where to open the file.
|
|
|
309
341
|
|
|
310
342
|
The variable must resolve when the configuration loads. Unset, empty, relative,
|
|
311
343
|
or naming a directory that is not there, each refuses the configuration and
|
|
312
|
-
says which. A store landing inside `.ssh`, `Secret/`, a cache or any other
|
|
313
|
-
directory on the deny list is refused too
|
|
314
|
-
directories, so it declines to write documents into them.
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
344
|
+
says which. A store landing inside `.ssh`, `Secret/`, a cache, or any other
|
|
345
|
+
directory on the deny list is refused too: the tool declines to read those
|
|
346
|
+
directories, so it declines to write documents into them.
|
|
347
|
+
|
|
348
|
+
Plain `${NAME}` is the whole syntax. A default value would let a machine
|
|
349
|
+
without the vault write documents into the repository and report success. The
|
|
350
|
+
Stop hook blocks on the same failure, so a missing variable cannot switch the
|
|
351
|
+
gate off quietly.
|
|
318
352
|
|
|
319
353
|
Deletion changes as well. A repository's documents ride the branch, and `git
|
|
320
354
|
checkout` brings one back. A store stands still while the checkout moves, so
|
|
@@ -334,12 +368,14 @@ auto:
|
|
|
334
368
|
budget: 5
|
|
335
369
|
```
|
|
336
370
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
`verify` afterwards.
|
|
341
|
-
|
|
342
|
-
|
|
371
|
+
constant-docs itself calls no model. It splits your command with `shlex` and
|
|
372
|
+
runs it directly, so an agent CLI, a script, and a Makefile target all work. No
|
|
373
|
+
credential goes in it. The command's success is checked: `auto` re-runs
|
|
374
|
+
`verify` afterwards.
|
|
375
|
+
|
|
376
|
+
It exits 0 when everything is clean, 1 when something is still stale, and 2
|
|
377
|
+
when the command could not run or exited non-zero. Three outcomes, because a
|
|
378
|
+
scheduler that confuses the last two retries the wrong one.
|
|
343
379
|
|
|
344
380
|
## Three things it checks that a hash cannot
|
|
345
381
|
|
|
@@ -354,9 +390,10 @@ document, with a date.
|
|
|
354
390
|
in those modules' files by name, so it goes stale when their boundary moves.
|
|
355
391
|
|
|
356
392
|
**An error catalogue agrees with the source, both ways.** Every message the
|
|
357
|
-
code raises appears in the catalogue, and every entry still
|
|
358
|
-
|
|
359
|
-
one document here the tool can
|
|
393
|
+
code raises appears in the catalogue, and every entry listed is still raised in
|
|
394
|
+
the code. The check needs no parsing beyond a string scan, so it costs nothing,
|
|
395
|
+
and it is the one document here the tool can guarantee. The rest are only
|
|
396
|
+
fresh.
|
|
360
397
|
|
|
361
398
|
## What a document looks like
|
|
362
399
|
|
|
@@ -381,8 +418,8 @@ constant_docs:
|
|
|
381
418
|
hash_method: sha256-over-sorted-path-and-content
|
|
382
419
|
hash_covers: source_files
|
|
383
420
|
timestamp: '2026-08-19T09:14:00Z'
|
|
384
|
-
generator: constant-docs/0.
|
|
385
|
-
generator_spec: https://github.com/
|
|
421
|
+
generator: constant-docs/0.8.0
|
|
422
|
+
generator_spec: https://github.com/daemon-systems/constant-docs/blob/main/SPEC.md
|
|
386
423
|
---
|
|
387
424
|
## Purpose
|
|
388
425
|
|
|
@@ -404,7 +441,7 @@ report records that it happened.
|
|
|
404
441
|
|
|
405
442
|
The frontmatter stands alone. An agent that has never run this tool can see
|
|
406
443
|
what the document describes, where its source lives, and how to recompute the
|
|
407
|
-
|
|
444
|
+
hash. `project` answers the question asked before every overwrite and every
|
|
408
445
|
deletion: whose document is this. The tool keeps everything it owns under one
|
|
409
446
|
`constant_docs` key, an Open Knowledge Format producer extension. `type`,
|
|
410
447
|
`title`, `tags` and the two dates stay yours, and unknown keys survive a write.
|
|
@@ -424,11 +461,11 @@ prune() # deletes orphans; under a store, lists them
|
|
|
424
461
|
|
|
425
462
|
## What it does not do
|
|
426
463
|
|
|
427
|
-
A module names a glob, and the tool
|
|
428
|
-
matches. Nothing in that step knows a language, which is what makes
|
|
429
|
-
no model client, no network call, no
|
|
430
|
-
byte moves the hash. Fixing a typo in a comment reports the
|
|
431
|
-
your agent rewrites a document the edit never touched.
|
|
464
|
+
A module names a glob, and the tool hashes the bytes of the files that glob
|
|
465
|
+
matches. Nothing in that step knows a language, which is what makes it work on
|
|
466
|
+
yours: no model client, no network call, no syntax tree, no import graph. The
|
|
467
|
+
cost is that any byte moves the hash. Fixing a typo in a comment reports the
|
|
468
|
+
module stale, and your agent rewrites a document the edit never touched.
|
|
432
469
|
|
|
433
470
|
It watches files and nothing else. A document quoting a queue depth or a
|
|
434
471
|
production URL stays fresh for ever, because nobody edited anything. Hash what
|
|
@@ -22,20 +22,29 @@ constant_docs:
|
|
|
22
22
|
- src/constant_docs/api.py
|
|
23
23
|
- src/constant_docs/cli.py
|
|
24
24
|
- src/constant_docs/kinds.py
|
|
25
|
-
source_hash: sha256:
|
|
25
|
+
source_hash: sha256:aeb0c87c1ea90b23e657543f1eb6c39707733d7def1a6f05096158295d76cec8
|
|
26
26
|
hash_method: sha256-over-sorted-path-and-content
|
|
27
27
|
hash_covers: source_files
|
|
28
|
-
timestamp: '2026-09-
|
|
29
|
-
generator: constant-docs/0.
|
|
30
|
-
generator_spec: https://github.com/
|
|
28
|
+
timestamp: '2026-09-19T13:07:50Z'
|
|
29
|
+
generator: constant-docs/0.9.1
|
|
30
|
+
generator_spec: https://github.com/daemon-systems/constant-docs/blob/main/SPEC.md
|
|
31
31
|
-->
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
34
42
|
# constant-docs
|
|
35
43
|
|
|
36
44
|
Documentation that fails the build when it drifts.
|
|
37
45
|
|
|
38
|
-
A Python command-line tool
|
|
46
|
+
A Python command-line tool and coding-agent plugin for maintaining source
|
|
47
|
+
documents. Each
|
|
39
48
|
document records a hash of the source files it describes, and `constant-docs
|
|
40
49
|
verify` exits 1 the moment the two disagree. The check is a hash comparison, so
|
|
41
50
|
it needs no model client and no network. Your agent writes the documents. The
|
|
@@ -65,8 +74,9 @@ Your coding agent writes every word, because it already holds the source and a
|
|
|
65
74
|
model connection. constant-docs names the documents that moved and takes the
|
|
66
75
|
new text back.
|
|
67
76
|
|
|
68
|
-
That one command is also the whole
|
|
69
|
-
change that alters behaviour and leaves its document behind does
|
|
77
|
+
That one command is also the whole continuous-integration check. It exits 1 on
|
|
78
|
+
drift, so a change that alters behaviour and leaves its document behind does
|
|
79
|
+
not merge:
|
|
70
80
|
|
|
71
81
|
```yaml
|
|
72
82
|
- run: constant-docs verify
|
|
@@ -138,13 +148,15 @@ clean report on no evidence. `plan --json` considers every module whatever the
|
|
|
138
148
|
marks say. `verify` hashes everything and exits 1 on drift. Keep `verify` as
|
|
139
149
|
the gate.
|
|
140
150
|
|
|
141
|
-
Claude Code
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
151
|
+
Codex and Claude Code can load the plugin. Its two document hooks run these
|
|
152
|
+
commands:
|
|
153
|
+
`PostToolUse` runs `mark` after each edit, and `Stop` runs `settle --hook` when
|
|
154
|
+
the turn ends. The agent reads the returned plan and updates the affected documents before
|
|
155
|
+
verification. Any
|
|
156
|
+
harness that can run a command on write and at turn end does the same job.
|
|
145
157
|
|
|
146
|
-
Regeneration happens at
|
|
147
|
-
coding loop never write the same file at once. The dirty set lives in
|
|
158
|
+
Regeneration happens at the end of a turn, never mid-edit, so the generator and
|
|
159
|
+
the coding loop never write the same file at once. The dirty set lives in
|
|
148
160
|
`.constant-docs/dirty.json`, which is gitignored, so a crashed session is
|
|
149
161
|
picked up on the next run.
|
|
150
162
|
|
|
@@ -152,9 +164,9 @@ The [harness guide](guides/harness-integration.md) walks through each route.
|
|
|
152
164
|
|
|
153
165
|
## Kinds
|
|
154
166
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
167
|
+
Every document has a kind. The kind decides which sections its body must carry,
|
|
168
|
+
which prompt its generator is handed, and whether a regeneration replaces the
|
|
169
|
+
body or appends an entry.
|
|
158
170
|
|
|
159
171
|
| Kind | For | Mode |
|
|
160
172
|
|---|---|---|
|
|
@@ -162,9 +174,10 @@ replaces the body or appends to it.
|
|
|
162
174
|
| `spec` | What a subsystem promises, and why | replace |
|
|
163
175
|
| `log` | A changelog or build log | **append** |
|
|
164
176
|
| `architecture` | The module map, with a diagram | replace |
|
|
165
|
-
| `errors` | Every message the
|
|
177
|
+
| `errors` | Every message the covered code can raise | replace |
|
|
166
178
|
| `config-reference` | Every configuration key | replace |
|
|
167
179
|
| `cli-reference` | Every command and flag | replace |
|
|
180
|
+
| `manual-page` | One user task, written for the person doing it | replace |
|
|
168
181
|
|
|
169
182
|
```yaml
|
|
170
183
|
modules:
|
|
@@ -183,8 +196,25 @@ An append never rewrites or reorders what is already there. Pass a whole body
|
|
|
183
196
|
to a log, or a single entry to a module document, and the tool refuses it. A
|
|
184
197
|
configuration that has never heard of kinds behaves exactly as it did.
|
|
185
198
|
|
|
186
|
-
One
|
|
187
|
-
that all
|
|
199
|
+
One shared writing register governs documents written from built-in prompts. It
|
|
200
|
+
lives in a single file that all eight built-in prompts receive, and is checked
|
|
201
|
+
against the canonical technical-writing skill. The smaller shared core can also
|
|
202
|
+
be consumed by other agents through a pinned import.
|
|
203
|
+
The [writing-register guide](guides/writing-register.md) describes the exports.
|
|
204
|
+
|
|
205
|
+
## User manuals
|
|
206
|
+
|
|
207
|
+
A user manual is an ordered collection of small task pages. Each page is a
|
|
208
|
+
normal tracked document, so a product change makes only the affected pages
|
|
209
|
+
stale. Constant-Docs assembles the navigation without a model call.
|
|
210
|
+
|
|
211
|
+
Manuals declare input and output token ceilings. A page already above its input
|
|
212
|
+
ceiling appears under `blocked` and is never handed to the supplied generation
|
|
213
|
+
workflow. Split that page by user task, narrow its source, or change the budget
|
|
214
|
+
deliberately.
|
|
215
|
+
|
|
216
|
+
This repository uses the feature itself. Start with the [Constant-Docs user
|
|
217
|
+
manual](docs/user-manual/index.md).
|
|
188
218
|
|
|
189
219
|
## Finding what is undocumented
|
|
190
220
|
|
|
@@ -194,17 +224,18 @@ constant-docs coverage # source no module covers, by directory
|
|
|
194
224
|
constant-docs completeness # kinds of document the contents warrant
|
|
195
225
|
```
|
|
196
226
|
|
|
197
|
-
`verify` can only check what has been declared, so a repository
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
justified is one nobody
|
|
227
|
+
`verify` can only check what has been declared, so a repository can drift a
|
|
228
|
+
long way undocumented while every check passes. `coverage` does one thing: a
|
|
229
|
+
set difference. It reports the files no declared glob matches and no exclusion
|
|
230
|
+
names. Declare the directories that should carry no document in the
|
|
231
|
+
configuration, with a reason each. An exclusion nobody justified is one nobody
|
|
232
|
+
decided.
|
|
202
233
|
|
|
203
234
|
Coverage answers whether a file has a document. It cannot answer whether the
|
|
204
|
-
set of documents is complete
|
|
205
|
-
and a
|
|
206
|
-
|
|
207
|
-
|
|
235
|
+
set of documents is complete. A document that was never written cannot drift,
|
|
236
|
+
and a repository with a document per module and no specification passes every
|
|
237
|
+
coverage check. `completeness` proposes the kinds your contents warrant, each
|
|
238
|
+
with the fact behind it:
|
|
208
239
|
|
|
209
240
|
```
|
|
210
241
|
Warranted and not written (2):
|
|
@@ -218,11 +249,11 @@ Warranted and not written (2):
|
|
|
218
249
|
It proposes from evidence in the repository, such as a console script or an
|
|
219
250
|
exception hierarchy. The existence of a kind is not itself evidence. A library
|
|
220
251
|
with no command line is not offered a CLI reference, because a document nobody
|
|
221
|
-
needs still has to be kept true and still
|
|
252
|
+
needs still has to be kept true and still fails the build when it drifts.
|
|
222
253
|
Nothing warrants a changelog: every repository could keep one, so the signal
|
|
223
254
|
fires everywhere and says nothing.
|
|
224
255
|
|
|
225
|
-
Write the document, or decline the kind under `unwarranted:` with a reason and
|
|
256
|
+
Write the document, or decline the kind under `unwarranted:` with a reason, and
|
|
226
257
|
it stops being proposed.
|
|
227
258
|
|
|
228
259
|
`verify --coverage` and `verify --completeness` fold the two into the gate,
|
|
@@ -232,13 +263,14 @@ document, and `prune` deletes it.
|
|
|
232
263
|
## Files it will not touch
|
|
233
264
|
|
|
234
265
|
Pointing the tool at a `docs/` folder the repository already has is safe. Every
|
|
235
|
-
document this tool writes carries a block naming the tool
|
|
236
|
-
|
|
237
|
-
|
|
266
|
+
document this tool writes carries a block naming the tool and, when you declare
|
|
267
|
+
a `project` id, the project that wrote it. A markdown file without that block
|
|
268
|
+
belongs to somebody else, and three commands act on the difference:
|
|
238
269
|
|
|
239
270
|
- `apply` refuses to write over one. It names the file and changes no byte
|
|
240
271
|
- `prune` refuses to delete one, and names every file it left alone
|
|
241
|
-
- `verify` lists them under a heading of their own, and
|
|
272
|
+
- `verify` lists them under a heading of their own, and none of them fails the
|
|
273
|
+
check
|
|
242
274
|
|
|
243
275
|
Take one over when you want it maintained:
|
|
244
276
|
|
|
@@ -259,7 +291,7 @@ rewrite it. Every body your agent generates still carries those headings, and
|
|
|
259
291
|
`apply` checks before it writes a byte.
|
|
260
292
|
|
|
261
293
|
A glob that matches no file gets the same restraint. `verify` names the module
|
|
262
|
-
and exits 1, `apply` refuses to record a
|
|
294
|
+
and exits 1, `apply` refuses to record a hash over nothing, and the document
|
|
263
295
|
stays where it is. A mistyped glob costs you a failing check and nothing else.
|
|
264
296
|
|
|
265
297
|
## Documents outside the repository
|
|
@@ -280,12 +312,14 @@ real directory on this machine: where to open the file.
|
|
|
280
312
|
|
|
281
313
|
The variable must resolve when the configuration loads. Unset, empty, relative,
|
|
282
314
|
or naming a directory that is not there, each refuses the configuration and
|
|
283
|
-
says which. A store landing inside `.ssh`, `Secret/`, a cache or any other
|
|
284
|
-
directory on the deny list is refused too
|
|
285
|
-
directories, so it declines to write documents into them.
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
315
|
+
says which. A store landing inside `.ssh`, `Secret/`, a cache, or any other
|
|
316
|
+
directory on the deny list is refused too: the tool declines to read those
|
|
317
|
+
directories, so it declines to write documents into them.
|
|
318
|
+
|
|
319
|
+
Plain `${NAME}` is the whole syntax. A default value would let a machine
|
|
320
|
+
without the vault write documents into the repository and report success. The
|
|
321
|
+
Stop hook blocks on the same failure, so a missing variable cannot switch the
|
|
322
|
+
gate off quietly.
|
|
289
323
|
|
|
290
324
|
Deletion changes as well. A repository's documents ride the branch, and `git
|
|
291
325
|
checkout` brings one back. A store stands still while the checkout moves, so
|
|
@@ -305,12 +339,14 @@ auto:
|
|
|
305
339
|
budget: 5
|
|
306
340
|
```
|
|
307
341
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
`verify` afterwards.
|
|
312
|
-
|
|
313
|
-
|
|
342
|
+
constant-docs itself calls no model. It splits your command with `shlex` and
|
|
343
|
+
runs it directly, so an agent CLI, a script, and a Makefile target all work. No
|
|
344
|
+
credential goes in it. The command's success is checked: `auto` re-runs
|
|
345
|
+
`verify` afterwards.
|
|
346
|
+
|
|
347
|
+
It exits 0 when everything is clean, 1 when something is still stale, and 2
|
|
348
|
+
when the command could not run or exited non-zero. Three outcomes, because a
|
|
349
|
+
scheduler that confuses the last two retries the wrong one.
|
|
314
350
|
|
|
315
351
|
## Three things it checks that a hash cannot
|
|
316
352
|
|
|
@@ -325,9 +361,10 @@ document, with a date.
|
|
|
325
361
|
in those modules' files by name, so it goes stale when their boundary moves.
|
|
326
362
|
|
|
327
363
|
**An error catalogue agrees with the source, both ways.** Every message the
|
|
328
|
-
code raises appears in the catalogue, and every entry still
|
|
329
|
-
|
|
330
|
-
one document here the tool can
|
|
364
|
+
code raises appears in the catalogue, and every entry listed is still raised in
|
|
365
|
+
the code. The check needs no parsing beyond a string scan, so it costs nothing,
|
|
366
|
+
and it is the one document here the tool can guarantee. The rest are only
|
|
367
|
+
fresh.
|
|
331
368
|
|
|
332
369
|
## What a document looks like
|
|
333
370
|
|
|
@@ -352,8 +389,8 @@ constant_docs:
|
|
|
352
389
|
hash_method: sha256-over-sorted-path-and-content
|
|
353
390
|
hash_covers: source_files
|
|
354
391
|
timestamp: '2026-08-19T09:14:00Z'
|
|
355
|
-
generator: constant-docs/0.
|
|
356
|
-
generator_spec: https://github.com/
|
|
392
|
+
generator: constant-docs/0.8.0
|
|
393
|
+
generator_spec: https://github.com/daemon-systems/constant-docs/blob/main/SPEC.md
|
|
357
394
|
---
|
|
358
395
|
## Purpose
|
|
359
396
|
|
|
@@ -375,7 +412,7 @@ report records that it happened.
|
|
|
375
412
|
|
|
376
413
|
The frontmatter stands alone. An agent that has never run this tool can see
|
|
377
414
|
what the document describes, where its source lives, and how to recompute the
|
|
378
|
-
|
|
415
|
+
hash. `project` answers the question asked before every overwrite and every
|
|
379
416
|
deletion: whose document is this. The tool keeps everything it owns under one
|
|
380
417
|
`constant_docs` key, an Open Knowledge Format producer extension. `type`,
|
|
381
418
|
`title`, `tags` and the two dates stay yours, and unknown keys survive a write.
|
|
@@ -395,11 +432,11 @@ prune() # deletes orphans; under a store, lists them
|
|
|
395
432
|
|
|
396
433
|
## What it does not do
|
|
397
434
|
|
|
398
|
-
A module names a glob, and the tool
|
|
399
|
-
matches. Nothing in that step knows a language, which is what makes
|
|
400
|
-
no model client, no network call, no
|
|
401
|
-
byte moves the hash. Fixing a typo in a comment reports the
|
|
402
|
-
your agent rewrites a document the edit never touched.
|
|
435
|
+
A module names a glob, and the tool hashes the bytes of the files that glob
|
|
436
|
+
matches. Nothing in that step knows a language, which is what makes it work on
|
|
437
|
+
yours: no model client, no network call, no syntax tree, no import graph. The
|
|
438
|
+
cost is that any byte moves the hash. Fixing a typo in a comment reports the
|
|
439
|
+
module stale, and your agent rewrites a document the edit never touched.
|
|
403
440
|
|
|
404
441
|
It watches files and nothing else. A document quoting a queue depth or a
|
|
405
442
|
production URL stays fresh for ever, because nobody edited anything. Hash what
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "constant-docs"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.9.1"
|
|
4
4
|
description = "Self-maintaining documentation for agentic codebases"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -32,15 +32,15 @@ classifiers = [
|
|
|
32
32
|
dependencies = ["pyyaml>=6.0.3"]
|
|
33
33
|
|
|
34
34
|
[[project.authors]]
|
|
35
|
-
name = "
|
|
35
|
+
name = "Daemon Agent"
|
|
36
36
|
|
|
37
37
|
[project.urls]
|
|
38
|
-
Homepage = "https://github.com/
|
|
39
|
-
Repository = "https://github.com/
|
|
40
|
-
Documentation = "https://github.com/
|
|
41
|
-
Changelog = "https://github.com/
|
|
42
|
-
Issues = "https://github.com/
|
|
43
|
-
Specification = "https://github.com/
|
|
38
|
+
Homepage = "https://github.com/daemon-systems/constant-docs"
|
|
39
|
+
Repository = "https://github.com/daemon-systems/constant-docs"
|
|
40
|
+
Documentation = "https://github.com/daemon-systems/constant-docs/blob/main/README.md"
|
|
41
|
+
Changelog = "https://github.com/daemon-systems/constant-docs/blob/main/docs/CHANGELOG.md"
|
|
42
|
+
Issues = "https://github.com/daemon-systems/constant-docs/issues"
|
|
43
|
+
Specification = "https://github.com/daemon-systems/constant-docs/blob/main/docs/SPEC.md"
|
|
44
44
|
|
|
45
45
|
[project.scripts]
|
|
46
46
|
constant-docs = "constant_docs.cli:main"
|