/**
 * Application-level services for business logic evaluation and document processing orchestration.
 * <p>
 * This package contains stateless, pure-logic services that evaluate document content,
 * apply business rules, and orchestrate the per-document processing flow.
 * Services in this package:
 * <ul>
 *   <li>Do not manage state or resources</li>
 *   <li>Do not depend on infrastructure (database, filesystem, network) directly;
 *       they interact with infrastructure exclusively through outbound ports</li>
 *   <li>Can be tested with simple unit tests and in-memory mocks</li>
 * </ul>
 *
 * <h2>Current services</h2>
 * <ul>
 *   <li>{@link de.gecheckt.pdf.umbenenner.application.service.PreCheckEvaluator}
 *       — Pre-check evaluation logic</li>
 *   <li>{@link de.gecheckt.pdf.umbenenner.application.service.DocumentProcessingService}
 *       — Complete document processing pipeline orchestration</li>
 *   <li>{@link de.gecheckt.pdf.umbenenner.application.service.DocumentProcessingCoordinator}
 *       — Per-document idempotency, status/counter mapping and consistent
 *       two-level persistence</li>
 *   <li>{@link de.gecheckt.pdf.umbenenner.application.service.AiRequestComposer}
 *       — Deterministic composition of AI request representations from prompt and document text</li>
 *   <li>{@link de.gecheckt.pdf.umbenenner.application.service.TitleCharacterRule}
 *       — Canonical character-allowlist rule for document titles (single authoritative implementation)</li>
 * </ul>
 *
 * <h2>Document processing flow ({@code DocumentProcessingCoordinator})</h2>
 * <p>
 * The {@link de.gecheckt.pdf.umbenenner.application.service.DocumentProcessingCoordinator}
 * implements the required processing order per candidate:
 * <ol>
 *   <li>Load the document master record by fingerprint.</li>
 *   <li>If overall status is {@code SUCCESS} → persist a skip attempt with
 *       {@code SKIPPED_ALREADY_PROCESSED}; do not change counters.</li>
 *   <li>If overall status is {@code FAILED_FINAL} → persist a skip attempt with
 *       {@code SKIPPED_FINAL_FAILURE}; do not change counters.</li>
 *   <li>Otherwise map the extraction outcome into status, counters and retryable flag
 *       using the minimal rules.</li>
 *   <li>Persist exactly one historised processing attempt.</li>
 *   <li>Persist the updated document master record.</li>
 * </ol>
 *
 * <h2>Processing rules (status and counter semantics)</h2>
 * <ul>
 *   <li>First deterministic content error → {@code FAILED_RETRYABLE},
 *       content error counter +1, {@code retryable=true}.</li>
 *   <li>Second deterministic content error → {@code FAILED_FINAL},
 *       content error counter +1 (cumulative = 2), {@code retryable=false}.</li>
 *   <li>Technical error after fingerprinting → {@code FAILED_RETRYABLE},
 *       transient error counter +1, {@code retryable=true}.</li>
 *   <li>Skip events do not change any failure counter.</li>
 * </ul>
 *
 * <h2>Persistence consistency</h2>
 * <p>
 * For every identified document, the processing attempt and the master record are
 * written in sequence. If either write fails, the failure is caught and logged;
 * the batch run continues with the next candidate. True transactionality across
 * two separate repository calls is not available due to the two-level persistence design; this is a known
 * and documented limitation.
 *
 * <h2>Pre-fingerprint failures</h2>
 * <p>
 * Failures that occur before a successful fingerprint is available are not handled
 * by this package. They are handled by the use case and are not historised in SQLite.
 */
package de.gecheckt.pdf.umbenenner.application.service;
