legaldown-validator 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- legaldown_validator-0.1.0/.gitignore +20 -0
- legaldown_validator-0.1.0/CONFORMANCE.md +71 -0
- legaldown_validator-0.1.0/LICENSE +29 -0
- legaldown_validator-0.1.0/PKG-INFO +308 -0
- legaldown_validator-0.1.0/README.md +272 -0
- legaldown_validator-0.1.0/pyproject.toml +89 -0
- legaldown_validator-0.1.0/src/legaldown/__init__.py +122 -0
- legaldown_validator-0.1.0/src/legaldown/cli.py +185 -0
- legaldown_validator-0.1.0/src/legaldown/definitions.py +206 -0
- legaldown_validator-0.1.0/src/legaldown/models.py +384 -0
- legaldown_validator-0.1.0/src/legaldown/parser.py +280 -0
- legaldown_validator-0.1.0/src/legaldown/py.typed +0 -0
- legaldown_validator-0.1.0/src/legaldown/serializer.py +200 -0
- legaldown_validator-0.1.0/src/legaldown/validator/__init__.py +74 -0
- legaldown_validator-0.1.0/src/legaldown/validator/core.py +808 -0
- legaldown_validator-0.1.0/src/legaldown/validator/helpers.py +83 -0
- legaldown_validator-0.1.0/src/legaldown/validator/patterns.py +70 -0
- legaldown_validator-0.1.0/src/legaldown/validator/result.py +79 -0
- legaldown_validator-0.1.0/tests/conformance/__init__.py +0 -0
- legaldown_validator-0.1.0/tests/conformance/test_legaldown_fixtures.py +156 -0
- legaldown_validator-0.1.0/tests/test_cli.py +100 -0
- legaldown_validator-0.1.0/tests/test_spec_alignment.py +320 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
.venv/
|
|
8
|
+
venv/
|
|
9
|
+
.pytest_cache/
|
|
10
|
+
.ruff_cache/
|
|
11
|
+
.coverage
|
|
12
|
+
coverage.xml
|
|
13
|
+
|
|
14
|
+
# Spec fixtures checkout used by the conformance harness in CI
|
|
15
|
+
.legaldown-spec/
|
|
16
|
+
|
|
17
|
+
# Editors / OS
|
|
18
|
+
.vscode/
|
|
19
|
+
.idea/
|
|
20
|
+
.DS_Store
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Conformance
|
|
2
|
+
|
|
3
|
+
`legaldown-validator` implements **Level 1 — Core** of the LegalDown specification (§16.2): parse
|
|
4
|
+
and validate a single document in memory.
|
|
5
|
+
|
|
6
|
+
It is verified against the specification's own
|
|
7
|
+
[fixtures corpus](https://github.com/ForLegalAI/LegalDown/tree/main/fixtures) — one case per
|
|
8
|
+
validation rule, paired with the diagnostic a conforming validator must produce. **57 of the
|
|
9
|
+
corpus's 95 rules are implemented, and every one the corpus can exercise at Core level passes.**
|
|
10
|
+
|
|
11
|
+
Seven implemented rules — `amend-def-override`, `amend-term-undefined`, `amend-term-unresolvable`,
|
|
12
|
+
`attachment-id-collision`, `attachment-id-duplicate`, `attachment-title-empty`, and
|
|
13
|
+
`attachment-unreferenced` — have fixtures that span several files or are marked for the Full level,
|
|
14
|
+
so this single-document harness skips them. They are covered by the unit tests in
|
|
15
|
+
`tests/test_spec_alignment.py` instead.
|
|
16
|
+
|
|
17
|
+
The specification (§16.5) requires an implementation never to silently skip a check it cannot
|
|
18
|
+
perform, so the remaining rules are named here. Most of them belong to conformance levels this
|
|
19
|
+
implementation does not claim — Rendering (§16.3) and Full (§16.4) — because they need to open
|
|
20
|
+
files other than the document itself.
|
|
21
|
+
|
|
22
|
+
## Rules not implemented
|
|
23
|
+
|
|
24
|
+
| Group | Rules |
|
|
25
|
+
|---|---|
|
|
26
|
+
| Filesystem-dependent (Full, §16.4) | `amends-file-missing`, `attachment-file-missing`, `attachment-has-frontmatter`, `attachment-has-h1`, `attachment-anchor-duplicate`, `supersedes-file-missing`, `path-not-relative`, `path-outside-root` |
|
|
27
|
+
| Includes (Full, §16.4) | `include-file-missing`, `include-not-legaldown`, `include-cycle`, `include-has-frontmatter`, `include-has-h1`, `include-anchor-duplicate`, `include-heading-skip` |
|
|
28
|
+
| Bilingual sets (Full, §16.4) | `translation-file-missing`, `translation-hierarchy-mismatch`, `translation-anchor-mismatch`, `translation-def-mismatch`, `translation-language-set-mismatch`, `translation-implicit-id`, `translation-authoritative-absent` |
|
|
29
|
+
| Lexer-level grammar (§11.2–11.4) | `directive-malformed`, `directive-duplicate-param`, `directive-unknown-param`, `brace-stray`, `value-curly-quote`, `raw-html`, `anchor-misplaced` |
|
|
30
|
+
| Other | `frontmatter-absent`, `frontmatter-invalid-yaml` (reported by the CLI, not the validator), `anchor-lossy-slug`, `def-lossy-slug`, `definition-circular`, `definition-used-before-declaration`, `language-code-invalid`, `authoritative-not-declared`, `supersedes-title-empty` |
|
|
31
|
+
|
|
32
|
+
In practice this means multi-file processing is out of scope: includes, attachment file contents,
|
|
33
|
+
and bilingual document sets are not resolved or cross-checked. Single-document authoring, editing,
|
|
34
|
+
and CI validation are fully covered.
|
|
35
|
+
|
|
36
|
+
One id appears on both sides of that line. `attachment-file-missing` is defined as *the attachment
|
|
37
|
+
`file` path exists*, which needs the filesystem and is therefore unimplemented — but the validator
|
|
38
|
+
also emits that id when an attachment declares no `file` at all, which is visible in the document
|
|
39
|
+
itself. The specification provides no separate id for the absent key, so a diagnostic carrying
|
|
40
|
+
`attachment-file-missing` from this implementation always means the key is missing, never that the
|
|
41
|
+
path failed to resolve.
|
|
42
|
+
|
|
43
|
+
## Checking this yourself
|
|
44
|
+
|
|
45
|
+
The conformance harness runs against a checkout of the specification repository:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
git clone https://github.com/ForLegalAI/LegalDown ../LegalDown
|
|
49
|
+
LEGALDOWN_FIXTURES_DIR=../LegalDown/fixtures pytest tests/conformance -q
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Cases for the rules above are skipped by name, so the 38 `not implemented` skips reproduce this
|
|
53
|
+
table one for one. The run reports 45 skips in total: the remaining seven are the implemented
|
|
54
|
+
rules named above, skipped as `multi-file case` or `requires conformance level full`. CI runs this
|
|
55
|
+
on every push and pull request.
|
|
56
|
+
|
|
57
|
+
## Declaring conformance in code
|
|
58
|
+
|
|
59
|
+
The package exports what it targets, so consumers can assert it:
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
import legaldown
|
|
63
|
+
|
|
64
|
+
legaldown.SPEC_VERSION # "0.1" — specification version targeted
|
|
65
|
+
legaldown.CONFORMANCE_LEVEL # "core" — conformance level claimed
|
|
66
|
+
legaldown.__version__ # implementation version
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The CLI's JSON output repeats the first and last of these on every run, as `legaldown_spec` and
|
|
70
|
+
`validator_version`. The conformance level is not part of the JSON payload — read it from the
|
|
71
|
+
package.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ForLegalAI s.r.o.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
The LegalDown specification itself is a separate work, published at
|
|
26
|
+
https://github.com/ForLegalAI/LegalDown under Creative Commons Attribution 4.0
|
|
27
|
+
International (CC BY 4.0). This implementation is an independent work that
|
|
28
|
+
implements that specification; the specification permits implementations to be
|
|
29
|
+
released under any license their authors choose.
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: legaldown-validator
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Reference parser and validator for the LegalDown legal document format
|
|
5
|
+
Project-URL: Homepage, https://github.com/ForLegalAI/legaldown-validator
|
|
6
|
+
Project-URL: Repository, https://github.com/ForLegalAI/legaldown-validator
|
|
7
|
+
Project-URL: Issues, https://github.com/ForLegalAI/legaldown-validator/issues
|
|
8
|
+
Project-URL: Specification, https://github.com/ForLegalAI/LegalDown
|
|
9
|
+
Author: ForLegalAI s.r.o.
|
|
10
|
+
Maintainer: ForLegalAI s.r.o.
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: contracts,document-format,legal,legaldown,legaltech,markdown,parser,validator
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Environment :: Console
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Intended Audience :: Legal Industry
|
|
18
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
25
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
26
|
+
Classifier: Topic :: Text Processing :: Markup
|
|
27
|
+
Classifier: Typing :: Typed
|
|
28
|
+
Requires-Python: >=3.11
|
|
29
|
+
Requires-Dist: pyyaml>=6.0
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
34
|
+
Requires-Dist: twine>=5.1; extra == 'dev'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
<div align="center">
|
|
38
|
+
|
|
39
|
+
# legaldown-validator 📐
|
|
40
|
+
|
|
41
|
+
### The reference implementation of [LegalDown](https://github.com/ForLegalAI/LegalDown)
|
|
42
|
+
|
|
43
|
+
**Parse, validate, and serialize LegalDown documents — from the command line or from Python.**
|
|
44
|
+
|
|
45
|
+
Targets specification **v0.1** · Every diagnostic carries a **stable rule id** · One dependency: PyYAML
|
|
46
|
+
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## What it does
|
|
52
|
+
|
|
53
|
+
[LegalDown](https://github.com/ForLegalAI/LegalDown) is an open plain-text standard for legal
|
|
54
|
+
documents. This package is the toolkit for working with those documents in code:
|
|
55
|
+
|
|
56
|
+
📥 **Parses** a `.lgd` file into a typed document model — frontmatter, sections, blocks, parties,
|
|
57
|
+
definitions, anchors.
|
|
58
|
+
|
|
59
|
+
✅ **Validates** it against the specification's rule set (§15): broken cross-references, undefined
|
|
60
|
+
terms, duplicate identifiers, malformed party metadata, bad dates and money values, and more.
|
|
61
|
+
|
|
62
|
+
📤 **Serializes** the model back to LegalDown source, so you can edit documents programmatically
|
|
63
|
+
and write them out again.
|
|
64
|
+
|
|
65
|
+
🏷️ **Names every finding.** Each diagnostic carries the specification's stable rule id (§15.1)
|
|
66
|
+
alongside its severity and message, so you can suppress one check, escalate another, or gate a
|
|
67
|
+
build on exactly the rules you care about. Rule ids survive specification renumbering — they are
|
|
68
|
+
the part of a diagnostic that is safe to depend on.
|
|
69
|
+
|
|
70
|
+
## Install
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pip install legaldown-validator
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Python 3.11 or newer. The distribution is named `legaldown-validator`; the import name is
|
|
77
|
+
`legaldown`:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
import legaldown
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The package ships a `py.typed` marker, so type checkers use its annotations directly.
|
|
84
|
+
|
|
85
|
+
## Quick start
|
|
86
|
+
|
|
87
|
+
Given a LegalDown document:
|
|
88
|
+
|
|
89
|
+
```markdown
|
|
90
|
+
---
|
|
91
|
+
title: Services Agreement
|
|
92
|
+
document_type: contract
|
|
93
|
+
sides:
|
|
94
|
+
- name: providers
|
|
95
|
+
label: Provider
|
|
96
|
+
parties:
|
|
97
|
+
- name: acme
|
|
98
|
+
label: Acme
|
|
99
|
+
type: legal_entity
|
|
100
|
+
legal_name: Acme Corporation
|
|
101
|
+
- name: clients
|
|
102
|
+
label: Client
|
|
103
|
+
parties:
|
|
104
|
+
- name: beta
|
|
105
|
+
label: Beta
|
|
106
|
+
type: legal_entity
|
|
107
|
+
legal_name: Beta Industries Inc.
|
|
108
|
+
language: en
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
# Fees {#fees}
|
|
112
|
+
|
|
113
|
+
{{party: beta}} shall pay {{money: 5000}} under {{ref: payment-terms}}.
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Validate it:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
legaldown validate contract.lgd
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
contract.lgd: error: [ref-broken] Broken section reference: 'payment-terms'.
|
|
124
|
+
contract.lgd: warning: [money-missing-currency] Money directive without currency parameter.
|
|
125
|
+
|
|
126
|
+
1 error(s), 1 warning(s), 0 info(s)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
One diagnostic per line, each prefixed with its rule id. A clean document reports
|
|
130
|
+
`No issues found.` and exits `0`.
|
|
131
|
+
|
|
132
|
+
Diagnostics go to **stdout**; the trailing summary and `No issues found.` go to **stderr**, so
|
|
133
|
+
`legaldown validate contracts/ > report.txt` captures the findings alone. `--quiet` drops the
|
|
134
|
+
summary entirely.
|
|
135
|
+
|
|
136
|
+
## Command line
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
legaldown validate contract.lgd # one document
|
|
140
|
+
legaldown validate contracts/ # a whole directory, recursively
|
|
141
|
+
legaldown validate --format json contract.lgd # machine-readable output
|
|
142
|
+
legaldown validate --ignore def-unreferenced doc.lgd # mute one rule
|
|
143
|
+
legaldown validate --warnings-as-errors doc.lgd # tighten a CI gate
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
| Option | Effect |
|
|
147
|
+
|---|---|
|
|
148
|
+
| `--format {text,json}` | Output format (default `text`) |
|
|
149
|
+
| `--ignore RULE_ID` | Suppress a rule by its stable id — repeatable |
|
|
150
|
+
| `--warnings-as-errors` | Report warnings at error severity |
|
|
151
|
+
| `--strict` | Exit non-zero on any diagnostic, not just errors |
|
|
152
|
+
| `--quiet` | Drop the trailing summary line |
|
|
153
|
+
|
|
154
|
+
Directories are searched recursively for `*.lgd`, `*.legaldown`, and `*.legal.md`, and
|
|
155
|
+
`legaldown --version` reports the validator version.
|
|
156
|
+
|
|
157
|
+
**Exit codes:** `0` clean · `1` diagnostics found (errors, or any diagnostic under `--strict`) ·
|
|
158
|
+
`2` a file could not be read.
|
|
159
|
+
|
|
160
|
+
### JSON output
|
|
161
|
+
|
|
162
|
+
`--format json` emits the structured shape described in §15.9 — ideal for CI annotations, editor
|
|
163
|
+
integrations, and dashboards:
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
{
|
|
167
|
+
"legaldown_spec": "0.1",
|
|
168
|
+
"validator_version": "0.1.0",
|
|
169
|
+
"diagnostics": [
|
|
170
|
+
{
|
|
171
|
+
"file": "contract.lgd",
|
|
172
|
+
"rule": "ref-broken",
|
|
173
|
+
"level": "error",
|
|
174
|
+
"message": "Broken section reference: 'payment-terms'."
|
|
175
|
+
}
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### In CI
|
|
181
|
+
|
|
182
|
+
```yaml
|
|
183
|
+
- run: pip install legaldown-validator
|
|
184
|
+
- run: legaldown validate contracts/ --warnings-as-errors
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
The exit code fails the job; the rule ids let you grant exceptions without turning whole checks
|
|
188
|
+
off.
|
|
189
|
+
|
|
190
|
+
## Python API
|
|
191
|
+
|
|
192
|
+
Three functions cover the common path:
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
from legaldown import parse_document, validate_document, serialize_document
|
|
196
|
+
|
|
197
|
+
document = parse_document(open("contract.lgd").read(), filename="contract.lgd")
|
|
198
|
+
result = validate_document(document)
|
|
199
|
+
|
|
200
|
+
for diagnostic in result.diagnostics:
|
|
201
|
+
print(diagnostic.level, diagnostic.rule, diagnostic.message)
|
|
202
|
+
|
|
203
|
+
if result.is_valid: # no Error-level diagnostics
|
|
204
|
+
print(serialize_document(document))
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Working with the result
|
|
208
|
+
|
|
209
|
+
Validating a document builds the indices the checks need — section numbers, resolved definitions,
|
|
210
|
+
party display text, every inline value found in the body. `ValidationResult` hands all of it back,
|
|
211
|
+
so a renderer or a UI can reuse the work instead of re-deriving it:
|
|
212
|
+
|
|
213
|
+
| Attribute | Contents |
|
|
214
|
+
|---|---|
|
|
215
|
+
| `diagnostics` | `Diagnostic(rule, level, message)` — the authoritative record |
|
|
216
|
+
| `is_valid` | `True` when no Error-level diagnostic was reported |
|
|
217
|
+
| `errors` / `warnings` / `infos` | Message strings by severity |
|
|
218
|
+
| `rules(level=None)` | Set of rule ids present, optionally filtered by severity |
|
|
219
|
+
| `sections`, `section_lookup` | Numbered section index; resolves `{{ref:}}` targets |
|
|
220
|
+
| `definition_lookup`, `party_lookup`, `side_lookup`, `attachment_lookup` | Resolved display text |
|
|
221
|
+
| `inline_dates`, `inline_money`, `inline_durations`, `inline_fields`, `inline_placeholders` | Field-spec values found in the body |
|
|
222
|
+
|
|
223
|
+
### Reading and editing the document model
|
|
224
|
+
|
|
225
|
+
`parse_document` returns a `Document` of plain dataclasses — `Metadata`, `Section`, `Block`,
|
|
226
|
+
`Side`, `Party`, `Attachment` — that you can inspect, edit, and write back out:
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
from legaldown import parse_document, serialize_document
|
|
230
|
+
|
|
231
|
+
document = parse_document(source)
|
|
232
|
+
document.metadata.governing_law = "Czech Republic"
|
|
233
|
+
|
|
234
|
+
with open("contract.lgd", "w", encoding="utf-8") as handle:
|
|
235
|
+
handle.write(serialize_document(document))
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
`document_to_dict()` / `document_from_dict()` round-trip the model through JSON-friendly
|
|
239
|
+
structures, and `render_block()` renders a single block when you are driving your own layout.
|
|
240
|
+
|
|
241
|
+
One guarantee worth knowing: the parser is **faithful** — it never rewrites your input to make it
|
|
242
|
+
valid, so what you authored is exactly what the validator judges. The serializer, by contrast,
|
|
243
|
+
normalizes: frontmatter is re-emitted as canonical YAML and paragraphs are written as single
|
|
244
|
+
lines, so expect a formatting-normalized file rather than a byte-for-byte copy.
|
|
245
|
+
|
|
246
|
+
## What gets checked
|
|
247
|
+
|
|
248
|
+
The full rule set with severities and examples lives in the specification (§15); this is the map:
|
|
249
|
+
|
|
250
|
+
| Area | Checks include |
|
|
251
|
+
|---|---|
|
|
252
|
+
| **Structure** | Heading depth and skipped levels, hardcoded section numbers, missing title |
|
|
253
|
+
| **Cross-references** | `{{ref:}}` targets that do not exist or point at an attachment |
|
|
254
|
+
| **Anchors** | Duplicate identifiers, malformed identifiers, auto-generated collisions |
|
|
255
|
+
| **Definitions** | Undefined `{{term:}}`, duplicate ids, missing quoted span, ambiguous quoting, unreferenced definitions |
|
|
256
|
+
| **Parties and sides** | Unknown `{{party:}}` / `{{side:}}`, malformed or duplicate names, invalid party types, minimum party and side counts, empty representatives |
|
|
257
|
+
| **Values** | Invalid dates, money without currency or with an unknown one, invalid durations and units, undeclared or reserved custom field types |
|
|
258
|
+
| **Placeholders** | Malformed ids, invalid or inconsistent types, placeholders in structural fields |
|
|
259
|
+
| **Attachments** | Undeclared `{{attach:}}`, duplicate or colliding ids, empty titles, unreferenced attachments |
|
|
260
|
+
| **Amendments** | Terms the amended original does not define, definition overrides, empty amendment titles |
|
|
261
|
+
| **Metadata** | Invalid document type, invalid dates, missing sides, issuer side requirements |
|
|
262
|
+
|
|
263
|
+
Each check reports at the severity the specification assigns it — Error, Warning, or Info.
|
|
264
|
+
|
|
265
|
+
## Scope
|
|
266
|
+
|
|
267
|
+
This implementation claims **Level 1 — Core** (§16.2): everything above applies to a single
|
|
268
|
+
document, in memory, with no filesystem access beyond reading the file you point it at. That
|
|
269
|
+
covers authoring, editing, and CI validation of individual documents.
|
|
270
|
+
|
|
271
|
+
It is verified against the specification's own
|
|
272
|
+
[fixtures corpus](https://github.com/ForLegalAI/LegalDown/tree/main/fixtures) — every rule it
|
|
273
|
+
implements passes, bar seven whose fixtures span several files and are covered by unit tests
|
|
274
|
+
instead. The specification (§16.5) requires an implementation to be explicit about the
|
|
275
|
+
checks it does not perform, so those are listed in
|
|
276
|
+
[CONFORMANCE.md](https://github.com/ForLegalAI/legaldown-validator/blob/main/CONFORMANCE.md) rather than left to be discovered.
|
|
277
|
+
|
|
278
|
+
## Development
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
git clone https://github.com/ForLegalAI/legaldown-validator
|
|
282
|
+
cd legaldown-validator
|
|
283
|
+
pip install -e ".[dev]"
|
|
284
|
+
pytest
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Conformance suite
|
|
288
|
+
|
|
289
|
+
The fixtures corpus lives in the specification repository, so point the harness at a checkout:
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
git clone https://github.com/ForLegalAI/LegalDown ../LegalDown
|
|
293
|
+
LEGALDOWN_FIXTURES_DIR=../LegalDown/fixtures pytest tests/conformance -q
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Cases for rules outside Core are skipped and named, so the run doubles as the coverage ledger in
|
|
297
|
+
[CONFORMANCE.md](https://github.com/ForLegalAI/legaldown-validator/blob/main/CONFORMANCE.md),
|
|
298
|
+
which accounts for every skip. CI runs it on every push and pull request.
|
|
299
|
+
|
|
300
|
+
Bug reports and pull requests are welcome in
|
|
301
|
+
[Issues](https://github.com/ForLegalAI/legaldown-validator/issues); questions about the format
|
|
302
|
+
itself belong in the specification repository's
|
|
303
|
+
[Discussions](https://github.com/ForLegalAI/LegalDown/discussions).
|
|
304
|
+
|
|
305
|
+
## License
|
|
306
|
+
|
|
307
|
+
MIT — see [LICENSE](https://github.com/ForLegalAI/legaldown-validator/blob/main/LICENSE). The LegalDown specification itself is published separately under
|
|
308
|
+
CC BY 4.0; it permits implementations to choose their own license.
|