Skip to content

0.4.0 Tooling API: legaldown.grammar, legaldown.syntax, and the answers tools re-derive today - #98

Merged
dvejsada merged 7 commits into
mainfrom
claude/jolly-johnson-e3yr9u
Oct 3, 2026
Merged

dvejsada merged 7 commits into
mainfrom
claude/jolly-johnson-e3yr9u

Conversation

@dvejsada

@dvejsada dvejsada commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Downstream tools (PactTrack, legaldown-render) import about 45 names from private modules, and re-derive what the validator already decides. This gives them a documented public surface for 0.4.0 that is covered by semver. Where the library already knows the answer, it now hands it over, so tools need fewer helpers.

No public behaviour changes. Every deep import path PactTrack and legaldown-render use keeps working. The new modules re-export the validator's own objects (grammar.X is validator.patterns.X), and the top-level legaldown.__all__ does not grow. Three internal names were renamed or dropped. Neither downstream repo uses them, and none of them was ever exported:

  • the old internal validator.templates.Blank accumulator is now _BlankState;
  • templates._DRAFTING_MARKER is now the public DRAFTING_MARKER;
  • parser.iter_directives and parser.text_fragments were incidental imports and are gone.

Closes #31, closes #32, closes #33, closes #34, closes #88, closes #93. Covers the block and item lines of #27; Diagnostic.line already shipped in #85.

New public modules

legaldown.grammar: the language's constants and rules that need no document.

legaldown.syntax: reading source the way the validator does.

Answers in the result and the model

Docs

The README gains a Tooling API section with both modules, examples, and a migration table from each private path to its public home. The old renderer-helpers table now points to that section. The result table documents blanks and alternative.

Verification

  • 2961 tests pass with the spec fixtures corpus, 2210 without it. ruff is clean.

  • tests/test_tooling_surface.py pins both __all__ lists, checks that every name is the implementation's own object, and maps each private import PactTrack and legaldown-render use today to its public home.

  • Differential check against main: all 160 spec fixture documents, validated in normal and final mode, give byte-identical results. That covers every diagnostic (rule, level, message, line), the section index, the placed markers and collect_source_directives.

  • An independent review fuzzed the new code. All of these held:

    • 125k directives: a span always replaced exactly that value;
    • 100k inputs for Lexed.literals;
    • code_content against a CommonMark parser;
    • line_of on 3000 random nested-list documents;
    • drafting_note_blocks against legaldown-render's current behaviour.

    Its should-fix findings are addressed in d42dc01.

🤖 Generated with Claude Code

https://claude.ai/code/session_013WmBAc5T7UCKVdpUmg9qxz

claude added 6 commits October 3, 2026 04:39
Two new public modules that re-export, as the same objects, what
downstream tools imported from private modules: the language's constants
and rules that need no document (grammar), and the lexer, markers,
fragments and Markdown helpers for reading source (syntax). Nothing moves
and no diagnostic changes.

New constants, defined where they are used: PARTY_TYPES and
MAX_SECTION_LEVEL (validator.patterns, read by core and units),
DRAFTING_MARKER (validator.templates, formerly private) and
FINAL_CHECK_RULES (validator.core, read by the final check).

slugify_identifier takes a keyword-only fallback= for text that yields no
identifier. choose_problem(directive, questions) gives the first
choose-invalid message for one directive; check_choose is built on the
same logic. find_markers(document) defaults to the package lexer.

Guard test tests/test_tooling_surface.py pins both __all__ lists, the
identity of every re-export, and the public home of each deep import
PactTrack and legaldown-render use today. README gains a Tooling API
section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013WmBAc5T7UCKVdpUmg9qxz
DocumentIndex.blanks (public Blank) and SectionIndexEntry.alternative hand over
what validate already decides; Directive.positional_span/param_spans and
Lexed.literals say where values and literal regions are; iter_document_directives
yields every directive with its location, and collect_source_directives is built
on it. The validator's internal accumulator Blank is now _BlankState.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013WmBAc5T7UCKVdpUmg9qxz
…tent, SourceLayout, Document.layout()/line_of() (#88, #93, #27)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013WmBAc5T7UCKVdpUmg9qxz
legaldown.syntax now also re-exports iter_document_directives and
DirectiveLocation (#33), quote_blocks and drafting_note_blocks (#88,
#93), code_content and CodeContent (#93), and the source-layout spans
behind Document.layout() (#93, #27). The guard test covers them, and
maps parser.quote_content and parser._layout to their public homes.

README: result.index.blanks and SectionIndexEntry.alternative (#31) in
the result table; the renderer helper table is replaced by a pointer to
the Tooling API section, which gains the new names, a "where things are
in the file" part, and migration rows. legaldown.validator names are no
longer listed as private.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013WmBAc5T7UCKVdpUmg9qxz
@dvejsada dvejsada added the ci label Oct 3, 2026 — with Claude
- iter_document_directives / collect_source_directives: a ref, term or
  definition block built in code whose value holds a line break no longer
  raises; its directive is built directly (main's results again).
- iter_document_directives also yields the {{def:}} lifted into a
  definition block (fragment None), as its docstring promised.
- Blank: type is None when no occurrence has a usable type (invalid
  type, decision question's id); new `consistent` is False for mixed
  types or two currencies/units, and `fixed` is "" then.
- syntax.body_layout(text): the layout of bare body text (a leading ---
  is a rule, not frontmatter), for tools that laid out editor fields
  with the private parser._layout.
- syntax re-exports is_drafting_note; README mentions is_template and
  is_drafting_note again.
- choose_problem raises ValueError for a directive that is not a
  well-formed {{choose:}}.
- Docstrings: layout spans (end exclusive, blank lines between parts
  belong to none), quote helpers past MAX_QUOTE_DEPTH and depth=,
  SectionIndexEntry.alternative (its preceding sibling).
- FINAL_CHECK_RULES moves to validator.patterns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013WmBAc5T7UCKVdpUmg9qxz
@dvejsada
dvejsada merged commit 93fd4bf into main Oct 3, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment