Skip to main content

M18 — Legal case files

State: to do — Depends on: M07, M09, M12, M14, M15, M16 — The AdCodicem.Pdf.CaseFile satellite (ADR 9, ADR 24); every resource through the resolver of ADR 38; the inventory's page numbers by the passes of ADR 39

Goal​

Build a case file from one model — numbered pieces, their titles, dates and sources — and get from it the exhibit stamps, the bookmarks, the page labels, a hyperlinked bordereau de communication de pièces, a JSON export and an integrity manifest, all of which agree because none of them is written by hand; shape the result for the court portal it is going to, by rules kept as data and read from the official texts; turn e-mails into pieces with their attachments; and link every "pièce n° 12" in the pleadings to the piece it names.

M06 and M07 assemble and split; M09 stamps "Pièce n° 2.1" on a page it is told to stamp. None of them knows what a case file is. French practice does: every piece a party relies on is numbered and communicated with a bordereau (Code de procédure civile, art. 132; a bordereau récapitulatif annexed to appeal conclusions, art. 954); before the administrative courts an inventaire détaillé accompanies the pieces (Code de justice administrative, art. R. 412-2), and in Télérecours each piece must be a file of its own or be identified by a bookmark designating it as the inventory does (art. R. 414-3) — on which the Conseil d'État ruled in SA Finamur (5 October 2018). Those texts are cited here as the survey reports them; each is read in its current wording before a preset encodes it. The failures this milestone exists to prevent are the ordinary ones: a bookmark reading "Pièce 12" while the inventory reads "Pièce 12 — Lettre de résiliation du 3 mars 2025"; a piece inserted after communication that renumbers the stamps and not the bordereau; an inventory whose page numbers are all off by one because the inventory itself grew a page; an e-mail printed from Outlook with its attachments gone; a file a portal refuses for its size on the evening of a deadline; a fingerprint nobody can recompute.

Scope​

In:

  • the AdCodicem.Pdf.CaseFile satellite, on AdCodicem.Pdf and AdCodicem.Pdf.Html, as docs/architecture.md places it;
  • the case-file model: the matter, the communicating party and its counsel, successive communications, and pieces — hierarchical numbers with the bis, ter suffixes French practice inserts, titles, dates, sources (a PDF, an image, an HTML fragment, an e-mail), sub-pieces —, with a versioned JSON form and its published schema;
  • one resolution of the model from which every product follows: M09's exhibit stamps and separator pages, M07's bookmarks, M06's page labels and named destinations, the bordereau rendered by M12, its links, the JSON export, the integrity manifest, the document metadata — and a verifier that checks each of them against the model by extraction;
  • outputs: one volume, bordereau first; one file per piece and the bordereau as a file of its own; or both;
  • the bordereau: HTML written by the satellite from the resolved model, rendered by M12, tagged by M13, its lines linked to the pieces; its page column filled by ADR 39's bounded passes where the bordereau's own length shifts what it lists;
  • signed, certified, encrypted and damaged pieces handled by M06, M09 and M16's policies, the signed original kept verifiable;
  • an integrity manifest: SHA-256 of every piece's source as received and of every file produced, embedded as an associated file, summarized in a custom XMP schema (M14), the volume's own digest in an appended update, and a sidecar;
  • court-portal presets — Télérecours, e-Barreau and the RPVA, PLEX — as data: a fixed vocabulary of rules in code, their values in JSON with the source each was read from, each preset verified against the current official texts and guides before it is encoded, as M10 and M14 do for their specifications;
  • e-mail to a piece: EML (RFC 5322 and MIME) and Outlook MSG (MS-OXMSG over MS-CFB), a header block and the body rendered through M12, inline images from the message itself, attachments as associated files or as sub-pieces, nested messages as sub-pieces, the original message kept as the piece's source;
  • references to pieces — "pièce n° 12", "pièces nos 3 à 5", "(pièce 2.1)" — found by M15's search in the pleadings and linked (M11) to the piece's first page, in the volume or across files;
  • the command-line tool's casefile verb.

Out, explicitly:

  • redacting and sanitizing pieces before they enter a case file — M19, which adds its steps to the piece pipeline defined here; until then a caller passes a source it has already processed;
  • OCR of scanned pieces, which a portal may require to be searchable — M22; M18 reports each page without text (M15's page kinds) under the preset's rule;
  • reducing a piece's size beyond splitting it — recompression and downsampling are M23's; until then a piece over a cap is split when the preset allows it and reported otherwise;
  • converting pieces to PDF/A — M21, through the tool's casefile --pdf-a, so that this satellite never depends on AdCodicem.Pdf.Conformance; checking a PDF/A claim beyond reading it — M20; a preset that requires PDF/A reports every piece whose claim is absent, and says it could not validate the others — once M20 exists, it asks the core's IPdfConformanceChecker when the caller has registered one, still without that dependency;
  • signing or sealing the volume, the manifest or a piece, and time-stamping — M26; the manifest is shaped so that a signature over the volume binds it. No claim is made about the copie fiable regime of décret n° 2016-1673: digests make a change detectable, they do not certify a process;
  • Word and office documents as pieces — M31 for DOCX; the others are an open question of the roadmap;
  • sending anything to a court or through the RPVA — never: the library produces files; it holds no credential, opens no connection and talks to no court system;
  • mailbox formats (MBOX, PST, EML collections), S/MIME decryption, DKIM verification — not planned; the EA-PDF archival profile of the PDF Association (2025) — not planned here, though the rendition leaves room for it (see Traps);
  • presets for other jurisdictions — the vocabulary accepts them as data; only the three French ones ship, verified;
  • an authored portfolio as the output — not planned (M07 unpacks received ones).

Design​

Where it lives​

AdCodicem.Pdf.CaseFile
Model/ CaseFileModel, CasePiece, CaseDate, CaseMatter, CaseCommunication, CasePieceSource,
CaseFileJson and its schema
Build/ CaseFileBuilder, ResolvedCaseFile, the page-label and bookmark plans, the passes
Inventory/ CaseInventoryWriter (HTML), the default stylesheet, CaseFileLocale (fr-FR shipped)
Integrity/ CaseIntegrityManifest, the XMP schema, the volume digest
Portals/ CasePortalPreset, the rule vocabulary, the shipped presets as embedded JSON
Email/ the MIME reader, the CFB and MS-OXMSG reader, EmailMessage, EmailToPiece
References/ CasePieceReferenceFinder, CasePieceLinker, the reference patterns as data
Verify/ CaseFileVerifier

The satellite adds no dependency beyond the two packages it builds on. Its parsers — MIME, compound files, LZFu, RTF de-encapsulation — are written here, bounded and fuzzed like the core's, unless an ADR in slice 9 finds that a maintained library (MimeKit for MIME, MsgReader for MSG, both MIT) meets the same bar: Native AOT and trimming without warnings, deterministic output, and every limit below enforceable from outside. Taking one amends the package table of docs/architecture.md.

Every operation that reads or writes takes M03's IProgress<PdfProgress> then CancellationToken, with the stages Analyzing, Rendering, Assembling, Verifying, counted in pieces; every result carries a report of M06's shape.

The model​

CaseFileModel immutable: Matter, Party, Counsel, Communications (ordered), Pieces (ordered, a tree),
Numbering, Locale, Metadata overrides; With… methods for edits
CaseMatter court, chamber, case numbers, the parties as the caller names them — ordered label/value lines,
shown by the bordereau's header as written
CaseCommunication number (1, 2, …), date (supplied, never the clock), whether it is a récapitulatif
CasePiece Number (M09's PdfExhibitNumber, below), Title (the libellé), Date, Source, Communication,
Children, Notes, Tags
CaseDate Day, Month (a month and a year), Period, or Text as written; formatted by the locale
CasePieceSource Pdf(path | stream factory | document, credentials), Image(path | stream) → M07,
Html(HtmlSource) → M12, Email(eml | msg) → below, Part(an M06 assembly source)
CaseFileJson Read(Utf8JsonReader) and Write(Utf8JsonWriter); a versioned schema published on the site
  • Numbers are the parties' contract. Once a communication is marked sent, the numbers of its pieces are frozen: a model that renumbers them is refused, typed (CaseFileException, naming the piece and the communication), since the court and the other side cite them. A piece inserted later takes the next number or a suffix (InsertPolicy.Suffix gives "12 bis"), as the caller chooses; a récapitulatif lists every piece of every communication.
  • Numbering is Explicit (the model's numbers, checked unique and ordered), Sequential or Hierarchical (sub-pieces numbered n.1, n.2), continued across communications.
  • One number model. A piece's number is M09's PdfExhibitNumber — the value M09's stamps print through their {piece} field and M18's reference finder parses — with its components (2, 2.1, 2.1.3), an optional ordinal suffix (bis, ter, quater … up to the Latin ordinals it lists as data) and a total order, 12 < 12.1 < 12.2 < 12 bis < 12 ter < 13, an inserted piece following its neighbor's sub-pieces; Parse is total. The suffix and its order belong to the core type rather than to a second model here, since a caller stamping with M09 alone numbers exhibits as a case file does, and two models of one number would disagree on its order: M09's specification carries them, and if M09 closed without them, slice 1 adds them to PdfExhibitNumber in the core.
  • The JSON form is the model and nothing else — no paths resolved, no digests —, so that a caller can keep it under version control; the resolved form is the export (below). Reading it is hostile input: Utf8JsonReader at its default depth, strings bounded by the stream, unknown properties refused with their path.
  • Locale data is ours. The strings of the bordereau and the stamps — "Bordereau de communication de pièces", "Pièce n°", "Désignation", month and day names — are data in CaseFileLocale, not CultureInfo lookups: under InvariantGlobalization, which containers and AOT deployments commonly set, CultureInfo("fr-FR") gives no French month names, and ICU's data differs between distributions (the trap M15 records for folding). fr-FR ships; other locales are data.

One resolution, every product​

CaseFileBuilder.Analyze(model, options) opens each source in turn — one open at a time — and produces a ResolvedCaseFile: every piece with its page count, its page range in the volume, its labels, its bookmark title, its file name, its source digest and what its source carries (signatures, encryption, text or not, active content, from M04, M16 and M15's inventory). Write(resolved, output) then produces everything from that one object:

ProductFrom the resolved modelWritten through
Exhibit stamp and per-piece page numbersNumber, the stamp template, the piece's page rangeM09's PdfExhibitStamper, pieces as ranges of the volume or as documents
Separator pageNumber and titleM09's PdfSeparatorPage; labeled with the piece's prefix alone, so its pages start at "-1"
BookmarkThe title pattern (the preset's or the default "Pièce n° {number} — {title}"); sub-pieces nested; the piece's own outline nested beneath it (M06's Nest) or droppedM07's PdfOutline
Named destinationpiece-{number}, one per piece, at the top of its first pageM06's name tree
Page labelsThe plan: bordereau in lower roman, each piece {number}- from 1 (default), continuous, or Bates-mirrored (M09)M06's PdfPageLabels
BordereauEvery line, and the numbers its page column showsM12, from HTML the satellite writes
Links from the bordereauEach line to piece-{number} in a volume, or a GoToR to the piece's file and its named destinationM12.6 and M07's open parameters
Metadata and viewer settingsTitle from the locale and the matter, language, the dates the caller supplies; UseOutlines, DisplayDocTitleM14's PdfDocumentMetadata, M06's viewer preferences
Integrity manifestEvery digestM06's attachments, M14's XMP
JSON exportThe resolved modelCaseFileJson.WriteResolved
Export anchorsPiece numbers per pageM15's PdfAnchorProvider, so that chunks of a volume cite "pièce 2.1, p. 3"

No product reads another product: the bookmark title is not taken from the inventory's text, nor the stamp from the bookmark. That is what "cannot disagree" means in code; the verifier (below) is what proves it on the output.

Outputs​

  • A volume: M06's assembly, bordereau first (its own part, RenderToPart), then each piece, optionally preceded by its separator. The whole is one forward-only write; memory follows the heaviest page plus the resolved model, a few hundred bytes a piece.
  • One file per piece: each piece its own assembly — stamps, its bookmark, its labels, the manifest entry for its source —, named by the preset's pattern; sub-pieces in their parent's file or in files of their own, as the preset or the options say. The files are written first, through a hashing sink, so that the bordereau, written last, can list each file's SHA-256 and link to each by GoToR. A file over the preset's size rule is cut by M07's split by size, when the preset allows a piece in several files, and named "{number} ({k}/{n})".
  • Both, from one analysis.
  • File names follow M07's safe naming (reserved characters removed, NFC, bounded length) and then the preset's pattern, character set and length; accented letters transliterated to ASCII only when the preset's character set demands it, through M15's generated decomposition tables, never through the platform's culture.

The bordereau​

  • HTML written by the satellite, never concatenated from strings: CaseInventoryWriter escapes every value of the model, so that a title containing </td> or <img src=…> is text. The default stylesheet — A4, the matter in a running header, "page X sur Y" in the footer through M12's late-filled counters — is replaceable, and so are a header and a footer fragment (a firm's letterhead, a signature block), loaded through the resolver of ADR 38 from the bases the caller declares.
  • Columns: number, title, date, and optionally the pages, the communication, the source's or the file's SHA-256, and "original signé joint" where a signed original travels with the piece. Sub-pieces are indented.
  • Tagged by M13 (Table, TH with Scope, H1, Lang fr), PDF/UA-1 as its target: with the e-mail renditions, the bordereau is what the library itself writes into a case file, and it is accessible. Whether the volume keeps a claim is M06's structure policy, which removes it as soon as one piece is untagged, and says so.
  • Links: href="#piece-2.1" names a destination the enclosing assembly defines, not an id in the HTML. M12.6 resolves #id against the document it lays out; M18 adds PdfRenderOptions.ExternalDestinations to AdCodicem.Pdf.Html — the member M12 reserves for this milestone —, the destinations the caller declares will exist, each resolved by the enclosing assembly and reported by M06 if it never appears. In per-piece output the links are GoToR to the file with #nameddest=piece-2.1, through M07's open parameters.

The bordereau's page column: two bounded passes​

What the page column shows decides whether the bordereau depends on its own length:

Page planThe column showsDepends on the bordereau's length
Bordereau in roman, each piece labeled from its own start (default)Nothing by default — every piece starts at "-1", and the link is the navigationNo: laid out once
Bordereau in roman, pieces numbered continuously from 1The label of each piece's first pageNo: laid out once
Continuous numbering including the bordereau, or no labels (the viewer's physical page)The physical pageYes
Bates-mirrored labels starting on the bordereauThe Bates numberYes

In the last two rows the builder runs ADR 39's strategy itself, since the pieces are PDF parts M12 does not lay out and target-counter() cannot reach them: a first render lays the bordereau out with every number replaced by a placeholder of the reserved width (ReservedCounterDigits of the widest digit, tabular-nums), writes nothing and keeps only its page count; the builder then knows every piece's first page and renders again, writing. A reserved width that holds cannot change the wrapping of a line, so the page count holds; if it moves anyway — a number wider than the reserve —, the builder renders again, up to M12's MaxLayoutPasses, then writes the last pass and reports casefile.inventory-unsettled with the pieces whose numbers may be wrong. A measure-only render is the map-only pass M12's orchestrator already runs on a non-seekable output; M12 exposes no call for it, so M18 adds one to .Html beside ExternalDestinations — IPdfRenderer.MeasureAsync(HtmlSource, …), returning the page count and the page of each id, writing nothing — with its tests in M12's suites.

Signed, certified, encrypted and damaged pieces​

PieceIn a volumeIn its own file
Approval-signedM06's AttachOriginal, the default here: the stamped copy in the volume, the signed original as a page-level associated file (Source) on its first page, its digest in the manifest, "original signé joint" in the bordereauStampAfterSignature, the default here: M09's incremental stamp, which leaves every signature covering its revision and reports stamp.after-signature; or the original passed through byte for byte, unstamped, when the caller or the preset prefers
Certified (DocMDP)M06's Refuse, by default; Insist treats it as signed and reports the certification voided in the copy — the original travels intact beside itRefused by M09; passed through unstamped when the caller asks
EncryptedOpened with the source's PdfCredentials (M16); Assemble demanded under PdfPermissionPolicy.Respect, as M16's table says; the output unencrypted unless the options encrypt itLikewise
DamagedRead by the tolerant reader, optionally repaired first (M05); every loss M01 and M05 report is carried into the piece's report, never silentLikewise
Without textAssembled; reported per page under the preset's text rule, if it has oneLikewise
With active contentAssembled as it is (M06 keeps scripts); reported under the preset's active-content rule; removed by M19 once it existsLikewise

The integrity manifest​

  • What is hashed. Each piece's source as received — the bytes of the PDF, image or message file, streamed through SHA256 —; each file produced in per-piece output; the volume. Sub-pieces extracted from an e-mail are hashed as the bytes the message carried, after transfer decoding.
  • The manifest is a JSON document — algorithm, matter reference, generator name and version, each piece's number, title, source name, media type, length and digest, its page range, its output file's name and digest where there is one — serialized deterministically (fixed property order, invariant numbers, no insignificant whitespace), under a published schema. It is embedded in the volume as a document-level associated file, AFRelationship /Supplement, application/json, and written beside it as a sidecar.
  • XMP: a custom schema of M14's — a namespace URI under the project's control, fixed before the first stable release, prefix adcCase — carries the matter reference, the communication, the piece count and the manifest's own digest, with its extension schema description generated for PDF/A.
  • The volume's digest cannot sit inside the bytes it digests. It is written in an appended update: revision 1 is the volume, written through a hashing sink; revision 2 changes only the metadata stream, adding adcCase:volumeDigest, the SHA-256 of revision 1's bytes — the file up to the end M04's revision history gives revision 1. Anyone can recompute it with M04's PdfRevision.CopyTo, pyHanko's revision extraction or head -c. Forward-only, no placeholder patched; the update needs the writer's index of revision 1, which it still holds, and an output it can append to. A non-seekable output, a preset that forbids incremental updates, or the caller selects VolumeDigest.SidecarOnly, and the report says where the digest went. A later signature (M26) covers both revisions.
  • Recomputation is CaseFileVerifier's: a piece's digest that no longer matches its source names the piece.

Court-portal presets, as data​

A preset is a JSON document shipped as an embedded resource:

CasePortalPreset id, name, jurisdiction, version; Sources: [{ url, title, clause, retrieved, verifiedOn }];
Rules: [{ rule, value, source }]
CasePortalRule one class per rule of the vocabulary: Check(resolved, outputs) -> findings; Shape(plan)
RuleChecksShapes by
portal.layoutOne file per piece, one file with a bookmark per piece, or eitherChoosing the output
portal.bookmark-titleEach piece's bookmark title against the pattern, and its lengthWriting it from the model
portal.file-nameEach file name against the pattern, character set and lengthNaming
portal.file-sizeEach file's lengthM07's split by size, when split-allowed; otherwise a finding
portal.total-size, portal.file-countThe submission's sumsA finding — dividing a filing into several sendings is the caller's decision
portal.inventoryThe bordereau present, where the preset wants it — first in the volume, or a file of its ownWriting it
portal.textEvery page of every piece has text (M15's page kinds)A finding naming pieces and pages — OCR is M22's
portal.encryptionNo piece or file encryptedWriting unencrypted; an encrypted piece whose permissions forbid it is a finding
portal.active-contentNo script, launch or submit action (M15's inventory)A finding — removal is M19's
portal.pdf-versionThe header versionM03's output version
portal.pdfaA PDF/A claim on every fileA finding — validation is M20's, conversion M21's
portal.page-sizeThe page boxesM09's fit to A4, when the preset allows it
portal.stampThe exhibit stamp on each piece's first pageM09
  • Values are sourced or absent. Every value cites one of the preset's sources; CasePortalPresetTests fails on a value without a source, a source without verifiedOn, or a rule the vocabulary does not know. No number is written in this document — no size cap, no file count: each is read from the text or guide in force when the preset is encoded, recorded with the preset, and cited in the commit that encodes it.
  • Shipped presets, each verified before it is encoded:
    • telerecours — the administrative courts' application: the Code de justice administrative (R. 412-2 on the inventory, R. 414-1 and following on Télérecours, R. 414-3 on how pieces are presented), SA Finamur on what a bookmark must carry, and the application's own guide and FAQ for what the texts leave to it;
    • rpva — electronic communication before the judicial courts through e-Barreau and the RPVA: the Code de procédure civile (art. 748-1 and following) and the technical orders in force, and the platform's guide;
    • plex — the ministry of justice's platform for exchanging files too large for the RPVA: its conditions of use and guide.
  • Re-verification is part of the release procedure (docs/releasing.md): a stable release re-reads every preset's sources and records the date. No test reads the clock.
  • The vocabulary's own tests use synthetic presets with arbitrary values; shipped presets are tested by what they encode, not by numbers invented for the test.

E-mail to a piece​

EmailMessage parsed: headers as written and decoded (From, Sender, To, Cc, Bcc when present, Reply-To,
Date, Subject, Message-ID, In-Reply-To), bodies (text, HTML, related parts), attachments
(name, media type, disposition, content id, length), nested messages; the source's digest
EmailReader ReadEml(stream, EmailLimits), ReadMsg(stream, EmailLimits) -> EmailMessage + report
EmailToPiece Render(message, EmailRenderOptions) -> a CasePieceSource (an M06 part) and its sub-pieces
EmailRenderOptions header fields shown and their order; date display (as written, or at a fixed offset the
caller gives); body preference (Html, Text); attachments (AssociatedFiles, SubPieces, Both,
ListOnly); nested messages (SubPieces); attachment converters (a caller's map from a media
type to a function returning a CasePieceSource); stylesheet; fonts; conformance target (M14's
PDF/A-3 when attachments are embedded); limits
  • EML — RFC 5322 headers, unfolded; RFC 2047 encoded words (B and Q) in any charset the framework's CodePagesEncodingProvider knows, an unknown one reported and read as UTF-8 then Latin-1; RFC 2231 parameter continuations and charsets for file names; base64 tolerant of line breaks and junk, quoted-printable tolerant of bad escapes; multipart boundaries with a missing final boundary read to the end, reported; format=flowed (RFC 3676); message/rfc822 nested; multipart/signed rendered with its signature part kept as an attachment and reported unverified; application/pkcs7-mime and PGP-encrypted bodies reported unreadable; winmail.dat (TNEF) kept as an attachment and reported.
  • The rendition is an HTML document the satellite writes and M12 renders: a header block — a labeled table, visibly framed, escaped, with the attachments listed by name, size and digest and where each went ("pièce 12.1", "fichier joint") —, then the body. An HTML body keeps its own styles, placed in a cascade layer below the header's (M12.1), so that a message cannot hide or restyle the header block; cid: references resolve from the message's own parts through a resolver over the message; data: is allowed; every remote reference is refused by ADR 38's default — a tracking pixel is never fetched, and each refusal is in the report. A text body is escaped into a pre-wrap block, flowed lines joined. Conditional comments, VML and mso- properties degrade as M12 degrades anything it does not support.
  • Attachments: as associated files on the piece's first page (AFRelationship Unspecified by default, configurable), as sub-pieces — PDFs appended as 12.1, 12.2, images through M07, nested messages rendered in turn —, or both. What cannot become pages (a spreadsheet, an archive) is attached and listed, never opened — unless the caller's attachment converters name its media type: M31's DOCX conversion is the case in view, so that a Word attachment becomes a sub-piece while this satellite never references AdCodicem.Pdf.Docx. The original message is attached at document level with AFRelationship /Source; its digest is the piece's source digest.
  • Dates are shown as written, with their offset; a normalized display uses the offset in the message or one the caller supplies, never the machine's zone (invariant 6). MSG carries times in UTC only (below): shown in UTC unless the caller gives an offset.

MSG​

  • MS-CFB: header, FAT and DIFAT chains, the mini stream, the directory's red-black tree walked iteratively with a visited set; a chain longer than the file's sector count is a cycle, cut and reported.
  • MS-OXMSG: the property streams (__substg1.0_XXXXYYYY, __properties_version1.0), recipients (type, display name, SMTP address — an Exchange X.500 address shown as written when no SMTP address exists), attachments (PidTagAttachDataBinary, embedded messages as sub-storages), PidTagInternetCodepage for 8-bit bodies.
  • Bodies: PidTagHtml when present; otherwise PidTagRtfCompressed decompressed (MS-OXRTFCP's LZFu, its CRC checked, its declared size bounded) and, when it encapsulates HTML (\fromhtml1), de-encapsulated as MS-OXRTFEX describes; otherwise PidTagBody; a native RTF body with no plain-text twin is rendered as its text, reported (email.rtf-body-as-text).
  • The roadmap calls MSG optional; it is specified here in a slice of its own, behind the same EmailMessage, so that it can be taken or deferred without touching the rest. M18 may close without it: the MSG half of the e-mail row and its exit criterion then move to a debt issue, naming what brings it back — a caller whose pieces arrive as Outlook messages.

References to pieces​

CasePieceReferenceFinder Find(document, resolved, CaseReferencePatterns) -> references: text, quads, numbers, page
CasePieceLinker Link(document, references, target: volume | per-piece files) -> M11 link annotations
CaseReferencePatterns data: French by default — "pièce n° 12", "pièce no 12", "Pièce N° 12", "nº", "pièces nos
3, 4 et 7", "pièces n° 3 à 5", "(pièce 2.1)", "pièce 12 bis" — and exclusions ("pièce
adverse n° 4", "pièce n° 4 adverse", "pièces justificatives")
  • Runs on M15's search — regular expressions without backtracking, folding the spaces of U+00A0 and U+202F, across line breaks and hyphens —, so that "pièce n°⏎12" is found with a quad per line fragment.
  • A single reference is linked on its whole phrase; an enumeration or a range links each number, the prefix linking to the first. A number the model does not hold is reported (casefile.reference-unknown), never linked; an excluded context is reported as seen and left alone.
  • Never linked: text inside our own exhibit stamps and Bates numbers — M09's marks are Pagination artifacts, which M15 marks as furniture —, the bordereau's own lines, text already under a link, text hidden by optional content.
  • Targets: in a volume, a GoTo to piece-{number}; with per-piece files, a GoToR to the file with its named destination (M07). In a tagged document each link gets its Link element and object reference, as M11 does.

Checking a case file​

CaseFileVerifier.Verify(output, resolved | embedded manifest, preset?) reads what was produced and compares it with the model, by extraction rather than by trusting the builder: the stamp text on each piece's first page and the per-piece page numbers (M15's search), each bookmark's title and target (M07's outline), each label (M06), each line of the bordereau and the page its link reaches (M15's text in structure order, M11's links), each digest recomputed, each preset rule. The same verifier checks a volume the caller received, against a model the caller supplies. Its findings are casefile.* codes; the casefile check verb prints them.

Bounds (ADR 34, applied to the satellite)​

BoundKindWhy
MIME nesting depth, parts per message, decoded part length, unfolded header length, nested messagesGuards, EmailLimits, on by default, each reached reported as email.limit-* naming its property; ThrowOnLimit as the reader has itA valid message can nest forwarded chains deeply, carry hundreds of parts or a header of thousands of recipients; defaults set from the corpus messages with a margin and recorded
An LZFu body's declared size; a CFB stream's declared sizeChecked against EmailLimits.MaxDecodedPartLength before any allocationSizes come from the file
A CFB sector chain longer than the file's sector countInternal: a cycle, cutNo valid file has one
RTF group nestingNo bound: an explicit stack, memory proportional to the inputWalked iteratively
Bordereau layout passesM12's MaxLayoutPassesADR 39
Reference patterns' match timeNone needed: NonBacktrackingM15
Pieces in a modelNone: the model is the caller's, a few hundred bytes a pieceNot read from a hostile file

Report codes​

casefile.*, portal.* (the rules above) and email.*, public and stable from the first stable release, disjoint from reader diagnostics, validation rules and every other report family:

CodeSeverityMeaning
casefile.numbering-frozenError (refusal)The model renumbers a piece of a communication already sent
casefile.signed-original-attachedInformationA signed piece travels as a stamped copy with its original attached
casefile.piece-without-textWarningA piece has pages without text (the page kinds of M15)
casefile.piece-splitInformationA piece was cut into several files by the size rule
casefile.inventory-unsettledWarningThe bordereau's passes ran out; the pieces named may be listed a page off
casefile.reference-unknownWarningA reference to a number the model does not hold
casefile.reference-excludedInformationA reference in an excluded context, left unlinked
casefile.stamp-mismatch, casefile.bookmark-mismatch, casefile.label-mismatch, casefile.inventory-mismatch, casefile.link-unresolvedErrorThe verifier found a product that disagrees with the model
casefile.digest-mismatchErrorA recomputed digest differs from the manifest's
email.charset-unknown, email.boundary-missing, email.encoding-damagedWarningRead through, as described
email.resource-refusedInformationA remote reference in a body, not fetched (ADR 38)
email.signed-unverified, email.encrypted-body, email.tnef-keptWarningKept as attachments; the content could not be verified or read
email.rtf-body-as-textWarningAn MSG body available only as native RTF, rendered as text
email.limit-*WarningA guard reached; the message names the EmailLimits property

The command-line tool​

casefile build MODEL.json -o OUT.pdf | --per-piece DIR [--preset ID] [--manifest SIDECAR.json] [--volume-digest update|sidecar]
casefile check (VOLUME.pdf | DIR) [--model MODEL.json] [--preset ID]
casefile inventory MODEL.json -o BORDEREAU.pdf
casefile email IN.eml|IN.msg -o OUT.pdf [--attachments embed|pieces|both|list]
casefile link PLEADINGS.pdf --model MODEL.json -o OUT.pdf [--volume | --per-piece DIR]
casefile presets [--json] each preset, its rules, its sources and when they were verified

Every sub-verb takes --json and M06's exit codes; a refusal by policy — frozen numbering, a certified piece, a permission — is exit code 4, a verifier finding at error severity exit code 1.

Slices​

Each slice ends on a green commit, with the codes it introduces documented and its benchmark, if it has one, recorded in docs/status.md.

  1. The model, its JSON and the analysis. Delivers CaseFileModel and its parts, PdfExhibitNumber's suffixes if M09 has not, numbering and frozen communications, CaseFileJson and its schema, CaseFileBuilder.Analyze with streamed source digests and each source's facts from M04, M15 and M16. Proved by unit tests (number parsing and ordering, 12 bis after 12's sub-pieces and before 13, a renumbering refused, every JSON error at its path); FsCheck — any model round-trips through JSON unchanged, and numbering is a total order —; integration: the schema validated by Python's jsonschema; the reference case file analyzed, its page counts equal the manifest's and its digests sha256sum's. Leaves output.
  2. The volume without its bordereau. Delivers separators, stamps, bookmarks, labels, named destinations, viewer settings and metadata from the resolved model, through M06's assembly; the signed, certified, encrypted and damaged rows. Proved by integration: pdftotext finds each stamp on its piece's first page and "p. k/n" on every page; qpdf --json lists the outline and the labels as the model says; pdf.js resolves each bookmark and label; the signed original, extracted with pdfdetach, is byte-identical to the source and pyHanko finds its signature covering its revision. Leaves the bordereau.
  3. The bordereau. Delivers CaseInventoryWriter, the fr-FR locale, the default stylesheet, the header and footer fragments, the links through ExternalDestinations, tagging, and the page column with the bounded passes over MeasureAsync — the two members added to .Html. Proved by unit tests (escaping every value, every column, the locale without ICU); FsCheck — for random piece counts, page counts and title lengths, under each page plan, every page number the bordereau prints is the page its link reaches, within the pass bound —; integration: M15's tables and pdfplumber read the bordereau's lines as the model's; PyMuPDF's get_links and pdf.js land each line on its piece's first page; veraPDF passes the bordereau alone under PDF/UA-1. Leaves per-piece files.
  4. Files per piece. Delivers per-piece output, naming, the size rule's split, GoToR links with named destinations, the digests listed in the bordereau, StampAfterSignature for signed pieces. Proved by integration: every file named as the pattern says; each GoToR resolved by pdf.js in a directory of the files; each listed digest equal to sha256sum's; pyHanko on each signed piece's file finds every signature intact and the stamp a later change. Leaves integrity.
  5. The integrity manifest. Delivers the manifest and its schema, the associated file, the XMP schema with its PDF/A description, the appended update carrying the volume's digest, the sidecar, recomputation in the verifier. Proved by integration: pdfdetach extracts the manifest, jsonschema validates it; ExifTool reads the XMP properties; an independent Python script — no code of ours — finds revision 1's end and recomputes every digest; qpdf and pikepdf list two revisions, the second changing only the metadata stream. Leaves presets.
  6. The preset framework. Delivers CasePortalPreset, the rule vocabulary with checks and shaping, the presets' schema with mandatory sources, casefile presets. Proved by unit tests per rule with synthetic presets — a case file that satisfies it, one that violates it, one the shaping repairs —; CasePortalPresetTests over every shipped file. Leaves the French presets.
  7. Télérecours. Delivers the telerecours preset, read from the Code de justice administrative in force, the decision cited above and the application's current guide, sources and dates recorded in the file and in the commit. Proved by the per-rule tests on the reference case file shaped by it, and the verifier's check. Leaves the judicial presets.
  8. e-Barreau, the RPVA and PLEX. Delivers rpva and plex, verified the same way. Proved by the same tests. Leaves e-mail.
  9. EML. Delivers the MIME reader, EmailMessage, EmailToPiece, attachments and nested messages as sub-pieces, EmailLimits, the ADR on writing the parser or taking MimeKit. Proved by unit tests over every construct above, hostile messages (a MIME bomb, a boundary that never ends, a header of a megabyte, a charset that lies); FsCheck — any message Python's email package writes is read to the same headers, parts and bytes —; integration: Python's email package as the oracle on the corpus messages; pdftotext finds every header and the body's text; pdfdetach gives each attachment byte for byte; the resolver's log shows no request. Adds the messages the corpus lacks (below), described through M07's eml and msg formats and their block, under a message use case this slice adds to the schema's enum. Leaves MSG.
  10. MSG. Delivers the CFB and MS-OXMSG readers, LZFu, HTML de-encapsulation, email.rtf-body-as-text. Proved by unit tests (a FAT cycle, a mini stream past its end, a CRC that fails, a declared size of 4 GB); integration: olefile and extract_msg, in a container, as oracles for streams, properties, bodies and attachments; the same rendition checks as EML. Leaves references.
  11. References to pieces. Delivers CaseReferencePatterns (French), the finder and the linker, tagged links. Proved by unit tests over every pattern and exclusion, across a line break and a hyphen; FsCheck — any phrase the patterns generate, placed by M08's builder, is found with quads covering exactly its glyphs —; integration: on the pleadings the corpus lacks (below), PyMuPDF's get_links and pdf.js land each reference on its piece; no link on the stamped volume's own stamps nor on the bordereau. Leaves the tool.
  12. The verifier, the tool, budgets. Delivers CaseFileVerifier as a public API, the casefile verb, CaseFileBenchmarks with MemoryDiagnoser, the documentation, the remote run. Proved by CorpusToolTests, the tampered-copy rows, the budget rows and a green Remote corpus run recorded in docs/status.md.

Tests required​

Unit — under CaseFile/, in tests/AdCodicem.Pdf.Tests as M10 placed its satellite's, or in the test project M12.1's first slice gives the satellites that carry .Html's native assets, if it gives them one:

  • The model: numbering in every mode; suffixes and their order; a frozen communication; a récapitulatif; sub-pieces three deep; JSON round trip; every schema error; a title of every Unicode plane.
  • Resolution: the page ranges, labels and bookmark titles for every page plan; a piece of zero pages (refused, typed); a separator's label; the same source given twice (one digest, two pieces, reported).
  • The bordereau: escaping — a title that closes the table, opens a script, carries a url() —; every column; the passes under each plan, including a plan that cannot settle (a reserve of one digit and a thousand pages).
  • The manifest: deterministic serialization; revision 1's digest; the sidecar; recomputation after a byte is flipped in a piece, in the manifest and in revision 1.
  • Presets: each rule's check and shaping; a preset without a source refused; a value of the wrong type refused.
  • E-mail: each RFC construct; hostile messages at every guard, raised and thrown; CFB and MS-OXMSG faults; a body in each form MSG has; dates with and without offsets.
  • References: each pattern, each exclusion, numbers the model lacks, our own stamps ignored.
  • Determinism: FsCheck over random models built from corpus documents — two builds, identical bytes, volume and files alike, the manifest included.

Integration — tests/AdCodicem.Pdf.IntegrationTests, every referee in a container (ADR 27):

  • poppler — pdftotext for stamps, bordereau lines and e-mail renditions; pdfdetach for the manifest, signed originals and e-mail attachments; pdfinfo for page counts;
  • qpdf — --check on every output; --json for the outline, labels and revisions;
  • pikepdf — associated files and their relationships, the XMP packet, the structure tree of the bordereau;
  • PyMuPDF — links and their targets, the table of contents, words and their quads for references;
  • pdf.js, pinned under Node — labels, bookmarks and GoToR links as a viewer resolves them;
  • MuPDF — mutool draw for the e-mail renditions' inline images and the bordereau against its reference image;
  • veraPDF — PDF/UA-1 on the bordereau; the preserved claims of M06 on volumes whose pieces all claim;
  • pyHanko — signed originals and signed pieces stamped after their signatures;
  • ExifTool — the adcCase XMP properties;
  • Python — jsonschema for the model, the export and the manifest; email as the EML oracle; olefile and extract_msg as the MSG oracles; an independent digest script;
  • pdfplumber — the bordereau's table.

Acceptance conditions​

"The reference case file" is the model kept in the test support: 1 documents/contract/chromium-contract-fr.pdf, 2 vendor/uk-ogl/word2019-ccs-contract-schedule.pdf, 2.1 vendor/us-federal/xerox-workcentre-5335-ocr-hud-fonsi-linearized.pdf, 2.2 documents/invoice/libreoffice-invoice-fr.pdf, 3 vendor/pdf-association/indesign13-pdfua1-german-book-chapter.pdf, 4 vendor/opf-format-corpus/pdfmaker7-powerpoint-va-cancer-database-course.pdf — M09's exhibit set —, then 5 vendor/fr-licence-ouverte/fop-dictao-dila-signed-joafe-notice.pdf (approval-signed, its own page labels), 6 vendor/fr-licence-ouverte/pdfmaker-acrobat-cerfa-12156-form.pdf (its own outline, fields and scripts), 7 vendor/us-federal/acrobat3-import-irs-1040-1988-scan.pdf (a CCITT scan with no text, two page sizes), 7 bis vendor/opf-format-corpus/openoffice32-writer-rc4-128-open-password.pdf (encrypted, its recorded password) and 8 documents/form/reportlab-subscription-form.pdf (fields): eleven pieces, 99 pages.

DocumentsBehaviorVerified by
The reference case file, as one volumeEvery piece's first page carries "Pièce n° {number}" and the firm, every page "p. k/n" with n its piece's count, as pdftotext reads them; one bookmark per piece titled "Pièce n° {number} — {title}", sub-pieces nested, the Cerfa's and the PowerPoint's own outlines nested beneath their pieces, as qpdf --json and PyMuPDF's table of contents list them; labels "i", "ii" on the bordereau and "{number}-1…" on each piece, as pdf.js and qpdf read them; the bordereau's lines in model order with 7 bis after 7, each link landing on its piece's first page in pdf.js; the PowerPoint's and the DILA notice's own page labels replaced and reportedCorpusCaseFileTests.Stamps_bookmarks_labels_and_bordereau_agree_with_the_model (new), PdfJsCaseFileRefereeTests (new)
The same volume, through the verifierNo finding; and on copies altered in the test — a bookmark retitled, a label range shifted, a stamp removed by M09, a piece's page deleted — exactly the finding for the alteration, naming the pieceCorpusCaseFileTests.The_verifier_finds_every_disagreement (new)
The signed notice (piece 5) in the volume; vendor/us-federal/itext-govinfo-us-code-certified.pdf added as a certified pieceThe stamped copy in the volume, the original attached to its first page with AFRelationship /Source, byte-identical to the source under pdfdetach, and pyHanko finding its signature covering its revision; the certified piece refused, typed, unless the caller insists, then attached alike and assembly.certification-voided reportedCorpusCaseFileTests.Signed_pieces_travel_with_their_signed_original (new), PyHankoCaseFileRefereeTests (new)
The reference case file, one file per pieceEleven files and a bordereau named by the default pattern; each GoToR in the bordereau opening its file at piece-{number} in pdf.js; each SHA-256 the bordereau prints equal to sha256sum's; the signed notice's file stamped after its signature, pyHanko reporting the signature intact and the stamp a later changeCorpusCaseFileTests.Files_per_piece_are_named_linked_and_fingerprinted (new)
Piece 4, the 53-page PowerPoint, and remote remote/opf-format-corpus/jhove-hul-117-acrobat101-student-design-report.pdf (247 pages, 15 MB), under a synthetic preset whose size rule each exceedsSplit by M07 into files under the cap, named "4 (1/2)", "4 (2/2)"; every page present once across the parts (qpdf); casefile.piece-split reported; under a preset that forbids splitting, portal.file-size names the piece and nothing is splitCorpusPortalPresetTests.The_size_rule_splits_or_reports (new)
The reference case file with its bordereau's page column showing physical pages, and the hundred committed documents M06's memory row assembles, whose bordereau spans several pagesEvery page number printed equals the page its line's link reaches; the passes stay within MaxLayoutPasses; casefile.inventory-unsettled never reported; with a reserve of one digit on the hundred-piece file it is, naming the piecesCorpusCaseFileTests.Bordereau_page_numbers_match_their_pieces (new)
The volume and the files per pieceThe manifest extracted by pdfdetach validates under its schema; each piece's source digest equals sha256sum of the corpus file; ExifTool reads adcCase:manifestDigest and adcCase:volumeDigest; the independent script recomputes revision 1's digest from the file alone and agrees; a byte flipped in a copy of any piece, of the manifest or of revision 1 is named by the verifierCorpusIntegrityTests.Every_digest_recomputes_independently (new), ExifToolCaseFileRefereeTests (new)
The bordereau of the reference case file, rendered aloneveraPDF's PDF/UA-1 profile reports no failure; the structure reads number, title and date per row in pdfinfo -struct-text; in the volume, the claim is removed because pieces are untagged, and assembly.conformance-claim-removed says soVeraPdfCaseFileRefereeTests.The_bordereau_is_accessible (new)
The reference case file shaped by each shipped preset — telerecours, rpva, plexEvery rule the preset encodes is satisfied by the output, one test per rule; the CCITT scan (piece 7) named under portal.text where the preset has that rule; the Cerfa's scripts named under portal.active-content where it has that one; each preset's sources carry a verification dateCorpusPortalPresetTests.Each_preset_rule_holds_on_the_shaped_case_file (new), CasePortalPresetTests.Every_rule_value_cites_its_source (new)
E-mails: plain and HTML, multipart/alternative, inline cid: images, a PDF, an image and a spreadsheet attached, a forwarded message/rfc822, encoded-word subjects in UTF-8 and ISO-8859-1, quoted-printable and base64, format=flowed, a remote tracking image; and MSG from Outlook with HTML, with RTF-encapsulated HTML and with plain text — not in the corpus (below)Each becomes a piece whose header block pdftotext reads with every header as written; the body's text found in reading order; each attachment byte-identical under pdfdetach or present as sub-piece 12.1, 12.2 with its own stamp; the inline image drawn (MuPDF renders it); the original message attached as Source, its digest the piece's; no request made for the tracking image, email.resource-refused reported; headers, parts and bytes equal to what Python's email package, olefile and extract_msg readCorpusEmailTests.Emails_become_pieces_with_their_headers_body_and_attachments (new)
Hostile messages generated in the test — a MIME bomb, an unterminated boundary, a megabyte header, an LZFu body declaring 4 GB, a CFB chain that loopsA report and a typed exception where the caller asked for one, within the time and allocation budgets; nothing allocated from a declared sizeCorpusEmailTests.Hostile_messages_never_break_the_reader (new)
Pleadings citing pieces in every form the patterns know — not in the corpus (below) — linked against the reference case fileEvery reference found at the quads PyMuPDF's search_for reports, linked to its piece's first page in the volume and, in per-piece mode, to its file; "pièce adverse n° 4" and "pièces justificatives" unlinked and reported; a reference to piece 99 reported, unlinkedCorpusReferenceTests.Every_reference_reaches_its_piece (new)
The volume of the reference case file itself, and its bordereau, run through the linkerNo link added: the stamps are M09's artifacts, the bordereau's lines already linksCorpusReferenceTests.Our_own_stamps_and_bordereau_are_never_linked (new)
Any case file above, built twiceIdentical bytes: volume, files, manifest, sidecar and JSON exportCorpusCaseFileTests.A_case_file_builds_to_the_same_bytes_twice (new)
A case file of the hundred committed documents M06 assembles; remote remote/govinfo/us-code-2023-title42.pdf (9,302 pages, signed) as one piece of a volumeRetained memory proportional to the largest page, flat between the 10 % and 100 % checkpoints, under a budget recorded in status.md; the signed original attached by streaming, never heldCorpusCaseFileTests.Building_a_large_case_file_holds_its_budget (new), CaseFileBenchmarks (new)
The reference case file, through the toolcasefile build, check, inventory and link give what the API gives, byte for byte, as a dotnet tool and as the AOT binary; a frozen renumbering exits with 4CorpusToolTests.Casefile_matches_the_api (new)

The remote rows close only on a green Remote corpus run, recorded in status.md with its date. The rows that name documents not in the corpus close when the priority-1 gaps below are filled. The MSG half of the e-mail row closes with slice 10, or moves to a debt issue with it.

Corpus​

What the corpus holds​

  • Pieces of every kind a case file receives: our own contract, invoice and form from Chromium, LibreOffice and ReportLab; Word 2019's contract schedule; an InDesign PDF/UA chapter with 21 thumbnails and 23 links; PDFMaker's 53-page PowerPoint export with rotated pages; French administrative forms — the Cerfa 12156 with its outline and 47 JavaScript actions, the Cerfa 13983, the static XFA Cerfa 14880; French and Luxembourg legal publications under signature or seal (fop-dictao-dila-signed-joafe-notice.pdf, antenna-house-legilux-memorial-pades-lta.pdf, fop22-legilux-memorial-seal-renewed-timestamps.pdf).
  • Scans, for the text rule: CCITT (acrobat3-import-irs-1040-1988-scan.pdf, finereader8-frb-sr0115-examiner-guidance.pdf), JBIG2 with and without OCR (the Xerox files), DCT (documents/scan/reportlab-scanned-receipt.pdf), JPX; remote, the copier scans.
  • Signatures: six approval-signed committed documents and a DocMDP certification (itext-govinfo-us-code-certified.pdf); remote, certified and sealed documents in number.
  • Encryption: eighteen committed encrypted documents, eleven opening with an empty password, with their recorded passwords (openoffice32-writer-rc4-128-open-password.pdf), and permissions that withhold assembly (livecycle-dod-dd293-aes128-xfa.pdf, pdfwriter3-copyright-office-dmca-summary-1998-rc4-40.pdf).
  • Outlines and labels in the pieces (outlines in some sixty documents, page-labels in some fifty), which the case file nests or replaces.
  • Scale: the 1000-page journal committed; remote, the 9,302-page United States Code and the 247-page, 15 MB student report for the size rule.
  • Damage: documents/damaged/* and the remote damaged files, for pieces that arrive broken.

What it lacks​

NeedWhyPriorityLikely source
E-mail messages — EML from the clients lawyers receive mail in (Outlook, Gmail's export, Thunderbird, Apple Mail) and MSG from Outlook, covering the constructs of the e-mail row: HTML and text alternatives, inline cid: images, PDF, image and office attachments, a forwarded message, encoded words in two charsets, a remote image, a signed messageThe roadmap's third acceptance condition, "the corpus e-mails convert", has no e-mail to run on: the corpus holds PDFs only1Generated here: Python's email package from our own fictitious content, recorded in build_corpus.py; a public source: the MSG test files of Apache POI's HSMF (Apache-2.0) and the EML test messages of open-source MIME parsers under attribution-only licenses, each screened; a contribution for real clients' output, anonymized (W17)
A manifest that can describe non-PDF inputs, with expectations of their own (headers, body strings, attachment names and digests)Done by M07 for every format, eml and msg among them, with this block (the maintainer's decision of 2026-09-27); this milestone adds fields to it only if its first messages need them1M07's slice 8
Pleadings citing pieces — French conclusions whose references take every form the patterns know, across line breaks, hyphenated, in a table and a footnote, with an opposing party's pieces and "pièces justificatives" beside them —, from Chromium, LibreOffice and Word, with the references recordedThe linking acceptance needs real text to find, and no corpus document cites pieces1Generated here: our own source in HTML, ODT and through build_word.ps1, the expected references recorded from the source
A case file made by the tools French lawyers use — a bordereau and stamped pieces from Kleos, Hub-Avocat or Acrobat by handThe verifier should read a real bordereau, and the presets' defaults should match what practitioners already file3A contribution, anonymized (M09 lists the stamped pieces at the same priority)
A filing a portal refused, with the portal's messageThe strongest test of a preset is the rule it would have caught3A contribution
Image files as piecesPieces that are photographs or scanned TIFFs go through M07, whose own gap this is2M07's image files, which M07's format field describes
A committed piece over the size caps the presets encodeThe size rule meets real documents only in the remote corpus, since a committed file stays under 2 MB3Stays remote: a synthetic preset with a small cap covers the committed run

Traps​

  • The bookmark must say what the inventory says. A signet built from a different string than the inventory line — a short title here, the full libellé there, a non-breaking space in one and a space in the other — is the exact mismatch the texts sanction. One model, one string, and a verifier that compares by extraction with spaces folded.
  • Numbers are cited by others. Renumbering after communication breaks every reference in the pleadings of both parties and of the court; "12 bis" exists for this reason.
  • The bordereau is part of what it counts. Placed first, its own length moves every physical page after it; with labels that restart per piece nothing moves — choose the plan before choosing the algorithm.
  • "n°" has many spellings: the degree sign, the masculine ordinal (U+00BA), a superscript "o", "no", "N°", with or without a space, "nos" for the plural; an OCR layer adds its own. A pattern that knows one misses half the references.
  • Our own stamps say "Pièce n° 12". A linker that searches the volume finds them first.
  • A volume carries no signature. Merging voids every signature it copies; the signed original must travel beside the stamped copy, and a file per piece can instead stamp after the signature.
  • A digest cannot contain itself. The volume's digest lives in an appended update over revision 1, or beside the file — never "patched in" to bytes it covers.
  • Portal rules change and are not ours. A number copied from a blog or a colleague is a rejected filing waiting to happen: every value cites the text or the guide it was read from, and the date.
  • E-mail is hostile HTML. A body can restyle whatever it shares a stylesheet with, fetch a pixel that tells its sender the message was opened, and pretend to be a header block. The header's styles win by layer, nothing remote is fetched, and the header block is framed as ours.
  • MSG has no time zone. Its times are UTC; the sender's offset is gone. Showing the machine's local time would make the same file render differently on two servers.
  • CultureInfo is not a locale database under invariant globalization: French month names come from the satellite's data.
  • An Exchange sender may have no SMTP address — an X.500 path is what the message holds, and what is shown.
  • What would reopen EA-PDF: an archive or a court asking for e-mail pieces in the PDF Association's archival profile; the rendition already keeps the source message as an associated file, which EA-PDF also asks for.

Documentation​

  • docs/website/docs/guides/case-files.md (new): the model, numbering and communications, the volume and files per piece, the bordereau and its page plans, signed pieces, the integrity manifest, checking a case file.
  • docs/website/docs/guides/email-to-pdf.md (new): EML and MSG to a piece, attachments, what is never fetched.
  • docs/website/docs/reference/court-portals.md (new): each preset, its rules, its sources and when they were verified, generated from the preset files so that the page cannot drift from them.
  • docs/website/docs/reference/case-file-schema.md (new): the JSON schemas of the model, the export and the manifest.
  • docs/website/docs/reference/casefile-report-codes.md (new): every casefile.*, portal.* and email.* code.
  • docs/website/docs/reference/tool/index.md: the casefile verb.
  • docs/website/docs/introduction.md: case files added to what the library does.
  • docs/architecture.md: the CaseFile satellite's parts, and any dependency the slice-9 ADR takes.
  • The HTML engine's guide and API reference (M12's): PdfRenderOptions.ExternalDestinations and IPdfRenderer.MeasureAsync, the two members this milestone adds to AdCodicem.Pdf.Html.
  • docs/releasing.md: re-verifying the presets' sources before a stable release.
  • docs/corpus.md: e-mails and other non-PDF inputs in the manifest; the new referees (Python's email, olefile, extract_msg).
  • docs/features/features.json: the case-files entry brought to its state.
  • docs/status.md: the measurements and the presets' verification dates.

Exit criteria​

  • The model, its JSON form and schema, numbering with suffixes and frozen communications exist, and every product follows from one resolution.
  • A volume and files per piece carry stamps, bookmarks, labels, named destinations and a bordereau that agree with the model, confirmed by the verifier and by external extraction.
  • The bordereau is rendered by M12, tagged, linked, and its page numbers are right under every page plan, within ADR 39's bounded passes.
  • Signed pieces keep a verifiable original; certified pieces are refused unless the caller insists; encrypted and damaged pieces follow M16's and M05's policies, and every loss is reported.
  • The integrity manifest is embedded, summarized in XMP, and every digest — the volume's included — recomputes with an independent script.
  • The Télérecours, e-Barreau/RPVA and PLEX presets exist as data, each value citing a source read and dated, and a test per rule holds on the reference case file.
  • EML becomes pieces with their headers, body and attachments intact, and MSG likewise — or its deferral is a debt issue, the roadmap making it optional; nothing remote is fetched; every guard is reported under its own code.
  • References to pieces are found and linked, and never on our own stamps.
  • The acceptance conditions above pass on the corpus, in CI, with no document skipped, and the remote rows on a green Remote corpus run recorded in status.md.
  • Unit tests cover each behavior, its degenerate and its hostile cases; the MIME, CFB, LZFu and RTF readers join the nightly fuzzing campaign.
  • Integration tests run poppler, qpdf, pikepdf, PyMuPDF, pdf.js, veraPDF, pyHanko, ExifTool, pdfplumber and the Python oracles in containers.
  • CaseFileBenchmarks measures analysis, a hundred-piece volume and e-mail rendering with MemoryDiagnoser; the memory budget is enforced in CI and recorded in status.md.
  • The tool's casefile verb ships with documented JSON.
  • The documentation site publishes the pages above; features.json and status.md are brought in line.
  • Every page of Documentation is written in its Diátaxis section, one mode per page (ADR 47).