Skip to content

Move third-party sources, tighten encapsulation, extend Konsist rules - #4

Merged
lemcoder merged 2 commits into
mainfrom
architecture-cleanup
Aug 12, 2026
Merged

Move third-party sources, tighten encapsulation, extend Konsist rules#4
lemcoder merged 2 commits into
mainfrom
architecture-cleanup

Conversation

@lemcoder

Copy link
Copy Markdown
Owner

Two commits.

1. Move vendored checkouts under third-party/

markitdown and anydoc are reference sources for the benchmark, not part of the build. third-party/markitdown and third-party/anydoc now; the repository root is this project's own layout again. scripts/benchmark.py also picks up a cargo build --release inside third-party/anydoc.

2. Encapsulation, and rules that keep it

The duplication that kept biting. JVM and Android held nine byte-identical converter files. They drifted twice during recent work — once in EpubConverter, once in PptxConverter — and nothing caught it either time. A jvmShared source set now holds every converter both targets run. Only PdfConverter (pdfbox vs pdfbox-android) and the platform factories remain per-target.

library/src/
  commonMain/    model, render, pipeline, plain-text converters
  jvmShared/     csv json xml html epub docx xlsx pptx + HtmlToDocument   ← was duplicated
  jvmMain/       PdfConverter, TikaMimeDetector, factory
  androidMain/   PdfConverter, AndroidMimeDetector, factory

Explicit API mode. The library now builds with explicitApi(), so every exported declaration states its visibility and return type. This surfaced several helpers that were public only because nobody wrote a modifier: HtmlToDocument, plainTextBlocks, TikaMimeDetector and AndroidMimeDetector are all internal now.

Smaller cleanups.

  • converter selection moved into a ConverterRegistry that sorts on registration and never exposes its entries, replacing a Pair<DocumentConverter, Double> list re-sorted on every parse
  • DocumentBuilder.title is private behind titleIfAbsent() / title()
  • both builders carry a @DocumentDsl marker, so an inlines { } block cannot silently call document-level methods

Six new Konsist rules (11 total), each verified to fail against a planted violation before being committed:

rule catches
helpers under utils are never public implementation detail leaking into the API
the model exposes no mutable state var on an exported model type
every DocumentConverter is named *Converter and lives in converters converters growing outside their package
converters do not import each other conversion chaining behind the pipeline's back
no wildcard imports, no printing from library code hygiene
no production file in two source sets the duplication above, coming back

The last one names the two legitimately platform-specific files explicitly, so adding a third requires saying so out loud.

Verification

./gradlew check passes: JVM tests, Android host tests, CLI build, detekt, ktfmtCheck, and all 11 architecture rules.

Converted output is byte-identical across all eight fixtures — this refactor moves code without changing behaviour.

🤖 Generated with Claude Code

lemcoder and others added 2 commits August 11, 2026 22:57
markitdown and anydoc are reference sources for the benchmark, not part of the
build. Grouping them keeps the repository root to this project's own layout.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The JVM and Android source sets held nine byte-identical converter files. They
drifted twice during recent work, each time silently, because nothing checked
them against each other.

- a jvmShared source set now holds every converter that both targets run; only
  PdfConverter (pdfbox vs pdfbox-android) and the factory stay per-target
- explicit API mode: every exported declaration states visibility and return
  type, which turned up helpers that were public only by omission —
  HtmlToDocument, plainTextBlocks and both MIME detectors are now internal
- converter selection moved into a ConverterRegistry that sorts on registration
  and never hands out its entries, instead of a Pair list re-sorted per parse
- DocumentBuilder's title is private behind titleIfAbsent()/title(), and the
  builders carry a @DocumentDsl marker so inline scopes cannot reach
  document-level methods

Six new Konsist rules, each verified to fail against a planted violation:

- helpers under utils are never public
- the model exposes no mutable state
- every DocumentConverter is named *Converter and lives in converters
- converters do not import each other
- no wildcard imports, no printing from library code
- no production file exists in two source sets, with the two platform-specific
  files named explicitly, so the duplication cannot come back unnoticed

Converted output is byte-identical across all eight fixtures.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lemcoder
lemcoder merged commit 0caf3cd into main Aug 12, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant