avouch 0.1.22

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 (54) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +6 -0
  3. package/README.md +356 -0
  4. package/SPEC.md +379 -0
  5. package/dist/cli/avouch.d.ts +2 -0
  6. package/dist/cli/avouch.js +68 -0
  7. package/dist/cli/check.d.ts +86 -0
  8. package/dist/cli/check.js +333 -0
  9. package/dist/cli/facts.d.ts +3 -0
  10. package/dist/cli/facts.js +157 -0
  11. package/dist/cli/gate.d.ts +3 -0
  12. package/dist/cli/gate.js +149 -0
  13. package/dist/cli/init.d.ts +21 -0
  14. package/dist/cli/init.js +282 -0
  15. package/dist/cli/jsonl.d.ts +5 -0
  16. package/dist/cli/jsonl.js +20 -0
  17. package/dist/cli/query.d.ts +2 -0
  18. package/dist/cli/query.js +138 -0
  19. package/dist/cli/reconcile.d.ts +9 -0
  20. package/dist/cli/reconcile.js +131 -0
  21. package/dist/src/checker.d.ts +3 -0
  22. package/dist/src/checker.js +1136 -0
  23. package/dist/src/contract.d.ts +135 -0
  24. package/dist/src/contract.js +3 -0
  25. package/dist/src/coverage.d.ts +65 -0
  26. package/dist/src/coverage.js +237 -0
  27. package/dist/src/evaluate.d.ts +66 -0
  28. package/dist/src/evaluate.js +535 -0
  29. package/dist/src/expr.d.ts +209 -0
  30. package/dist/src/expr.js +497 -0
  31. package/dist/src/index.d.ts +11 -0
  32. package/dist/src/index.js +11 -0
  33. package/dist/src/ledger.d.ts +96 -0
  34. package/dist/src/ledger.js +241 -0
  35. package/dist/src/markdown.d.ts +66 -0
  36. package/dist/src/markdown.js +701 -0
  37. package/dist/src/patch.d.ts +6 -0
  38. package/dist/src/patch.js +37 -0
  39. package/dist/src/query.d.ts +333 -0
  40. package/dist/src/query.js +739 -0
  41. package/dist/src/reconcile.d.ts +61 -0
  42. package/dist/src/reconcile.js +151 -0
  43. package/dist/src/stage1-check.d.ts +31 -0
  44. package/dist/src/stage1-check.js +329 -0
  45. package/dist/src/touched.d.ts +1 -0
  46. package/dist/src/touched.js +9 -0
  47. package/dist/src/unknown.d.ts +10 -0
  48. package/dist/src/unknown.js +173 -0
  49. package/docs/adapter-guide.md +187 -0
  50. package/docs/markdown-convention.md +635 -0
  51. package/ontology.schema.json +1757 -0
  52. package/package.json +57 -0
  53. package/templates/agents-block.md +12 -0
  54. package/vocab.ttl +524 -0
package/LICENSE ADDED
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
package/NOTICE ADDED
@@ -0,0 +1,6 @@
1
+ Avouch
2
+ Copyright 2026 Githon LLC
3
+
4
+ SPEC.md section 2 quotes short definitions from the Palantir Foundry
5
+ documentation. These quotations belong to their authors. The Apache
6
+ License 2.0 of this project does not apply to them.
package/README.md ADDED
@@ -0,0 +1,356 @@
1
+ # Avouch
2
+
3
+ Avouch is an ontology spec whose every claim cites its source. An Avouch file describes objects, links and actions. A checker compares the file with facts that a project adapter extracts from the project's own source documents and evidence.
4
+
5
+ ## Status
6
+
7
+ Avouch is an experimental draft. It is not a standard. The spec can change. A change that breaks existing files raises the version number in both places at the same time (SPEC section 8).
8
+
9
+ ## Why Avouch
10
+
11
+ 1. OWL, SHACL and LinkML describe classes, properties and data constraints. They do not describe commands with preconditions, link effects, state transitions and permissions. Avouch adds this layer.
12
+ 2. Palantir Foundry has action types, submission criteria and parameters. Avouch uses the same names for its keys (SPEC section 2). Avouch is a spec for files that are checked against the project's own source documents. It is not a runtime platform.
13
+ 3. Discipline: each claim has a verbatim quote from its source (R2). An unknown is written explicitly as `unknown` or `unspecified`. A waiver that matches no violation is reported as `stale_waiver`. R7 compares the declarations with execution evidence.
14
+
15
+ ## Quick start
16
+
17
+ In this directory, run:
18
+
19
+ ```
20
+ npm install
21
+ npm test
22
+ npm run typecheck
23
+ ```
24
+
25
+ Read in this order:
26
+
27
+ 1. `SPEC.md`
28
+ 2. `examples/library/`: `library.md`, then `library.ontology.yaml`, then `adapter.ts`, then `profile.ts`
29
+ 3. `docs/adapter-guide.md`
30
+
31
+ ## Adopting Avouch
32
+
33
+ Start with step 0, then do the two steps, in this order.
34
+
35
+ 0. At the top level of the git repository, run `npx -y avouch init`. It installs the Claude Code plugin (see "Claude Code plugin") and adds the Avouch instructions for coding agents. The instructions tell an agent to read the rules with `avouch query`, not with a text tool, and to run `avouch check` and `avouch gate docs` after a change. There are two modes: `--append` adds the instructions to `AGENTS.md` and `CLAUDE.md`, if they exist; `--print` writes them to the terminal, and you add them yourself. Without a flag, `init` asks in a terminal and prints otherwise. `--dry-run` shows what would change and writes nothing. Then run `/avouch:scan` in Claude Code, which drafts `avouch.config.json`, a first ontology and the document review ledger. See "init" in "Command line".
36
+ 1. Every project: keep a document review ledger, and run `avouch gate docs` in CI. See "Document review ledger (gate docs)". The gate works with any Markdown style. It guarantees that each changed section of a design document has a recorded review decision. It does not guarantee that the decision is complete: it does not check that a person read the section, that `claims` lists every claim, or that the claimed places say what the section says.
37
+ 2. For the structured checks, choose one path. The checks are: the field lists and catalogs of R2, and R3, R5, R6, R8, R9 and R10 (SPEC section 5). Each needs a fact from the project's documents (SPEC section 6). R7 needs execution evidence, which the built-in adapter does not read.
38
+ - (a) Write the documents to the Markdown convention, `docs/markdown-convention.md`, and use the built-in adapter. `avouch facts` checks the documents without an ontology.
39
+ - (b) Write your own adapter: `docs/adapter-guide.md` sections 1 to 6. The grammar of the documents is then free. An AI agent writes it in a new session, not in the session that writes the ontology, with the prompt in "For AI agents" of that guide.
40
+
41
+ Avouch does not widen the built-in grammar for one project.
42
+
43
+ A rule whose fact is not available is switched off in the profile, with a reason: `"rules": {"R8": {"disabled": "<reason>"}}` (in `profile` of the Markdown configuration; `docs/adapter-guide.md` section 5). The checker prints the reason. Without the switch, the rule reports `fact_missing` for the missing fact. The built-in adapter switches R3 and R9 off by itself when their facts are not set, and R7 always (`docs/adapter-guide.md` section 7).
44
+
45
+ ## Layout
46
+
47
+ | Path | Content |
48
+ |---|---|
49
+ | `SPEC.md` | concepts (foundation / extension), fields, rules R1–R12, fact contract |
50
+ | `ontology.schema.json` | JSON Schema: `$id` and `formatVersion`; shape only, no project values |
51
+ | `vocab.ttl` | RDF vocabulary (`ofv:`) for exporting an ontology as RDF; one term per schema field (`test/vocab.test.ts`) |
52
+ | `src/contract.ts` | `SourceFacts`: the facts with their sources, and diagnostics |
53
+ | `src/checker.ts` | `check(ontology, facts)` returns the violations |
54
+ | `src/patch.ts` | JSON-pointer patch that the mutation tests use |
55
+ | `src/query.ts` | read-only queries over an ontology (`avouch query`) |
56
+ | `src/reconcile.ts` | runtime consistency: `reconcile`, `projectSnapshot` (SPEC section 9) |
57
+ | `src/coverage.ts` | decision-row coverage of reconcile rows, with a baseline (`verifyCoverage`, `avouch gate verify`) |
58
+ | `src/ledger.ts` | document review ledger: units, outline, ledger shape, pointers (`verifyLedger`, `avouch gate docs`) |
59
+ | `src/markdown.ts` | built-in adapter for plain Markdown sources (configured in `avouch.config.json`); see `docs/adapter-guide.md` section 7 |
60
+ | `cli/` | command line: `avouch check` (`check.ts`), `avouch query` (`query.ts`), `avouch reconcile` (`reconcile.ts`), `avouch gate verify` and `avouch gate docs` (`gate.ts`) |
61
+ | `tsconfig.build.json` | configuration for the JavaScript and type declaration build |
62
+ | `dist/` | build output; not tracked in version control |
63
+ | `examples/library/` | toy project: source document, ontology, mutations, adapter, profile, evidence |
64
+ | `test/` | tests of the spec, checker and CLI |
65
+ | `docs/` | guides (`adapter-guide.md`) |
66
+ | `LICENSE` | Apache License 2.0 |
67
+ | `NOTICE` | copyright notice and third-party text |
68
+
69
+ ## Write an adapter
70
+
71
+ A project supplies an adapter and a profile. The adapter reads the project's sources and returns `SourceFacts`. The profile sets the rule switches and the project patterns. `docs/adapter-guide.md` explains each fact and walks through the toy library adapter. A project that writes its documents to `docs/markdown-convention.md` needs no adapter; see "Adopting Avouch".
72
+
73
+ ## Tests
74
+
75
+ - `test/library.test.ts`: the toy library project runs through the checker. The clean ontology has no violations, and each mutation fails with exactly its expected rule.
76
+ - `test/schema.test.ts`: the JSON Schema accepts the toy ontology and rejects bad shapes.
77
+ - `test/cli.test.ts`: command line results, schema errors, checker violations and adapter errors.
78
+ - `test/config.test.ts`: the project configuration `avouch.config.json`: validation, discovery, precedence, and when it is read.
79
+ - `test/query.test.ts`: the query functions on the toy ontology and on patched variants of it.
80
+ - `test/query-cli.test.ts`: `avouch query`: the JSON envelope, the stamp (git and check), exit codes, text output, and that a query writes no file.
81
+ - `test/coverage.test.ts`: `verifyCoverage` on the toy ontology and on patched variants of it: units, gaps, stale baseline entries, failing reconcile rows, and the shape checks.
82
+ - `test/reconcile-cli.test.ts`: `avouch reconcile`: JSON lines in and out, exit codes, and the line number in each input error.
83
+ - `test/gate-cli.test.ts`: `avouch gate verify`: the pipeline from cases to the gate, text and JSON output, the check with `--adapter`, and exit codes.
84
+ - `test/markdown.test.ts`: the built-in Markdown adapter: the toy library gives the same findings as with its own adapter (outside the rules the generic adapter cannot check), and each parse convention.
85
+ - `test/boundary.test.ts`: the runtime (`src/` and `examples/`) imports only relative paths inside this package. It imports no npm package and no file outside the package.
86
+
87
+ ## Command line
88
+
89
+ Build with `npm run build`, then run it. `avouch --help` (or `-h`) prints the usage lines of all commands; `avouch <command> --help` prints the usage of one command. Both exit with 0 and need no files, configuration or git. For example:
90
+
91
+ ```sh
92
+ avouch check [-f <ontology.yaml>] [--config <avouch.config.json>] [--adapter <module|config.json>]
93
+ ```
94
+
95
+ All four commands (`check`, `query`, `reconcile`, `gate verify`) take the ontology file with `-f <file>` and the project configuration with `--config <file>`. See "Configuration" below for the ontology file and the adapter that a command uses when it is not given them.
96
+
97
+ In a source checkout, use `node dist/cli/avouch.js` in place of `avouch`.
98
+ The command runs both the JSON Schema validator and the checker. Exit code 0 means both pass; 1 means there are schema errors or unwaived violations; 2 means invalid arguments or a file, parsing or adapter error.
99
+
100
+ The adapter module must export a named function `facts(ontologyPath)` that returns `SourceFacts`, or a promise of `SourceFacts`. It receives the absolute ontology path. The facts include the project profile and source lookup functions; see `src/contract.ts` and `docs/adapter-guide.md`. Adapter paths resolve from the current directory. A `.ts` adapter can load directly only on a Node version that supports type stripping, and its syntax must be supported by that runtime.
101
+
102
+ An `--adapter` path that ends in `.json` is not loaded as a module: it is the configuration of the built-in Markdown adapter (`src/markdown.ts`, `docs/adapter-guide.md` section 7). Without `--adapter`, the adapter is the `adapter` of the project configuration. `avouch check` always runs the check: when there is no adapter, the command prints the reason and the usage and exits with 2. `avouch query` and `avouch gate verify` run it only with `--check` or `--adapter`; otherwise their check stays `not_run`.
103
+
104
+ ### init
105
+
106
+ ```sh
107
+ avouch init [--append | --print] [--plugin | --no-plugin] [--dry-run]
108
+ ```
109
+
110
+ `avouch init` prepares a project for Avouch. Run it inside a git repository; it works on the git top level. Step 1 is the Claude Code plugin: with `--plugin`, it runs `claude plugin marketplace add githonllc/avouch` and `claude plugin install avouch@avouch` (it does not check if the plugin is already installed, and runs both commands again on each run). Without a flag, it asks in a terminal and only prints the two commands otherwise. When `claude` is not on `PATH`, it prints the two commands. `--no-plugin` skips the step. Step 2 is the instructions for coding agents (`templates/agents-block.md`). `--append` adds them, between the lines `<!-- BEGIN avouch -->` and `<!-- END avouch -->`, to `AGENTS.md` and `CLAUDE.md` at the top level. It never creates a file: when neither file exists, it prints the instructions. It replaces an earlier block in place, keeps all other bytes and the line endings (CRLF or LF) of the file, and a second run changes nothing. A file that does not have exactly one BEGIN line and then one END line is an error, and the file is not changed. It skips `CLAUDE.md` when the file has the line `@AGENTS.md` and `AGENTS.md` exists. `--print` writes the instructions to stdout. Without a flag, it asks in a terminal and prints otherwise. `--dry-run` runs no command and writes no file; it prints the commands and, for each file, `append`, `replace` or `unchanged`. Step 3 tells you to run `/avouch:scan` in Claude Code. Exit code 0 means done (also a print and a dry run); 1 means a plugin command failed or a file could not be changed; 2 means invalid arguments or no git repository.
111
+
112
+ ### facts
113
+
114
+ ```sh
115
+ avouch facts [--config <avouch.config.json>] [--adapter <config.json>] [--json] [--inventory <file.json>]
116
+ ```
117
+
118
+ `avouch facts` checks design documents against the built-in Markdown adapter, without an ontology. It reads the documents of a Markdown configuration (`--adapter <config.json>`, else the `adapter` of the project configuration; a module adapter is an error) and prints what the adapter read: one `FACT <name> <anchor>: <summary>` line for each fact (`FACT <name>: not configured` for a fact without an anchor), one `FIELDS <section>#<first line>: <n> field(s)` line for each top-level `text` code block, a `WARN` line for a line in such a block that is not a field line (a warning only), and a `DIAG <rule> <kind> <key>: <msg>` line for each diagnostic of the adapter. The last line is `OK: 0 diagnostic(s)` or `FAIL: <n> diagnostic(s)`. `--json` prints one object `{"facts", "fields", "warnings", "diagnostics"}` instead. `--inventory <file.json>` compares the facts and fields with an inventory file `{"conventionVersion": 1, "facts": {...}, "fields": {...}, "prose": [...]}` (`conventionVersion` is the version of the Markdown convention, `docs/markdown-convention.md`) (the shapes of `--json`; `prose` is ignored) and prints each difference as `DIFF <path>: inventory <x>, document <y>`. Exit code 0 means no diagnostic and no difference; 1 means a diagnostic or a difference; 2 means invalid arguments or a configuration or file error.
119
+
120
+ ## Configuration
121
+
122
+ The project configuration `avouch.config.json` names the ontology file and the adapter, so that a command needs no `-f` and no `--adapter`. It is strict JSON; an unknown key is an error. Every path in it is relative to the configuration file.
123
+
124
+ ```json
125
+ {
126
+ "ontology": "avouch/avouch.ontology.yaml",
127
+ "adapter": { "markdown": { "docs": ["docs/design.md"], "facts": { "storeCatalog": "#stores" }, "profile": {} } }
128
+ }
129
+ ```
130
+
131
+ ```json
132
+ {
133
+ "ontology": "avouch/avouch.ontology.yaml",
134
+ "adapter": "test/avouch-adapter.mjs"
135
+ }
136
+ ```
137
+
138
+ - `ontology`: optional; when present, a non-empty string. A command that needs the ontology file (`check`, `query`, `reconcile`, `gate verify`) and has no `-f` fails with `<configuration file>: ontology: missing`. `avouch facts` needs no ontology, and `avouch check -f <file>` with a configuration that has no `ontology` uses the adapter of the configuration (there is no ontology to compare with `-f`).
139
+ - `adapter`: required, in one of two forms.
140
+ - A string: a file path, relative to the configuration file, with the same meaning as `--adapter`. A path that ends in `.json` is the configuration of the built-in Markdown adapter (a stand-alone file; its `docs` are relative to that file); any other path is an adapter module (`facts(ontologyPath)`). The file is read only when the command uses the adapter. An empty string is an error, and so is a path that is not a file (`<configuration file>: adapter: file not found`). In a query or gate stamp, `check.adapter` is the string as written in the configuration file (as `--adapter` stamps its argument as given).
141
+ - An object with one key, `markdown`: the configuration of the built-in Markdown adapter, inline (`docs`, `facts`, `profile`; `docs/adapter-guide.md` section 7). Any other key (also `module`) is an unknown key. In a stamp, `check.adapter` is `<configuration file>#adapter`, where the configuration file is the `--config` argument as given, or `avouch.config.json` when the file was found at the git top level.
142
+ - `ledger`: optional, used only by `avouch gate docs`: an object with the optional keys `file` (the ledger file, relative to the configuration file or absolute; default `avouch.ledger.jsonl`) and `docs` (a non-empty list of distinct document paths, relative to the configuration file; default the `docs` of the Markdown adapter; required with an adapter module). See "Document review ledger (gate docs)".
143
+ - An error in the file is reported as `<file>: <json path>: <problem>` with exit code 2.
144
+
145
+ Where the file is:
146
+
147
+ 1. `--config <file>`, relative to the working directory. A file that does not exist is an error (`--config <file>: file not found`); there is no fallback.
148
+ 2. Else, with `-f`, `avouch.config.json` at the git top level of the directory of the `-f` file. The configuration belongs to the repository of the ontology, so an ontology in another repository (or in a submodule, which has its own top level) does not use the adapter of the current repository. A directory outside any git repository has no configuration.
149
+ 3. Else `avouch.config.json` at the git top level of the working directory.
150
+
151
+ There is no search in parent directories and no hidden file.
152
+
153
+ What a command uses:
154
+
155
+ | | Source, in order |
156
+ |---|---|
157
+ | ontology file | `-f`; the `ontology` of the configuration; `avouch/avouch.ontology.yaml` at the git top level |
158
+ | adapter | `--adapter`; the `adapter` of the configuration |
159
+
160
+ The configuration is read only when the command has no `-f`, or when it must run the check and has no `--adapter`. With `-f` and `--adapter` together, the command reads no configuration and runs no `git`. `avouch check` always runs the check; `avouch query` and `avouch gate verify` run it with `--check` or `--adapter`; `avouch reconcile` never runs it. When the command runs the check without `--adapter` and `-f` is not the ontology of the configuration, the command exits with 2 (`-f <file> is not the ontology in <config> (<ontology>): pass --adapter`), because the adapter of the configuration belongs to the other ontology.
161
+
162
+ Errors, each with exit code 2:
163
+
164
+ - `--config <file>: file not found`;
165
+ - `no -f and no --config, and the working directory is not in a git repository (or git is not available)`;
166
+ - `no -f, no avouch.config.json at <top>, and <top>/avouch/avouch.ontology.yaml does not exist; to set up a project, run …`: the message ends with the next step, `init` and then `/avouch:scan` (`gate docs` without a configuration ends the same way);
167
+ - `no adapter: pass --adapter, or set adapter in <top>/avouch.config.json; next step: …`. The next step is a link to "For AI agents" in `docs/adapter-guide.md` (`facts` without an adapter ends the same way). An `avouch.json` next to the ontology is no longer read; if one exists, the message says to set `"adapter"` in `avouch.config.json` to the path of that `avouch.json` (relative to the top level), or to pass it with `--adapter`.
168
+
169
+ ## Query
170
+
171
+ `avouch query` answers questions about the specification in an ontology file. It reads the file and changes nothing. It does not read runtime data.
172
+
173
+ ```sh
174
+ avouch query <verb> <arg> [-f <ontology.yaml>] [--json] [--check] [--config <avouch.config.json>] [--adapter <module|config.json>]
175
+ ```
176
+
177
+ The command takes exactly two positional arguments. The options can be in any position.
178
+
179
+ | Verb | Argument | Answer |
180
+ |---|---|---|
181
+ | `list` | `actions`, `objects`, `links`, `derived`, `dispositions`, `anchors` or `gaps` | the entries of that kind; `anchors` counts the claims per source anchor |
182
+ | `action` | action id, for example `BORROW` | permission, idempotency key, parameters, conditions, the decision rows with the conditions each row requires, edits, creates, emits, link effects, state machine transitions |
183
+ | `object` | object type, for example `Book` | properties, derived properties, state machine, links, scope, the actions that write it and the conditions that read it; for an out-of-scope object, the reason and the actions that write it |
184
+ | `result` | disposition id | every decision row (and `otherwise`) of every action that gives this result |
185
+ | `writes` | `<Object>.<property>`, for example `Book.state` | the actions that edit the property; for a derived property, the actions that edit its inputs, with the input path |
186
+ | `reads` | `<Object>.<property>` | the conditions, derived properties and materialized properties that read it, directly or through a derived property |
187
+ | `cites` | source anchor, for example `L6` | every claim whose cite names this anchor, in file order, with a JSON Pointer path |
188
+ | `link` | link id, for example `loan_book` | the ends of the link, the effect of every action that has an entry for it (`incident` is false for an action that touches neither end: an R6 stray), and `missing`, the actions that touch an end but have no entry (R6 `link_effect_missing`) |
189
+
190
+ Notes:
191
+
192
+ - `requires` of a decision row: the row is reached only when each earlier row does not hit. A `passes c` row that does not hit gives `{c, must: "fail"}`; a `fails c` row that does not hit gives `{c, must: "pass"}`. The last entry is the condition of the row itself. `fail` means that `c` is not T (it is F or UNKNOWN). The list is not simplified. It covers the decision table only; the invocation results (permission, shape, binding, idempotency) are not in it.
193
+ - `specified` is false when the condition has no expression, does not type-check, or reaches a derived property that has no expression. `blockedBy` names the first such condition in an earlier row. Both are static: `decide` stops at a `blockedBy` row for an input where that condition is undefined (always for an `unspecified` condition; for a derived property without an expression only when evaluation reaches it).
194
+ - `reads`: `direct` means the condition expression reads the property itself. `via` lists only the first derived property on the path (a condition that reads A, where A reads B and B reads the property, has `via: [A]`); follow the chain with the `derived` entries. `rows` lists only the rows whose `when` names the condition; later rows and `otherwise` also depend on it through `requires`. A navigation over a foreign-key link reads its `via` when `via` is a declared property of the link's `from` object and neither the `via` nor the object name has a dot, so a navigation in the condition expression itself reads that property directly (`direct: true`), and a navigation in a derived property gives `via: [<derived property>]`; a navigation over a pure link reads no property.
195
+ - `writes`: creating an object is listed in `creators`, not in `writers`. A materialized property is not expanded, because the checker (R11) already requires its writers to list it in `edits`.
196
+ - `object`: `readers` lists the properties of the object that a condition reads, directly or through derived properties (the full read set, as `reads` gives it).
197
+ - `cites` matches the anchor exactly: `L6` does not match `L6a`.
198
+ - A condition or derived property that does not type-check is listed in `unanalyzed`; it is not dropped silently.
199
+
200
+ `--json` prints a versioned envelope: `{avouchQuery: 1, stamp, verb, arg, result}`. The result types are in `src/query.ts`. Without `--json`, the command prints three stamp lines, an empty line, and an indented text form of the result.
201
+
202
+ The stamp tells which specification answered:
203
+
204
+ - `ontology`: in git, equal to `git.path`; outside git, the real path of the file relative to the working directory, "/" separated. The stamp holds no absolute path unless you wrote one (`--adapter`, `--config`);
205
+ - `git`: the commit, whether the file has uncommitted changes (`dirty`), and its path in the repository; `null` outside a git repository;
206
+ - `check`: `not_run`, or, with `--check` or `--adapter`, the result of the same check as `avouch check` (`pass` or `fail`, with the counts). `--check` uses the adapter of the project configuration. In the JSON stamp, `adapter` is the module path, or the source of a Markdown configuration (the `--adapter` path as written, or `<config file>#adapter`, with the `--config` argument as given or `avouch.config.json`). A failed check does not change the exit code.
207
+
208
+ Exit code 0 means there is a result. 1 means there is no result: the id is not declared, or the list is empty (the envelope is still printed). 2 means invalid arguments, or a file, parsing or adapter error.
209
+
210
+ ## Runtime consistency on the command line
211
+
212
+ `avouch reconcile` compares the result that the runtime reported with the result of the evaluator and with the scenario expectation (SPEC section 9, Runtime consistency). `avouch gate verify` checks that the reconciled cases cover each decision row. Both commands read files and write no file. They do not run project code: the project's test harness writes the cases.
213
+
214
+ ```sh
215
+ avouch reconcile [-f <ontology.yaml>] [--config <avouch.config.json>] --cases <cases.jsonl>
216
+ avouch gate verify [-f <ontology.yaml>] --rows <rows.jsonl> [--baseline <baseline.json>] [--json] [--check] [--config <avouch.config.json>] [--adapter <module|config.json>]
217
+ ```
218
+
219
+ ### reconcile
220
+
221
+ The cases file has one `RuntimeCase` (SPEC section 9) per line, as JSON. Empty lines are skipped. A case can have more keys than `RuntimeCase`; they are ignored. The command prints one `ReconcileRow` per case, as one JSON line, in input order. For example, `avouch reconcile -f library.ontology.yaml --cases cases.jsonl > rows.jsonl` writes the input of `gate verify`.
222
+
223
+ Exit code 0 means that each case gave a row, also a `mismatch` or a `cannot_reconcile` row: the command does not judge the rows. 2 means invalid arguments, a file or parse error, or a line that is not a `RuntimeCase`; the message gives the file and the line number. The command does not use exit code 1.
224
+
225
+ ### gate verify
226
+
227
+ The unit of coverage is the decision row. Each row of a decision table and its `otherwise` is one unit. A unit has the name `<action>:rows.<i>` (`i` from 0) or `<action>:otherwise`, as in the R12 keys. A unit is verified when a `match` row has its action, its `row` and the result of that row. A `mismatch` row does not verify its unit. An action without a decision table has no units; it is printed as `no_table` and does not change the result. The command checks every action of the ontology.
228
+
229
+ Verified means: for one case, `decide` reached this row, and the runtime result and the expected result are the same as the result of `decide`. The runtime does not report which row it ran. A case without an expected result never verifies a unit: `reconcile` gives `expectation_missing` for it, and the gate prints it as `unasserted`. When the runtime result of such a case is a result that the action cannot give, `reconcile` gives `runtime_unmapped` instead, and the case fails the gate. An action with `permission: unknown` never reaches its decision table in `decide`, so all its units stay as `uncovered` entries until the permission is written.
230
+
231
+ A gap is a condition that a decision row names and that is declared without an expression (`unspecified`). `decide` stops at the first row that names a gap, so the units from that row to `otherwise` are blocked: they are printed as `blocked` and are not reported as uncovered. Each gap needs an exemption with a reason in the baseline. When a gap gets an expression, the units it blocked become ordinary units. The change that adds the expression must also add cases or `uncovered` entries for them.
232
+
233
+ The baseline is a JSON file. In this example, the condition `loan_active` has no expression:
234
+
235
+ ```json
236
+ {
237
+ "avouchCoverage": 1,
238
+ "uncovered": [
239
+ { "action": "BORROW", "row": 3, "when": { "fails": "returns_in_good_condition" }, "result": "REFERRED" },
240
+ { "action": "BORROW", "row": "otherwise", "when": null, "result": "BORROWED" }
241
+ ],
242
+ "exempt": [
243
+ { "action": "RETURN", "condition": "loan_active", "kind": "unspecified", "reason": "the source does not state when a loan is active" }
244
+ ]
245
+ }
246
+ ```
247
+
248
+ - `uncovered` lists units that have no `match` yet, each with the `when` and `result` of its row. An entry for a unit that is now verified fails the gate. The tool does not stop new entries; review the baseline diff for that.
249
+ - `exempt` lists gaps, each with a non-empty `reason`.
250
+ - Both keys are required. A key that is not shown above is an error (exit code 2).
251
+ - Without `--baseline`, both lists are empty.
252
+ - Units are named by position. When rows are added, removed or moved, the positions change; an `uncovered` entry whose `when` or `result` is not the same as in the ontology is stale (`moved`).
253
+
254
+ The canonical form of the file is `JSON.stringify(baseline, null, 2)` with a final line break, the keys in the order shown above, `uncovered` in unit order (actions in code-unit order, then rows from 0, then `otherwise`) and `exempt` in action order, then condition order. The gate does not check the form. `--json` prints `uncovered` and `unexempted` in the shape and order of the baseline entries; to make a first baseline, copy them and add a reason to each `unexempted` entry.
255
+
256
+ The gate fails (exit code 1) when:
257
+
258
+ | Output | Cause |
259
+ |---|---|
260
+ | `uncovered` | a unit has no `match`, is not blocked, and is not in `uncovered` of the baseline |
261
+ | `unexempted` | a gap has no entry in `exempt` |
262
+ | `unreasoned` | an `exempt` entry has no reason, or only white space |
263
+ | `stale` | an `uncovered` entry names a unit that does not exist, that has another `when` or `result` (`moved`), that is verified or that is blocked, or repeats an entry; an `exempt` entry names a condition that is not a gap, or repeats an entry |
264
+ | `failed` | a reconcile row names an action that the ontology does not have (`action_unknown`); a reconcile row is a `mismatch`; a `cannot_reconcile` row has the reason `runtime_missing`, `runtime_unmapped`, `expectation_unmapped`, `snapshot_missing` or `evaluator_error`, or the reason `indeterminate` with a detail other than `unspecified` and `permission_unknown` (`why` is the detail for `no_row_matched`, `no_decision_table` and `permission_condition`, and `indeterminate` for another detail or none); a `match` row names a row that the action does not have, or a result that is not the result of that row (`row_unknown`) |
265
+ | `check` | with `--check` or `--adapter`, the same check as `avouch check` fails |
266
+
267
+ The reasons `envelope` and `expectation_missing` do not fail the gate, and neither does `indeterminate` with the detail `unspecified` or `permission_unknown`. A replay is not a decision; a case without an expectation states nothing to compare. A case gives `indeterminate` only when it has an expected result that the action can give, so an `indeterminate` case is an expectation that `decide` does not check. It fails the gate unless its detail is one of the two details of a gap that an ontology can declare: `unspecified` is a condition without an expression, which is a gap in `exempt` of the baseline, or a condition that reads a derived property without an expression, which R12 reports as `condition_unspecified`; `permission_unknown` is `permission: unknown`, which R10 reports when it runs. `no_row_matched` means that no row applies and the table has no `otherwise`. `no_decision_table` means that `decide` passed the shape, permission and binding steps of an action without a decision table, so the expected result is wrong. `permission_condition` means that no alternative of the permission passes, and in at least one alternative the caller lacks only conditional keys and `conditional_when` is U or absent: write a `conditional_when` that decides the case, or remove the expectation of the case (it is then `unasserted`).
268
+
269
+ What the gate does not check. The gate does not compare the runtime result with the expectation of an `indeterminate` case, so a wrong expectation at a declared gap (`unspecified` or `permission_unknown`) passes. The gate does not check that the detail of an `indeterminate` row agrees with the ontology: a rows file made before a gap was filled still passes with `unspecified` or `permission_unknown`, so reconcile the cases again with the current ontology. The gate also keeps no record of a declared gap of its own: no case verifies a unit of a `permission: unknown` action, so each unit is `uncovered`, `baselined` or `blocked`, as any unit that no case verifies; an action without a decision table has no units (the gate prints it as `no_table`); and a condition that reads a derived property without an expression does not block its units, so another case can verify a unit that an `unspecified` case expected. R10 records `permission: unknown`, and R12 records a condition that reads a derived property without an expression; `avouch check` and `gate verify --adapter` run them.
270
+
271
+ Exit code 0 means the gate passes. 1 means it fails. 2 means invalid arguments (also a repeated option), a file or parse error, an ontology without an `actionTypes` mapping, or a rows or baseline file with a wrong shape. Without `--json`, the command prints one `FAIL` line for each failure, `INFO` lines (`check`, `blocked`, `no_table`, `unasserted`), and a summary line that starts with `PASS` or `FAIL`. `--json` prints `{avouchGate: 1, gate: "verify", check, result}`.
272
+
273
+ The same gate is a function in the package root, for tests that run without a file system or child processes: `verifyCoverage(ontology, rows, baseline)` returns the `result` object (`src/coverage.ts`). The command is a thin wrapper around it. The function does not run the check.
274
+
275
+ ### Document review ledger (gate docs)
276
+
277
+ ```sh
278
+ avouch gate docs [--config <avouch.config.json>] [--json]
279
+ ```
280
+
281
+ `avouch gate docs` makes sure that each change to a design document has a recorded review decision. It reads no facts, so it works with any document style. Like `gate verify`, it is a rule of the tool, not of the format.
282
+
283
+ - Units. The gate removes the BOM and changes CRLF to LF. It does not remove the generated blocks: their text is hashed as other text, so a change in generated text re-opens its unit (fail closed). Then it cuts each document at the ATX headings outside fences. A unit runs from its heading line to the next heading of any level. The text before the first heading is the unit with the path `[]`. The key of a unit is the document, the path (the heading titles from the top-level ancestor to the heading, without closing `#`s) and `n`, the occurrence of that path in the document (from 1). The hash is the sha256 of the unit text, line breaks and trailing spaces included. A unit with only blank lines after its heading needs no entry.
284
+ - Outline. The sha256 of the JSON text of the list of `[path, n, heading line]` of all units of a document, in document order, empty units included. The heading line is the exact first line of the unit (`null` for the text before the first heading), so a change of level or of closing `#`s changes the outline, also for an empty unit. A reorder changes the outline.
285
+ - Ledger. By default `avouch.ledger.jsonl` next to the configuration file, one JSON value per line (a leading BOM is removed). The first line is `{"avouchLedger":1}`. Then, for each document, one outline entry `{"doc", "outline"}`, and one entry `{"doc", "path", "n", "sha256"}` for each unit that is not empty, with exactly one of `claims` (a non-empty list of distinct JSON Pointers into the ontology) and `noClaim` (a non-empty reason). The lines are sorted by `doc`, then the outline entry, then `path` element by element (a prefix first), then `n`. So two changes to different documents change different lines.
286
+ - Pointers. RFC 6901, strict: a `~` must be followed by `0` or `1`, and the root pointer `""` is not a claim. One extension: on an array, a segment selects the element whose `id` is the segment, or the string element equal to it (`/linkTypes/loan_book`). A numeric index does not select an element.
287
+ - Configuration. The optional key `"ledger": {"file": "<path>", "docs": ["<path>", ...]}` of `avouch.config.json` (section "Configuration"). A `doc` in the ledger is relative to the directory of `avouch.config.json`, with `/`. The docs of a stand-alone Markdown adapter configuration are converted from the directory of that file. Two names of one file (a link, or a case variant on a case-insensitive file system) are one document named twice.
288
+
289
+ The gate fails (exit code 1) with one line for each failure: `FAIL changed <key>` (the hash of a unit changed), `FAIL unreviewed <key>` (a unit has no entry), `FAIL stale <key>` (an entry has no unit), `FAIL stale <doc> outline` (an outline entry of a document that is not configured), `FAIL outline <doc>` (the outline entry is missing or has another hash) and `FAIL dangling <key> <pointer>` (a pointer does not resolve). A key is printed as `<doc> <path as JSON> <n>`. The units in each list are in ledger order.
290
+
291
+ Exit code 2, with one line on stderr, means:
292
+
293
+ - invalid arguments;
294
+ - an error in the configuration (`<file>: <json path>: <problem>`), or a module adapter without `ledger.docs`;
295
+ - a missing ledger, ontology or document, or one document named twice;
296
+ - a ledger line that is not JSON, or that has a wrong shape, a duplicate key, an unsorted position, a pointer that is not strict RFC 6901, or an unknown version (`<ledger>:<line>: <reason>`).
297
+
298
+ `--json` prints `{avouchGate: 1, gate: "docs", result}`. Each changed or unreviewed unit has its current `sha256`, its `firstLine` and `entry`: the ledger line of the unit as it is now, without the decision. Add `claims` or `noClaim` to it and put it in the ledger.
299
+
300
+ The gate does not check that a person read a unit, that `claims` lists every claim of the unit, or that the claimed places say what the unit says. Review the ledger diff in the pull request, next to the document diff. Known limit: two changes to adjacent ledger lines (for example two units of one document that follow each other) can still give a merge conflict in git.
301
+
302
+ ## Claude Code plugin
303
+
304
+ This repository is also a Claude Code plugin marketplace. Install the plugin:
305
+
306
+ ```sh
307
+ claude plugin marketplace add githonllc/avouch
308
+ claude plugin install avouch@avouch
309
+ ```
310
+
311
+ Inside a Claude Code session, the same two steps are `/plugin marketplace add githonllc/avouch` and `/plugin install avouch@avouch`.
312
+
313
+ The plugin has two skills and one hook. The skills:
314
+ - `cli` (`/avouch:cli`): checks an ontology and runs `avouch query`.
315
+ - `scan` (`/avouch:scan [project-dir]`): drafts or updates a project's ontology from its design documents, and records the review decision of each document section in the ledger (`avouch gate docs` must pass). It uses the adapter that the project has configured and never writes an adapter. The result is a candidate. `avouch check` must exit 0, and each source gap is listed with a proposed fix to the document. When most objects have no documented field list, it also writes `avouch/doc-patch.md`: field lists, foreign keys and store names derived from the code, for a human to review and merge into the documents. It never drafts states, permissions or rules from code.
316
+
317
+ The plugin also has a hook (`hooks/hooks.json`, script `hooks/ontology-read-guard.mjs`, Node 22 or later). Before a Bash or Grep call, it blocks a text read of an ontology (`*.ontology.yaml`, `*.ontology.yml`, or a directory named `ontology` that holds an `*.ontology.yaml` file) with tools such as `grep`, `rg`, `sed`, `cat`, `head`, `python` or `yq`, and tells Claude to use `avouch query`. The reason: text output mixes YAML comments and neighbouring entries with the ontology content, and a comment looks like ontology semantics, but the checker never reads comments. `avouch query` gives the structured permission, conditions, decision rows, effects and citations, with the commit and the check status. The hook allows `avouch` itself, `git`, the Read and Edit tools, searches that do not name the ontology, and a write such as `cat > <file>.ontology.yaml`. A script runtime (`node`, `deno`, `bun`, `python`, `ruby`, `perl`) is blocked only for inline code (`-e`, `-c`, stdin), not for a script file. The hook also allows the locate form `grep -n -- <anchor> <file>.ontology.yaml | cut -d: -f1` that finds the lines to edit; the grep must print line numbers (`-n`). The hook is a reminder, not a guarantee: it matches command text, so a recursive search of a parent directory, a shell variable or `bash -c` is not seen, and the Read tool also shows comments. It does nothing in a clone of this repository, where the test fixtures need the raw text. The hook is part of the plugin: to turn it off, disable or uninstall the plugin.
318
+
319
+ Before 0.1.3 the two skills were named `avouch-cli` and `aquery-scan`.
320
+
321
+ The skills run the CLI with `npx -y avouch@<version>`, pinned to the version of this repository. Set `AVOUCH_CLI` to a built `dist/cli/avouch.js` to use a local build instead. The `prepare` script builds `dist/` when npm installs the package from git (`npx -y github:githonllc/avouch`). `skills/scan/fixture/` is the test project for the skill.
322
+
323
+ ## Publishing
324
+
325
+ The npm package is `avouch`, and its command is `avouch`. Versions 0.1.0 to 0.1.21 were published as `@githonllc/avouch`, which is deprecated. A library user changes imports from `@githonllc/avouch` to `avouch`. Versions 1.0.1 to 1.0.7 of `avouch` are an unrelated earlier package from 2020 by another author. The name was transferred to Githon LLC in 2026. Those versions sort higher than 0.x, so `release.yml` publishes with `--tag latest`.
326
+
327
+ `package.json` has `"private": true` in the repository, so an accidental `npm publish` from a working copy is refused. The release workflow removes that line only in its own checkout.
328
+
329
+ A release needs the explicit approval of the project owner. Releases are published by the GitHub Actions workflow `.github/workflows/release.yml` through npm trusted publishing: no npm token is stored, and npm adds provenance. Do these steps in this sequence:
330
+
331
+ 1. Set the new `0.x` version in `package.json`, `.claude-plugin/plugin.json` and the `npx -y avouch@<version>` pins in `skills/*/SKILL.md`, merge it to `main`, and wait for CI to pass. Check the file list that the CI step `npm pack --dry-run` prints.
332
+ 2. Tag the merge commit `v<version>` (the same version as `package.json`) and push the tag. The workflow checks that the tag matches the version, runs `npm ci`, the type check and the tests, removes `"private"`, and publishes with `--access public --tag latest`.
333
+ 3. The job waits in the `npm` environment until the owner approves it in GitHub. The owner's approval is the release approval.
334
+ 4. Make sure that the release works:
335
+ - `npm view avouch version` shows the new version (a new version can take a few minutes to become available);
336
+ - in an empty directory, `npx -y avouch@<version>` (no arguments) prints the usage lines and exits with code 2.
337
+
338
+ Version 0.1.0 was published by hand, because npm accepts a trusted publisher only for a package that exists.
339
+
340
+ CI and the release workflow run `scripts/scan-private.mjs`. It scans the path and text of each tracked file, and the commit messages of a pull request, for private content: local paths, tokens, private keys, and the patterns in the repository secret `PRIVATE_PATTERNS`. A hit shows only the file and line (or commit) and a pattern label, never the text. Pull requests from forks do not get the secret, so they run with the built-in patterns only. Pushes to `main`, pull requests from this repository and releases fail if the secret is missing.
341
+
342
+ The secret is generated from a list kept in a private repository; maintainers never edit it by hand. After the list changes, a maintainer runs the generator, which prints a pattern count and a sha256 fingerprint, and re-sets the secret with `gh secret set PRIVATE_PATTERNS`. The scan prints `PRIVATE_PATTERNS: <n> patterns, sha256 <12 hex>` for the patterns it loaded (comment lines dropped, pattern lines joined by `\n`); the next CI run on `main` must show the same count and fingerprint as the generator.
343
+
344
+ ## Versions and identifiers
345
+
346
+ The ontology file has `formatVersion: 1`. The schema `$id` is `https://w3id.org/avouch/v1/schema.json`. The version rules are in SPEC section 8.
347
+
348
+ The RDF vocabulary is `vocab.ttl`, namespace `https://w3id.org/avouch/v1/vocab#` (prefix `ofv:`), served at `https://avouch.dev/v1/vocab.ttl`. An RDF export of an ontology (for example the Turtle export of Avouch Studio) names each field with the term of the same name. The check does not read the vocabulary. A change that adds a schema field adds its term in the same change: `test/vocab.test.ts` fails on a field without a term, except for the top-level sections, expressions (exported as one JSON literal), the `properties` of an object type (OWL properties) and the `ref` of a value type (the class itself); each exclusion has its reason in the test.
349
+
350
+ ## License
351
+
352
+ Copyright 2026 Githon LLC. Licensed under the Apache License 2.0. See `LICENSE` and `NOTICE`. The next section gives the exception.
353
+
354
+ ## Third-party text
355
+
356
+ SPEC.md section 2 quotes seven short definitions from the Palantir Foundry documentation (https://www.palantir.com/docs/foundry/ontology/overview/). These quotations belong to their authors. The Apache License 2.0 of this project does not apply to them.