Filer assembles an outbound case file from three streams you've already organised on disk — Administration & Financial (file-opening, conflict checks, CW1/2 + ECF1, EOM, disbursements, closing forms), Casework (file notes, attendance notes, letters, emails, client-care / opening-advice / closing letters), and Documents (applications, statements, bundles, previous reps, SARs, decisions, evidence, reports) — and produces either one bound PDF (cover → clickable index → section dividers → documents — or, when it's too big for one file, the part PDFs inside a single ZIP with an HTML index) or a foldered ZIP package (one PDF per document + a cover letter + a clickable HTML index).
It's matter-agnostic — the same three streams for every matter type. It's the outbound companion to Bundle Builder (court bundles) and Evidence Exhibitor (exhibit-level packaging); all three share the Case Details, Backup, Config, and Settings tabs.
YYMMDD REF SEQ Document name.ext off each filename (the pattern is configurable in Config) for the index name + date; files that don't match keep their full filename. .jpg/.png scans get wrapped into A4 pages (a big phone-camera scan is downsampled to ~200 DPI first so it doesn't bloat the file) so they go in the bound PDF and the ZIP alike; .docx and other non-PDF files show a "not a PDF" badge — they're skipped from the bound PDF but carried (metadata-scrubbed) in the ZIP.<Case>-Index.html at the root instead. The PRIVATE & CONFIDENTIAL header + DRAFT watermark go on if ticked, and "Lock for distribution" encrypts the result — the single PDF directly, or, for a ZIP, the ZIP file itself (AES-256 password).Identity (GMIAU ref · Matter reference · Client's name) plus the per-case build options: Output format, Cover page (include? Short or Full?), Cover title (an optional headline stamped on the cover — Case file / Escape case / File transfer / File review / Audit / Peer review; default none), index page-numbers (bound-PDF only), Dates (Date of request · Date actioned · Case closed on — all default to today; clear "Case closed on" if the case is still open), a free-text Case description (prints on the Full cover's page 2), and Document protection (PRIVATE & CONFIDENTIAL header · DRAFT watermark · lock for distribution — encrypts the bound PDF, or password-protects the ZIP — + passwords). Most of the cover fields fill from Import Case Summary. The GMIAU logo is off by default on the cover — turn it on (and the firm/contact block) per-caseworker in Config → Cover content when the cover is going somewhere external.
The work. A Sort a whole case folder zone at the top — drop the matter folder here and Filer walks it, sending each file to one of the three sections by your Config → Folder routing rules (ignore-globs prune whole subtrees → folder-name globs pick the section, deepest match wins → filename globs can then override) — then the three section drop zones (Administration & Financial · Casework · Documents), each with a sort dropdown and a grouped list. The section zones are literal: what you drop there goes there (a folder's sub-folders become subsections). Re-order within a subsection in Manual mode. Image scans are wrapped into A4 PDF pages; other non-PDF files (.docx, .wav, …) are flagged and skipped from the bound PDF but carried in the ZIP. The Compile button at the bottom builds the bound PDF or the ZIP per Case Details → Output; its label tracks that toggle.
The reverse of File: drop a Filer-built bound PDF back in and Extract reads its bookmark tree to give you one PDF per document (or per index section) as a ZIP. Edit any filename in the table before extracting. Extracted PDFs carry scrubbed metadata — no client name, reference or preparer.
Save every tab's state — including the files you dropped into the File tab (base64-encoded in the envelope) — as a single gmiau-bundle/1 JSON, and restore it later. Home to the Clear client data danger-zone action. Run Backup before Clear or Restore — both wipe state.
The caseworker's Filer profile — sticks across cases, survives a reinstall (it lives in the One File config), and exports as a portable filer-profile/1 JSON you can hand to a colleague: the filename pattern (global + per-section overrides, live preview), File-tab defaults (default sort · unparsed-file placement · date display format), Folder routing (the pattern → section rules, the default section for unmatched folders, and the ignore-list), the cover-content row toggles, the PDF body font, and the preparer / caseworker-name defaults. Export profile… / Import profile… sit near the bottom.
App appearance (light / dark / system, and the theme family — GMIAU, Dracula, Buffy) plus the app font family + size. These sync across every GMIAU tool.
YYMMDD REF SEQ Document name.ext (or however you've set the pattern in Config) — that's what gives Filer the document name + date for the index. Unparsed names fall to the bottom of Name / Date sorts and show their full filename.0001 …, 0002 … so the extracted folder keeps bundle order — alongside 00 Cover Letter.pdf and <Case>-Index.html at the root (the index, as a plain web page — its links open the documents in any browser or file manager once the ZIP is extracted). Pick ZIP when the recipient wants to file documents their own way. (If Lock for distribution is on, the bound PDF is encrypted; the ZIP is delivered as an AES-256 password-protected ZIP — the files inside aren't separately encrypted.)(Part 1 of 3) … parts, each under the limit. A split bound PDF comes back as one download — a single ZIP holding the part PDFs (each just the cover, with Part [n] of [n] on page 1, and that part's documents — no PDF index inside) plus <Case>-Index.html linking each document to its part. A split ZIP package is one ZIP per part, each with 00 Cover Letter.pdf + <Case>-Index.html (the full index, with Part N for documents in another ZIP) + only that part's section folders (your browser will ask to allow the multiple downloads). Sizing is approximate (it goes by source-file size); a single document bigger than the limit gets a part to itself..docx/.xlsx/etc. carried in the ZIP get their document properties stripped too; legacy .doc/.xls and .wav are carried as-is and flagged "metadata not scrubbed". The bound PDF keeps the fuller metadata.*LAA* → Admin & Financial, Docs → Documents, everything else → Casework), then file rules that override by filename (e.g. *COI*, *CW1*, *EOM*, *CW3* → Admin & Financial — so file-opening / legal-aid papers sitting loose in a casework-stage folder still land in Admin & Financial), and an ignore list (Archive / Unused / …) that prunes whole subtrees. Then the auto-sort zone splits any matter folder correctly. The three section zones ignore all of this — drop there for precise placement..jpg/.png documents are wrapped one-per-A4-page; only Office docs and other non-PDF types are left out of the bound PDF (the ZIP carries them).Q. Nothing happens when I click Compile.
A. The bound PDF needs at least one PDF or image scan in some section — the status line under the button says why. If the queue is all .docx/.doc/etc., switch Output → ZIP package and Compile again: those ride along in the ZIP.
Q. My .docx / .wav files didn't make it into the bound PDF.
A. By design — the bound PDF only holds PDF pages (and image scans, which Filer wraps to A4 pages). Office docs and other non-PDF files are flagged "not a PDF" in the list; switch Output → ZIP package to include them — the ZIP carries them in their section folder, with document metadata stripped where Filer can manage it.
Q. The index shows the filename, not a tidy name.
A. That file didn't match the filename pattern. Rename it to YYMMDD REF SEQ Name.ext, or set a global / per-section pattern in Config → Filename pattern that matches how you name files.
Q. I dropped the matter folder on the auto-sort zone and everything went to Casework.
A. You've no routing rules yet — Casework is the default for unmatched folders. Add rules in Config → Folder routing (e.g. *LAA* → Admin & Financial, Docs → Documents) and drop again.
Q. How do I open the documents from a ZIP?
A. Extract the ZIP, then open <Case>-Index.html at the root — its links open each document in your browser or file manager. (A ZIP no longer carries a PDF index — in-browser PDF viewers were silently swallowing its cross-document links.)
Q. Can the recipient open the password-protected ZIP?
A. Yes — it's a standard AES-256 ZIP. Windows Explorer's built-in unzip can't open encrypted ZIPs, but 7-Zip / WinRAR / macOS Archive Utility / the unzip command all can — they prompt for the password. Send the password out-of-band, not in the same email.
Q. Can I un-bundle a Filer PDF a colleague sent?
A. Yes — drop it into the Extract tab. (Works on one part of a split bundle too — each part is a normal Filer PDF; it just won't have an internal index page.)
Q. The split parts came out bigger than my limit.
A. Sizing is approximate — the planner packs documents by their source file size, but a freshly-built PDF (especially a wrapped image scan) can end up larger. Lower the limit a notch and Compile again. Also: a single document bigger than the limit can't be split, so it goes in a part on its own.
Q. My browser blocked the extra downloads when the output split.
A. A split bound PDF now comes back as a single ZIP, so there's nothing extra to allow. A split ZIP package still downloads one ZIP per part — your browser asks "Download multiple files?" the first time; choose Allow, or only the first part comes down.
Q. An Arabic / Chinese / Cyrillic name shows up blank (or partly missing) on the cover or in a bound PDF's index.
A. The cover and a bound PDF's index render in Helvetica or Times, which only carry Latin glyphs — non-Latin text in a client name, cover field or filename gets accented letters folded to plain ASCII and anything else dropped (it used to crash the compile outright). A ZIP's <Case>-Index.html keeps the original text intact, and the documents themselves are untouched.
Q. How do I set up Filer for a colleague (or carry my setup to another machine)?
A. Config → Filer profile → Export profile… writes a filer-profile/1 JSON (filename pattern, folder routing, sort/date defaults, cover toggles). Keep it in the vault; the other person runs Import profile….
Q. Will my Config and theme follow me to another machine?
A. Theme + font sync across GMIAU tools via browser storage. The Config preferences live in the One File config (~/Documents/Work/Config/Tools-GMIAU-RB.conf) — export it from Config → Config file, or just the Filer-specific bits via Config → Filer profile.
More: the GMIAU Caseworker User Guide in your Obsidian vault, and FILER-SPEC.md in gmiau-specs/; see also Bundle Builder (court bundles) and Evidence Exhibitor (exhibit packaging). — Confidentiality: this tool runs entirely in your browser; nothing is uploaded. The files you load, the case references and the client names are confidential — keep the produced files on this machine and send them only through your normal, approved channels.
<name> (Part 1 of 3).pdf …), each just cover (Part [n] of [n] on page 1) + that part’s documents — no PDF index inside — all delivered together inside one <name> (split into N parts).zip whose <Case>-Index.html links each document to its part, so it’s a single download. ZIP package → one ZIP per part, each with 00 Cover Letter.pdf + <Case>-Index.html (the full index, with Part N shown for documents in another part) + only that part’s section folders (your browser will ask to allow the multiple downloads). Sizing is approximate (it bin-packs whole documents by source size); a single document bigger than the limit gets a part to itself. With Lock for distribution on, each ZIP is AES-256 password-protected.
Workflow dates printed on the bundle cover (both pages). The three below are caseworker-entered and default to today — adjust or clear as needed. (The case opened date on the Full cover comes from the imported Case Summary, separately.)
Optional free-text description. Prints on the Full cover (Page 2) after the firm/contact block when the corresponding Config-tab row is on.
PDF-OUTPUT-SPEC §7a.
PDF-OUTPUT-SPEC §7a.
Light = GMIAU Light · Dark = GMIAU Dark · Ryan = Dracula. The dark themes stay dark.
Syncs across every GMIAU Shell tool via ifyi_hide_guide_tab + ifyi_hide_config_tab.
The current FILER-SPEC.md is inlined at build time. Edit the spec at gmiau-specs/FILER-SPEC.md and run immigrationfyi-tools update to refresh every tool that embeds it.
FILER-SPEC.md# GMIAU Filer Spec
**Version 0.8 · 2026-05-12**
Filer assembles a paginated **case file** from three documentary streams
the caseworker has already organised on disk:
- **Administration & Financial** — file-opening papers, conflict checks,
closing forms, CW1/2 + ECF1, EOM, disbursements, CW3s, self-grants.
- **Casework** — file notes, attendance notes, letters, emails, client
care letter, opening advice, closing letter, 3-month updates.
- **Documents** — applications, statements, bundles, previous reps,
SARs, decisions, determinations, evidence, reports.
The output is a single bound PDF (default) or a ZIP package, with
a Case Summary cover, a clickable index, and three section dividers.
Caseworkers don't all file the same way, so the File-tab parsing and the
section assignment are driven by an editable **caseworker profile** (the
Config tab — §4) that can be exported / imported as a JSON (§4.7). Whole
case folders can be dropped in one go and fanned out to the three sections
by glob rules (§4.6). Non-PDF inputs are handled too: image scans are
wrapped into pages so they flow into the bound case file; Office documents
ride along in the ZIP (§2.4).
This is the **outbound** companion to:
- `bundle-builder.html` — court bundles (inbound).
- `evidence-exhibitor.html` — exhibit-level packaging (inbound).
**Filer is matter-agnostic.** The three section streams are the same
for every matter type. Bundle Builder's matter-toggle scaffolding is
not reused.
Cross-links:
- `EXPORT-SPEC.md` — filename grammar (`YYMMDD CLIENTREF SEQ Description.ext`).
- `PDF-OUTPUT-SPEC.md` — visual rules every PDF must follow.
- `CASE-SUMMARY-IMPORT.md` — Case-Summary ingestion contract (Filer is a consumer).
- `naming.yaml` / `references.yaml` — case-folder + ref formats.
---
## 1. Tabs (6)
Filer is a six-tab GMIAU Shell tool. (A mandatory **Guide** tab, first and
default-active, is added GMIAU-Shell-wide per GMIAU-STYLE-GUIDE — see that
spec; it does not change the tabs below.)
| Tab | Icon | Role |
|---|---|---|
| **Case Details** | 📋 | Identity (refs + client name + preparer) + Case Summary import + per-case build options: Output format toggle, Split-into-parts tickbox + size limit (§3.d), Cover-page tickbox + Short/Full type + Cover title, index page-numbers, Dates, Case description, Document protection. Same shell scaffolding as Bundle Builder's Case Details, less the matter-toggle. |
| **File** | 📂 | The work. Three upload sections plus a "Sort a whole case folder" auto-route zone (§4.6); drag-drop or pick. Compile produces the bound PDF or the ZIP package per Case Details → Output. |
| **Extract** | ✂️ | Reverse of File: drop a Filer-built bound PDF, get one PDF per document (or per section) back as a ZIP. Engine promoted from Evidence Exhibitor / Bundle Builder, Filer-ised. |
| **Backup** | 💾 | Backup / Restore. Save every tab's state (incl. the File-tab files, base64) as a `gmiau-bundle/1` JSON envelope and load saved envelopes back. Owns the "Clear client data" danger-zone action. |
| **Config** | 🛠️ | Caseworker profile (filename pattern + per-section overrides, folder + file routing rules + ignore-list, default sort, unparsed-file placement, date display format, cover-content row toggles, identity defaults) + profile export/import — see §4. |
| **Settings** | ⚙️ | Theme, appearance, font. |
Tab icons + labels match the **Evidence Exhibitor canon** (📋 ✂️ 💾 🛠️ ⚙️), not Bundle Builder's older `↑` Extract icon. No matter tabs, no per-recipient profiles.
---
## 2. File tab — three upload sections
The File tab is the core. Each section is an independent drop zone +
sortable list.
### 2.1 Sections
| Section | Where it usually comes from (illustrative — folders aren't enforced) | Contents |
|---|---|---|
| **Administration & Financial** | a `LAA/`-ish subfolder (`LAA/`, `LAA and opening stuff/`, …) + file-opening / conflict / legal-aid papers loose in the casework-stage folder (picked up by the §4.6 *file rules*) | File Opening Form · Conflict of Interests Check · File Closing Form · CW1/2 + ECF1 · EOM · Disbursements · CW3s · Self-grants |
| **Casework** | the casework-stage folder(s) — `Casework/`, `01 CW1/`, `00 MCC/` + `01 LH/`, … (number-prefixed and incrementing as the matter progresses); the §4.6 *default section*, so they need no routing rule | File Notes · Attendance Notes · Letters · Emails · Client Care Letter · Opening Advice Letter · Closing Letter · 3-month update letters |
| **Documents** | `Documents/` / `Docs/` | Applications · Statements · Bundles · Previous Reps · SARs · Decisions · Determinations · Evidence · Reports |
Source folders are **conventional, not enforced** — the user drag-drops
files in (and the §4.6 routing rules + §4.6 file rules sort a whole matter
folder out). Filer does not read the filesystem directly. (Future: File
System Access API once the secure-origin flag is sorted on this box.)
### 2.1.1 Folders → subsections
Each section accepts both individual files and **folders**. Folders can
be added via:
- **Drag-drop** of a folder (or many folders + loose files) onto the
section's drop zone. Filer walks each dropped folder via
`DataTransferItem.webkitGetAsEntry()`.
- **"Pick a folder…"** link inside the drop zone, which fires a hidden
`<input webkitdirectory>` and reads `webkitRelativePath` on every file.
The **immediate sub-folders** of a dropped/picked folder become
**subsections within the section**. Files at the dropped folder's top
level live at the section root (above the subsection headers). Files in
deeper sub-sub-folders flatten into the matching first-level subsection,
preserving their relative path in the displayed name (e.g. files in
`Casework/Letters/2025/` show as `2025/<filename>` inside the *Casework
→ Letters* subsection).
Within each subsection the **sort dropdown applies independently**
(global mode + per-subsection sorted view); manual drag-reorder is
constrained to the same subsection — moving a file out of its
subsection isn't supported (remove + re-add via folder pick instead).
These three section drop zones are **literal** — whatever you drop on a
section goes into that section. To drop a whole matter folder and have
Filer split it across the three sections, use the **"Sort a whole case
folder"** zone above them (§4.6).
### 2.2 Per-section list controls
Each section's file list supports sorting by:
- **Name** — alphabetical on the parsed *document name* (not the raw filename).
- **Date** — chronological on the parsed date.
- **Upload** — insertion order (the order files were dropped in).
- **Manual** — drag-and-drop reordering. Becomes the active sort the
moment the user drags anything.
Default sort + the placement of files without a parsed date are both
**Config-tab settings** (see §4). The user can still override per
session via the section's sort dropdown — Config provides the starting
point.
### 2.3 Filename → list-row parsing
Uploaded filenames are parsed using the Config-tab pattern (see §4) into
three fields:
- **`{seq}`** — sequencing number. Drives file order when the user picks
*Sort: Name* (with seq as tiebreaker) or any custom-ordered run.
**Hidden from the index and the bound PDF.** Internal use only.
- **`{name}`** — document name. Shown in the index, the section
divider's TOC, and the per-PDF metadata title.
- **`{date}`** — optional date. Shown in the index alongside the name.
Display format (UK long / ISO / UK short) is a Config-tab setting
(see §4). Omitted when absent.
Files that don't match the Config pattern keep their full filename as
the document name, no date, no seq. Their placement (top / bottom / by
upload order) is a Config-tab setting.
**Fallback scrape on unparsed names** — for portal downloads (UKVI,
HMCTS, …) whose filenames don't follow the EXPORT-SPEC shape, Filer
still lifts two tokens from anywhere in the basename so the row carries
useful metadata and *Sort: Date* works:
- `YYYY-MM-DD` (19xx / 20xx years) → `{date}`, normalised to YYMMDD for
the sort key.
- UKVI UAN — `NNNN-NNNN-NNNN-NNNN` or `NNNN NNNN NNNN NNNN` (the form
defined in `references.yaml`) → `{ref}`, normalised to the canonical
hyphenated form.
The scraped tokens are stripped from the displayed `{name}` so the row
shows `Change of Condition Application` rather than the full noisy
basename. Rows hit this way remain *unparsed* for placement purposes
(§4.3) — they don't promote into the matched bucket; they just sort
chronologically among themselves instead of by `file.lastModified`.
### 2.4 Non-PDF files & image scans
Filer's bound PDF can only embed PDF pages, so non-PDF inputs are split
two ways:
- **Image scans** — `.jpg` / `.jpeg` / `.png` (and any other raster format
the runtime can embed; `.gif` / `.bmp` / `.webp` are accepted on a
best-effort basis). Filer **wraps each image into a one-page PDF** — an
**A4** page (portrait for tall images, landscape for wide ones), the
image scaled to fit inside a uniform margin and centred. A large scan is
**downsampled first** — if its longer edge exceeds what an A4 page needs at
~200 DPI (≈2340 px) it's redrawn smaller on a canvas and re-encoded (JPEGs
back to JPEG at quality ≈0.85, PNGs back to PNG so text screenshots stay
crisp), keeping whichever of the original / re-encoded bytes is smaller;
an image already within budget, or one that can't be decoded, is embedded
verbatim. From then on the wrapped page is treated exactly like any other
document: it gets an index entry, a
section-divider listing, the confidential header / DRAFT watermark / lock
in the bound PDF, and a slot in its section folder in the ZIP. Building a
fresh PDF around the image is also the metadata scrub — EXIF / XMP on the
source don't travel. The File-tab row shows an "image → page" hint.
- **Office documents and everything else** — `.docx` / `.xlsx` / `.pptx` /
`.xlsm` (and the macro variants), the legacy `.doc` / `.xls` / `.ppt`,
`.wav`, and any unrecognised type. These **cannot go in the bound PDF**.
In the File-tab list the row carries a *"not a PDF — bound PDF skips it;
ZIP carries it"* badge, and the bound-PDF Compile status reports the
count of skipped files with a nudge to switch Output → ZIP. In the **ZIP
package** they are carried through into their section folder, with a
best-effort metadata scrub — see §3.b.
So: a `.jpg` of a degree transcript ends up in the bound case file; a
`.docx` working copy of a witness statement does not (but is in the ZIP);
the ZIP is the "true file copy" output.
---
## 3. Output
Picked per run via the **Output format** toggle in Case Details → Output
(default: bound PDF). Persisted per-case in `shared.outputFormat`
(`'pdf'` | `'zip'`); surfaced to the build pipeline via
`ifyiBundleOptions().outputFormat`. The File-tab Compile button's label
tracks the toggle ("Compile bound PDF" / "Compile ZIP package").
### 3.a Bound PDF (default)
- One paginated PDF, with cover sheet → clickable index → three section
dividers → per-document content.
- Same engine as Bundle Builder.
- Index entries: `"Name of Document, date (if one)"`. **No seq numbers.**
- The clickable index page lives inside the PDF **only when the bundle fits
in a single file**. If it has to be split for size (§3.d), each part PDF is
*cover → its documents* with no index page — the bundle's index travels as
`<slug>-Index.html` at the root of the wrapper ZIP.
### 3.b ZIP package
- One file per source document, in its section's subfolder, named
`NNNN Name of Document, date (if one).pdf` — a **4-digit order prefix**
(`0001`, `0002`, … restarting per section) so the extracted folder listing
keeps bundle order. The prefix is a fresh contiguous index; the hidden
`{seq}` parsed off the source filename (§4.1) is still dropped, and the
rest of the name uses the §4.5 date format. PDFs — and the image scans
Filer wrapped into one-page PDFs (§2.4) — get `.pdf`; Office documents and
other non-PDF non-image files keep their original extension
(`0007 Name of Document, date.docx` when the filename parsed, otherwise
`NNNN <raw filename>` verbatim). Name collisions inside a section folder
are de-duped (`… (2).pdf`).
- A top-level `00 Cover Letter.pdf` (only when **Include cover page** is on —
it's the §3.c cover rendered standalone) and `<slug>-Index.html`. There is
**no `01 Index.pdf`** — the standalone PDF index was dropped (v0.8): too
many in-browser PDF viewers silently swallow its cross-document GoToR links,
and one index per ZIP is enough. The bound PDF, produced as a single file,
still carries its index internally (§3.a).
- One subfolder per **non-empty** section: `Administration & Financial/`,
`Casework/`, `Documents/`. Empty sections drop out (mirrors the bound-PDF
flow — no folder, no index heading).
- `<slug>-Index.html` is the bundle's index, **rendered as a plain web page**
— section headings + one row per document (name + date) — with ordinary
relative `<a href>` links into the foldered files, so it works in any
browser or file manager once the ZIP is extracted (it's the only index in
a ZIP). Non-PDF files are listed here too, and their link opens the file in
the OS default app. Self-contained: inline CSS, system font, light/dark via
`prefers-color-scheme`, a `PRIVATE & CONFIDENTIAL` header with the client
name / reference / compile date; all values HTML-escaped. In split mode
(§3.d) it lists every document; a row whose target is in another ZIP shows
`Part N` instead of a link. The text is rendered by the browser, so
non-Latin names (Arabic, CJK, …) survive intact here even when the PDF
cover / a bound PDF's index had to fold or drop them (see §5).
- **Metadata scrub.**
- *PDFs (incl. wrapped image scans), cover letter* — built fresh with
`PDFDocument.create()` + `copyPages()` (or, for images, a brand-new page),
so the source file's `/Info` dict and any catalog XMP `/Metadata` don't
travel. Then **only** `Title` (= the document / "Cover Letter") and
`Creator`/`Producer` (`GMIAU Filer` / `GMIAU Toolkit`) are set, with
`CreationDate` **and** `ModificationDate` pinned to the moment the package
was built. No `Author`, `Subject`, or `Keywords`.
- *Office Open XML* (`.docx` / `.xlsx` / `.pptx` / `.xlsm` + macro
variants — they're ZIP containers) — re-built with the identity-bearing
metadata stripped: in `docProps/core.xml`, `dc:creator` /
`cp:lastModifiedBy` / `dc:title` / `dc:subject` / `cp:keywords` /
`dc:description` / `cp:category` / `cp:contentStatus` are blanked and
`dcterms:created` / `dcterms:modified` set to the package build time;
in `docProps/app.xml`, `Company` / `Manager` are blanked;
`docProps/custom.xml` (and its relationship + content-type override) is
removed. Macros (`xl/vbaProject.bin`, etc.) and document body are
untouched. The File-tab row shows ✓ scrubbed.
- *JPEG* (`.jpg` / `.jpeg`) — the `APP1` (Exif + XMP) and `APP13`
(Photoshop / IPTC) marker segments are removed; the scan itself is
untouched. ✓ EXIF stripped. (When image-wrapping is in play these are
handled by the wrap instead — see §2.4.)
- *PNG* (`.png`) — the `tEXt` / `zTXt` / `iTXt` / `eXIf` / `tIME` ancillary
chunks are removed.
- *Legacy Office* (`.doc` / `.xls` / `.ppt`), *`.wav`, and any unrecognised
format* — copied through **untouched**. The File-tab row shows a
`⚠ metadata not scrubbed` marker so the caseworker knows before sending,
and the Compile status reports the count.
- The **bound PDF** keeps the richer §5 metadata — it's one cohesive
document, not a scatter of loose files.
- The per-document PDFs (incl. wrapped images) still get the §5 overlay stack:
confidential header (on every body page) + DRAFT watermark when those are on.
The cover-letter PDF skips the runtime confidential header (it has its own
banner) but still gets the watermark. They are **not** individually
qpdf-encrypted — for ZIP output the "Lock for distribution" toggle protects
the ZIP file itself (see below). Non-PDF non-image files get none of the
overlays — they're not PDFs.
- **Encryption (FILER-SPEC §5).** When "Lock for distribution" is on with a
password, the **ZIP itself** is built as an **AES-256 password-protected
ZIP** (zip.js, `encryptionStrength: 3`, web workers off so it runs in the
inlined offline build) using the read/user password — or the owner password
if only that was entered. The PDFs and other files inside are *not*
separately encrypted (the ZIP password covers everything), and the owner
("settings") password isn't used — a ZIP can't carry PDF permission flags.
Off / no password ⇒ a plain DEFLATE ZIP (built with JSZip, the unchanged
path; zip.js stands in if JSZip didn't load).
- Downloads as `<slug>.zip` (`gmiauBundleNameSlug()` — same stem as the bound
PDF and the backup envelope), or as `<slug> (Part n of N).zip` per part when
the output is split (§3.d).
### 3.c Cover
Cover output is governed by these Case Details widgets (Cover page section):
- **Include cover page** — tickbox; default on. When off, the bundle skips
straight to the index.
- **Cover type** — `Short` or `Full`. Picks which fixed template renders.
- **Cover title** — `<select>`, default `(none)`. Options: `CASE FILE` ·
`ESCAPE CASE` · `FILE TRANSFER` · `FILE REVIEW` · `AUDIT` · `PEER REVIEW`
(added `CASE FILE` 2026-05-13). When set, it's stamped as a big bold
caps headline on cover Page 1 (just under the PRIVATE & CONFIDENTIAL
banner), and prefixed onto the bound-PDF metadata title
(`<COVER TITLE> — Case File of <client>, <date>`). Per-case, in
`shared.coverTitle`; surfaced via `ifyiBundleOptions().coverTitle`.
Both page layouts are **fixed** (not configurable per-field). Documented in
the Config tab → Cover content for caseworker reference. Both pages are
headed `PRIVATE & CONFIDENTIAL`; both end with a `DATE OF REQUEST` /
`DATE ACTIONED` footer (caseworker-entered in Case Details → Dates).
#### Page 1 (Short = Page 1 only)
```
PRIVATE & CONFIDENTIAL
[<COVER TITLE>, if set]
[GMIAU LOGO, if enabled — OFF by default]
CLIENT NAME: <client_name>
CLIENT REFERENCE NUMBER: <case_reference>
FIRM: <provider_name> (firm/contact block — OFF by default)
CONTACT NAME: <contact_name>
CONTACT EMAIL: <provider_email_address>
CONTACT NUMBER: <telephone>
DATE OF REQUEST: <dateOfRequest>
DATE ACTIONED: <dateActioned>
```
#### Page 2 (Full = Page 1 + Page 2)
```
PRIVATE & CONFIDENTIAL
CLIENT NAME: <client_name>
CLIENT REFERENCE NUMBER: <case_reference>
CLIENT DATE OF BIRTH: <client_date_of_birth>
COUNTRY OF ORIGIN: <country_of_origin>
NATIONALITY: <client_nationality>
CONTACT DETAILS
ADDRESS: <client_address>
EMAIL: <client_email_address>
MOBILE: <client_mobile>
CASE OPENED ON: <case_open_date>
CASE OWNER: <case_owner>
MATTER TYPE: <matter_type>
FUNDING SOURCE: <funding_sources>
LAA UCN: <ucn>
LAA UFN: <ufn>
CASE CLOSED ON: <caseClosedOn>
FIRM: <provider_name>
CONTACT NAME: <contact_name>
CONTACT EMAIL: <provider_email_address>
CONTACT NUMBER: <telephone>
DATE OF REQUEST: <dateOfRequest>
DATE ACTIONED: <dateActioned>
```
#### Field sources
Most fields come from the imported `gmiau-case-summary/1` JSON and live in
`shared.*` after `applySummary` runs (`clientDob`, `countryOfOrigin`,
`clientNationality`, `clientAddress`, `clientEmail`, `clientMobile`,
`caseOpenDate`, `caseOwner`, `matterType`, `fundingSources`, `ucn`, `ufn`,
`providerName`, `contactName`, `providerEmail`, `telephone`). Three
date fields are **caseworker-entered** in Case Details → Dates:
`dateOfRequest`, `dateActioned`, `caseClosedOn` — all three **default to
today** (a fresh case / `clearClientData` re-seeds them to today; a
resumed session restores whatever was saved, including blanks the
caseworker cleared). Clear `caseClosedOn` when the case is still open.
Missing fields render as empty rows — the cover renderer doesn't error on
absence.
GMIAU logo is sourced from `~/Documents/Work/Law_Library/60-Media/Logos/IFYI_Logo_&_Text.svg` (per `ref_ifyi_brand_logo_svg`); rendered to PNG at compile time for jsPDF.
There is **no Custom Masthead** override. Bundle Builder's masthead
override was inherited from the clone and removed in the 2026-05-10
reshape.
### 3.d Splitting into parts
Two Case Details → Output widgets (under the Output-format toggle):
- **Split the output into parts when it exceeds a size limit** — tickbox;
default off. Per-case, in `shared.splitEnabled`.
- **Maximum part size** — number, MB; default `15`, clamped `[1, 500]`.
Per-case, in `shared.splitMb`. Surfaced via `ifyiBundleOptions().split =
{ enabled, bytes }`.
When on, the compiler **bin-packs whole documents** (in bundle order,
preserving section grouping) into the fewest parts that each stay under the
limit; a single document larger than the limit gets a part to itself.
Documents are never split mid-document. Sizing is **approximate** — the
planner uses each source file's byte size as a proxy (so an image-heavy ZIP
or a freshly-wrapped scan can run a little over), with a small fixed
reservation held back per part for the cover. If everything fits in one part,
nothing is split (the status line says so).
The index always travels **once**, as `<slug>-Index.html` at the root of the
wrapper ZIP — listing every document, with a `Part N` marker (no link) for
documents that live in a different part/ZIP. The cover is repeated per part so
each part is self-describing.
- **Bound PDF** → one PDF per part — `<slug> (Part n of N).pdf` — **all
delivered together inside a single ZIP**, `<slug> (split into N parts).zip`,
so it's **one download**. The ZIP carries a root `<slug>-Index.html` whose
every row links to the part PDF that document landed in. Each part = cover
(only the sections present in that part get dividers / document pages) →
that part's documents — **no index page inside the part PDF** (the index is
the HTML one). Cover **page 1** carries a centred `Part [n] of [n]` line,
just under the `PRIVATE & CONFIDENTIAL` banner (and under the cover title if
one is set); the same suffix is appended to the PDF metadata title. If
Include-cover is off, a standalone one-line `Part [n] of [n]` banner page
stands in for it. The part PDFs are not individually encrypted; when "Lock
for distribution" is on the **wrapper ZIP** is the AES-256 password-protected
one (§5). (If neither ZIP runtime — JSZip nor zip.js — is available, the
build errors out rather than shipping loose part PDFs; if only a password
was asked for and zip.js is missing, same.)
- **ZIP package** → one ZIP per part, downloaded as `<slug> (Part n of N).zip`.
Each part ZIP has `00 Cover Letter.pdf` (with the `Part [n] of [n]` page-1
line) + `<slug>-Index.html` (the **complete index**, with a `Part N` marker
for every document so the recipient can see which ZIP each one is in; only
this part's documents get working links) + only that part's non-empty
section folders. Each part ZIP is independently AES-256 password-protected
when locking is on. The 4-digit order prefixes (§3.b) are numbered across
the whole bundle, so a document keeps the same `NNNN` in whichever part it
lands.
A split **bound PDF** is therefore a single download. A split **ZIP
package** is still one ZIP per part — multiple browser downloads in quick
succession, which the status line warns the browser may ask to allow.
---
## 4. Config tab — caseworker profile
The Config tab is the single home for the caseworker's Filer **profile**:
filename pattern + per-section overrides (§4.1), default sort (§4.2),
unparsed-file placement (§4.3), cover-content row toggles (§4.4), date
display format (§4.5), and the folder→section routing rules (§4.6). It all
persists to the One File config (`~/Documents/Work/Config/Tools-GMIAU-RB.conf`,
section `filer.*` — additive within schema `gmiau-config/1`, parallel to
existing `cases.*`) — not localStorage, so it survives reinstalls — and it
can be exported / imported as a portable `filer-profile/1` JSON (§4.7).
There is **no in-tool multi-profile dropdown** — one profile per Filer
install (= its Config). Switching to a colleague's filing style means
importing their profile JSON.
### 4.1 Filename pattern
Teaches Filer the caseworker's naming scheme so it can extract `{date}`,
`{ref}`, `{seq}`, `{name}` from each uploaded filename.
The pattern is a **token template** — literal text interleaved with
tokens. A trailing `?` on a token (`{ref}?`, or the `{ref?}` form — both
accepted) marks it optional *and* absorbs the immediately-following
whitespace, so `{date} {ref}? {seq}` matches both `240102 RB12345 001`
and `240102 001`. Tokens:
| Token | Matches |
|---|---|
| `{date}` | UK `YYMMDD` or ISO `YYYY-MM-DD` |
| `{ref}` | the generic reference form — `[A-Z][A-Z0-9-]+` or 4+ digits (`RB12345`, `A12345678`, `26542`) |
| `{seq}` | sequencing number — `\d+` |
| `{name}` | free text up to the next token / the extension |
Default: `{date} {ref}? {seq} {name}` (the GMIAU EXPORT-SPEC shape). The
pattern is edited as a template-string (not a raw regex); a live sample
preview + a collapsed view of the compiled regex sit underneath. Three
per-section overrides (Admin & Financial / Casework / Documents) let a
caseworker whose incoming `Documents/` folder uses a different scheme
(e.g. `{name}, {date}` with no ref/seq) parse that section differently —
blank = inherit the global pattern. A bad template falls back to the
built-in default at parse time, so a Config typo never breaks uploads.
Where the two numeric tokens land in the *other* order (`date · seq ·
ref · name`, as some caseworkers file), the global pattern still parses
the **date** and the **name** correctly — only the hidden `{seq}` and a
stray copy of the matter ref end up swapped into the name (a cosmetic
index-entry blemish, not a functional one). A per-section override fixes
it where the whole folder is consistent. *(Planned refinement: anchor
`{ref}` to the Case Details Matter reference and strip a stray copy of
it from the parsed `{name}`, so mixed-order folders parse cleanly without
a per-section override — not yet implemented.)* Files Filer can't
resolve at all (typo'd refs, bare-acronym names like `FOA.pdf`) keep
their full filename as the document name — a perfectly serviceable index
entry.
`{seq}` is **hidden** everywhere user-facing (index, dividers, ZIP
filenames, PDF metadata) — it only orders files within a *Sort: Name*
run. `{date}` shows in the index next to the name (display format per
§4.5). `{name}` is the document name.
### 4.2 Default sort mode
Which sort the three File-tab sections start in when a fresh case is
opened. Options: **Name** · **Date** (default — Filer organises by date
out of the box) · **Upload** · **Manual**. The user can still flip
per-session via the section's sort dropdown.
### 4.3 Unparsed-file placement
Where files that don't match the §4.1 pattern land in their section's
list. Options: **Bottom** (default) · **Top** · **By upload order**.
### 4.4 Cover content (per-row toggles)
The two cover types use **fixed page templates** (§3.c — Page 1 layout for
Short / page 1 of Full; Page 2 layout for page 2 of Full). Page 1 is
shared between Short and Full. The PRIVATE & CONFIDENTIAL header and the
Date-of-request / Date-actioned footer are always present.
Each row inside a page is independently toggleable in the Config tab.
(The GMIAU logo and the firm/contact block on Page 1 are **off by default**
— most outbound packages don't want them; turn them on per-caseworker when
the cover is going somewhere external.)
| Page | Row | Persisted key | Default |
|---|---|---|---|
| 1 | GMIAU logo | `filer.cover.page1.showLogo` | **off** |
| 1 | Client name | `filer.cover.page1.showClientName` | on |
| 1 | Client reference number | `filer.cover.page1.showClientRef` | on |
| 1 | Firm + contact block | `filer.cover.page1.showFirm` | **off** |
| 2 | Client name + reference | `filer.cover.page2.showClient` | on |
| 2 | DOB · country · nationality | `filer.cover.page2.showBio` | on |
| 2 | Contact details (address · email · mobile) | `filer.cover.page2.showContact` | on |
| 2 | Case opened · owner · matter · funding · UCN · UFN · closed | `filer.cover.page2.showCaseLaa` | on |
| 2 | Firm + contact block | `filer.cover.page2.showFirm` | on |
| 2 | Case description (free-text, from Case Details) | `filer.cover.page2.showCaseDescription` | on |
The **Cover title** (§3.c) is *not* in this table — it's a per-case value
(a `<select>` in Case Details → `shared.coverTitle`), not a per-caseworker
toggle.
Persisted under `ifyiConfig.filer.cover.{page1,page2}.*` in the One File.
Surfaced to the build pipeline via `ifyiBundleOptions().cover.{page1,page2}`.
### 4.5 Date display format
How parsed dates render in the index, the section dividers, and the
ZIP filenames. Options:
| Setting | Example |
|---|---|
| **UK long** (default) | *"12 August 2025"* |
| **ISO** | *"2025-08-12"* |
| **UK short** | *"12/08/2025"* |
### 4.6 Folder → section routing
The File tab carries a fourth drop zone — **"Sort a whole case folder"** —
above the three section zones, with a matching "Pick a folder…" link. Drop
a matter root (e.g. `26542 CLIENTNAME/`) on it and Filer walks the tree and
fans each file out to one of the three sections, in this order:
1. **Ignore-list (prune).** A list of glob patterns; a folder whose name
matches is **pruned** — its whole subtree is skipped — wherever it appears
on the path. Default: `Archive`, `Billing`, `Reviews`, `Unused`. (Any-depth
matching is what makes a nested second copy of the matter under
`Unused/…`, with its *own* `Unused/`, vanish entirely. The ignore-prune
wins over both the folder rules and the file rules.)
2. **Folder rules → section.** A list of `pattern → section` rows. The
pattern is a **glob** (`*` = any run of characters; case-insensitive)
compared against a **folder name** (one path segment). A file is routed to
the section of a rule when **any** folder on its path matches that rule's
pattern; if rules matching different segments on the same path disagree,
the one matching the **deepest** segment wins. No rule matches → the
**default section** (Config setting; default **Casework**) — so the
casework-stage folders that vary caseworker-to-caseworker and increment as
the matter progresses (`01 CW1`, `00 MCC`, `01 LH`, …) need no rule.
3. **File rules → section (override).** A second `pattern → section` list,
the pattern matched against the **filename** (basename). The first match
**overrides** the section the folder rules picked, and moves the file to
that section's **root** (subsection cleared). Use these for the
file-opening / conflict-check / legal-aid papers (`COI`, `CW1`, `ECF`,
`EOM`, `CW3`, self-grants, authorities forms, …) that often sit *loose in
a casework-stage folder* rather than in an `LAA/`-named subfolder —
without a file rule those land in Casework.
**Subsections under a routed folder.** Within the target section, the
**immediate sub-folder below the matched folder** (or below the dropped
root, for default-section files) becomes the subsection; files at the
matched folder's own level go to the section root; deeper folders flatten
into that subsection with the path tail in the displayed name — the §2.1.1
rule, just re-anchored at the routed folder. (Files moved by a *file rule*
in step 3 always land at the section root, no subsection.)
Example — a typical GMIAU caseworker profile:
| Layer | Rule | → Section |
|---|---|---|
| Folder rule | `*LAA*` | Administration & Financial |
| Folder rule | `Docs` | Documents |
| Folder rule | *(anything else)* | **Casework** *(default)* |
| File rule | `*COI*` · `*CW1*` · `*ECF*` · `*EOM*` · `*CW3*` · `*Self Grant*` · `*Authorities Form*` · `*Third Parties Form*` | Administration & Financial |
| Ignore (any depth) | `Archive` · `Billing` · `Reviews` · `Unused` *(+ `*duplicate*` if dead copies get parked under `Unused/`)* | — *(pruned)* |
Persisted under `ifyiConfig.filer.routing = { rules: [{pattern, section}],
fileRules: [{pattern, section}], defaultSection, ignore: [pattern, …] }`
(additive within `gmiau-config/1`; `section` values are the section ids
`admin-financial` / `casework` / `documents`). Surfaced to the walker only;
**not** exposed to the build pipeline — routing is purely an ingestion-time
concern, and the section drop zones (§2.1.1) ignore all of it.
**Shipped default routing** (2026-05-18, user-requested for
Compiler/Filer parity): `rules` ships with `[{pattern:'LAA*', section:'admin-financial'}]`
so dropping a real `01 Stage/` folder routes its `LAA/` subfolder
directly to Administration & Financial without the caseworker needing
to add the rule. Compiler does the same via its `inLaa` flag at parse
time (see [`COMPILER-SPEC §6.3`](./COMPILER-SPEC.md#63-administration--financial-section-added-2026-05-18)).
Caseworkers can extend or override the default via Config; the JSON
snapshot above shows both the default rule and an additional `Docs`
rule a caseworker has added.
### 4.6.1 Merged-source outline preservation (added 2026-05-18)
Source PDFs ingested by Filer that carry their own outline (a Compiler
output, a previous Filer build, anything pdftk- or Acrobat-merged with
bookmarks) keep their internal navigation in Filer's output. During the
load step Filer reads each source PDF's top-level outline via pdf.js
(`getOutline` / `getDestination` / `getPageIndex`) and stores it on the
file entry. The outline-builder then renders those entries as
**nested grandchildren** under the source doc's leaf in Filer's
section tree:
```
Outlines
├── Cover
├── Administration & Financial
│ ├── 240411 12345 001 File Opening Form FOF
│ ├── 240411 12345 002 Conflict of Interests Check COI
│ └── …
├── Casework
│ ├── 260518 12345 003 Case-Compiler-RB12345.pdf ← Compiler output dropped in
│ │ ├── 17 October 2025 — File note ← preserved from source
│ │ ├── 11 April 2026 — Client Care Letter CCL ← preserved from source
│ │ ├── …
│ │ └── 18 May 2026 — File note
│ └── …
└── Documents
```
Page-0 source-outline entries are dropped (they'd duplicate the
parent leaf). Only the source's TOP LEVEL is consumed — deeper
nesting is flattened. Wrapped images (`image → A4` per §2.4) have no
outline; nothing surfaces. The mechanism mirrors Compiler's
`_extractPdfOutline` / `subBookmarks` flow — both tools use the same
pattern, so a Compiler PDF dropped into Filer keeps its full
chronology in the final bookmark pane.
### 4.7 Caseworker profiles — export / import
The whole `ifyiConfig.filer.*` subtree **is** the caseworker's profile:
filename pattern + per-section overrides, folder routing (rules / default /
ignore), default sort, unparsed placement, date display format, and the
cover-content row toggles. It persists to the One File config, so it
survives reinstalls — and the Config tab adds **Export profile…** /
**Import profile…** actions so a profile can travel between machines and
between caseworkers.
Export writes a `filer-profile/1` JSON envelope:
```json
{
"schema": "filer-profile/1",
"name": "Camille — GMIAU",
"exported": "2026-05-11T14:00:00Z",
"filename": { "pattern": "{date} {ref}? {seq} {name}",
"sections": { "documents": "{name}, {date}" } },
"routing": { "rules": [ { "pattern": "*LAA*", "section": "admin-financial" },
{ "pattern": "Docs", "section": "documents" } ],
"fileRules": [ { "pattern": "*COI*", "section": "admin-financial" },
{ "pattern": "*CW3*", "section": "admin-financial" } ],
"defaultSection": "casework",
"ignore": ["Archive", "Billing", "Reviews", "Unused"] },
"defaultSort": "date",
"unparsedPlacement": "bottom",
"dateFormat": "uk-long",
"cover": { "page1": { … }, "page2": { … } }
}
```
downloaded as `<slugified name> filer-profile.json`. Import: file picker →
validate `schema === 'filer-profile/1'` → deep-merge into
`ifyiConfig.filer.*` → persist to the One File → re-render the Config tab
and re-parse every file already in the File tab.
**Where they live.** Recommended home:
`~/Documents/Work/Config/Filer-profiles/<Name>.filer-profile.json` —
vault-resident, so it survives reinstalls and so a colleague's profile
(built by RB from the folder structure they send) can be handed over.
Filer doesn't read this directory itself (no File System Access on this
box) — the path is just the convention.
---
## 5. Per-PDF features (cross-tool)
Every PDF Filer outputs gets the same bake-in pattern shared with
Bundle Builder + Evidence Exhibitor:
- **PDF metadata.** *For the bound PDF and the backup envelope:* Title
(= document / package name), Author (= preparer name), Subject (= ref +
party), Keywords (= ref / party / ISO date), Creator (`GMIAU Filer`),
Producer (`GMIAU Toolkit`), CreationDate, ModificationDate. Sourced from
`shared.*`. *For the loose files in a ZIP package and the per-document PDFs
the Extract tab emits:* a deliberately **scrubbed, minimal** set — PDFs
(and image scans wrapped to PDF, §2.4) get Title + GMIAU Creator/Producer
only, both dates pinned to build/extraction time, no Author/Subject/
Keywords; Office Open XML files get their `docProps` identity fields
blanked; legacy `.doc`/`.xls`/`.wav`/unknown pass through untouched with a
"metadata not scrubbed" marker on the row (all per §3.b). Loose files that
may get separated and forwarded carry no client identity in their
metadata where Filer can manage it.
- **Non-Latin text on the PDF cover / a bound PDF's index.** The cover and a
bound PDF's index render in Helvetica or Times (WinAnsi / CP1252 — Latin
only; a ZIP's index is HTML and unaffected). Before any
text is drawn, strings are passed through a safety filter: characters those
fonts can encode pass straight through; accented Latin letters that can be
folded (`é`→`e`, …) are folded; anything outside Latin-1 (Arabic, CJK,
Cyrillic, control characters, …) is dropped. This is a **hard requirement**
— without it pdf-lib throws `WinAnsi cannot encode "…"` mid-build and the
whole compile fails (which it did on a client name containing Arabic). The
`<slug>-Index.html` (§3.b) renders in the browser and keeps the original
text; the documents themselves are copied verbatim and unaffected. (When
the EB Garamond PDF font is in use the throw doesn't happen — non-Latin
glyphs render as `.notdef` boxes — but the same filter still runs, so the
cover/index stay clean.)
- **`PRIVATE & CONFIDENTIAL` page header.** Optional Case Details
toggle (`Show PRIVATE & CONFIDENTIAL stamp`, default on; ampersand
glyph, never "AND" — per `feedback_private_and_confidential_glyph`).
**Position is caseworker-selectable** — one of the six canonical
anchors documented in
[`PDF-OUTPUT-SPEC §7a`](./PDF-OUTPUT-SPEC.md#7a-body-page-overlay-positions);
default `top-centre`. Persisted via
`ifyiConfig.filer.confidentialStamp.{included,position}`. Skipped on
cover + index pages regardless of position.
- **Per-page footer** (body pages only — cover + index pages always
skipped). Three independently toggleable elements in Case Details:
`Show client reference` · `Show client name` · `Show page number`
(`p.N`, body-page-only numbering). When any element is on, the footer
renders as `<elements joined by " — ">` at the caseworker-selected
position (six-anchor enum from
[`PDF-OUTPUT-SPEC §7a`](./PDF-OUTPUT-SPEC.md#7a-body-page-overlay-positions);
default `bottom-centre`). Element-defaults + position-default live in
`ifyiConfig.filer.footer.{ref,name,page,position}` (all ticks `true`
by default). The element joiner ` — ` is silently elided around empty
values (no orphan em-dashes if a single element is empty/off). If all
three element ticks are off, the footer is omitted entirely (no empty
reserved line). Same mechanics as Compiler — see
[`COMPILER-SPEC §8.3`](./COMPILER-SPEC.md#83-per-page-footer) for
the shared design.
- **`DRAFT` watermark.** Optional Case Details toggle. Faint diagonal
across every page.
- **Encryption ("Lock for distribution").** Optional Case Details toggle +
two password fields (read/user, settings/owner). What gets encrypted
depends on the output:
- *Single bound PDF* — the PDF itself, via inline `qpdf-wasm` (same engine
as `pdf-encrypt.html`): AES-256, user + owner passwords (if only one is
set it's used for both), default permissions no-modify / no-copy /
no-annotate, printing + accessibility allowed.
- *ZIP package, and the wrapper ZIP a split bound PDF is delivered in* —
the **ZIP** is built as an AES-256 password-protected ZIP (zip.js,
`encryptionStrength: 3`, web workers off) using the read password (or the
owner password if only that was entered). The files inside aren't
separately encrypted; the owner password and the PDF permission flags
don't apply to a ZIP. Recipients need a tool that handles encrypted ZIPs
(7-Zip, WinRAR, macOS Archive Utility, `unzip` — not Windows Explorer's
built-in unzip).
Off (or on but no password) ⇒ nothing is encrypted; ZIPs are plain DEFLATE
(JSZip).
---
## 6. Shared with Bundle Builder
Reuse, not fork:
- **Case Details** scaffolding via the existing `shared` push-to-all flow.
Filer's Case Details holds: GMIAU reference · Matter reference · Client's
name · Date · Document protection (PRIVATE/CONFIDENTIAL header · DRAFT
watermark · lock for distribution). Bundle Builder's Parties block
(appellant / initials / number-parties / anonymity / respondent) and
Custom Masthead override are **not** reused — Filer's cover is driven by
Case Details + the Config-tab Cover-content toggles (§4.4).
- **Backup / Restore** envelope: continues to use `gmiau-bundle/1`
schema. Filer writes `client.case_reference` / `client.matter_reference`
/ `client.client_name` / `client.cover_date` / `client.preparer_name`;
legacy keys (`client.appeal_reference` / `client.appellant_name`) from
Bundle Builder envelopes are honoured on import for cross-tool
compatibility.
- **Theme + Settings** + the `ifyi-theme` localStorage sentinel.
- **Extract** pane — engine promoted from Bundle Builder / Evidence
Exhibitor; Filer-specific touches: it expects Filer's bound case-file PDF
(it drops the top-level Cover bookmark from the section/document lists), the
granularity nouns read "document" / "section", and extracted PDFs get the
§3.b scrubbed metadata.
---
## 7. Out of scope
- **Matter-tab variants** (the v0.2 5-recipient design — Counsel Brief,
File Transfer, LAA Compliant, File Review, Full File). Replaced by
the single matter-agnostic File tab.
- **Per-recipient address profiles** beyond Config-tab caseworker
defaults.
- **Email sending.** Filer produces the package; the user sends it via
their normal mail client.
- **Time recording.** Already handled by `filing.py` + `todo` CLI
integration (see project memory `project_ifyi_filing_time_record.md`).
- **Inbound replication.** Bundle Builder + Evidence Exhibitor cover
inbound.
- **Filesystem auto-listing** (reading the matter folder's `LAA/` /
casework-stage / `Documents/` subfolders directly, rather than the
caseworker drag-dropping). Future work pending the secure-origin flag.
- **Office → PDF conversion.** Word / Excel / PowerPoint documents are
carried into the ZIP, not converted (in-browser conversion fidelity is
poor and caseworkers keep PDF copies of the ones that matter). Image
scans *are* wrapped to PDF (§2.4) — that's an embed, not a conversion
(a large scan is downsampled to ~200 DPI on the way in).
- **In-tool multi-profile dropdown.** One profile per install (= the
Config tab); portability is via the `filer-profile/1` JSON (§4.7).
---
## 8. Build phases
Reordered 2026-05-10 around the simplified design.
| Phase | Scope | Status |
|---|---|---|
| 0 | Spec — this document. | **Done · 2026-05-10** |
| 1 | **Strip + scaffold.** Rip matter-toggle and the appeal/application/jr matter tabs out of the cloned `filer.html`. Six-tab shell: Case Details · File · Extract · Backup · Config · Settings. Inherit Bundle Builder's Case Details (minus matter-toggle) and Settings. | **Done · 2026-05-10** |
| 2 | **File tab UI.** Three upload sections (Admin & Financial · Casework · Documents). Drag-drop + file picker + folder picker per section. Folders → subsections (§2.1.1). Per-section list with the four sort modes (Name / Date / Upload / Manual) applied within each subsection. Compile-button stub. | **Done · 2026-05-10** |
| 2.5 | **Case Details reshape.** Strip Parties + Custom Masthead. Generic Matter reference. Cover-page tickbox + Short/Full type toggle in Case Details. Three caseworker-entered dates (Date of request · Date actioned · Case closed on). Imported case-summary fields cached in `shared.*` for the cover renderer (DOB, nationality, address, contact details, LAA UCN/UFN, provider/contact block, etc.). Cover layouts are fixed (§3.c) — Config tab shows a read-only description, no live toggles. | **Done · 2026-05-10** |
| 3 | **Filename parsing.** Hard-code the GMIAU EXPORT-SPEC default pattern. Parse `{seq}`, `{name}`, `{date}` on upload. Show parsed columns in the per-section list. | **Done · 2026-05-10** |
| 4 | **Bound PDF output.** Cover + clickable index + three section dividers + per-document pages. Index format `"Name of Document, date (if one)"` — no seq. Per-PDF metadata + `PRIVATE & CONFIDENTIAL` header + `DRAFT` watermark + qpdf-wasm encryption (toggles in Case Details). | **Done · 2026-05-11** |
| 5 | **Config tab.** Four caseworker preferences (§4): filename pattern · default sort · unparsed-file placement · date display format. All persisted to the One File config under a new `filer.*` section (additive within `gmiau-config/1`). Live filename-pattern preview. | **Done · 2026-05-11** |
| 6 | **ZIP output.** Output-format toggle in Case Details. Per-non-empty-section subfolders + `00 Cover Letter.pdf` + `01 Index.pdf` (GoToR links into the foldered files — *replaced by `<slug>-Index.html` in v0.8, phase 14*). Per-PDF feature stack reused; loose PDFs get scrubbed, bare-minimum metadata with build-time dates (§3.b). | **Done · 2026-05-11** |
| 7 | **Extracter** — reverse of File. Drop a Filer bound PDF, get a ZIP back (one PDF per document or per section). Engine promoted from Bundle Builder / Evidence Exhibitor; Filer-ised (drops the top-level Cover bookmark, "document"/"section" nouns, scrubbed metadata on extracted files). | **Done · 2026-05-11** |
| 8 | **Non-PDF visibility + image wrapping.** "Not a PDF — bound PDF skips it; ZIP carries it" badge in the File-tab list; bound-PDF Compile status reports the skip count + nudge to switch Output → ZIP. Image scans (`.jpg`/`.png`/…) wrapped into one-page **A4** PDFs (portrait/landscape per aspect, fit-to-margin, centred) so they flow into the bound PDF and the ZIP like any document. | **In progress · 2026-05-11** |
| 9 | **Folder → section routing.** "Sort a whole case folder" drop zone + folder picker; glob `pattern → section` rules matched on folder name at any depth (deepest wins); configurable default section; ignore-list pruning subtrees. Config-tab editor (rules table + default-section select + ignore-list). `ifyiConfig.filer.routing.*` (additive within `gmiau-config/1`). | **In progress · 2026-05-11** |
| 10 | **ZIP carries non-PDF.** Non-PDF files written into their section folder; Office Open XML `docProps` scrub (core/app fields blanked, `custom.xml` dropped) + JPEG `APP1`/`APP13` strip + PNG text-chunk strip; legacy `.doc`/`.xls`/`.ppt`/`.wav`/unknown pass through with a `⚠ metadata not scrubbed` marker + count in the Compile status; non-PDF rows in `01 Index.pdf` (GoToR to the file). | **In progress · 2026-05-11** |
| 11 | **Caseworker profiles.** Config-tab **Export profile…** / **Import profile…** (`filer-profile/1` JSON: filename pattern + per-section overrides, routing rules/default/ignore, sort/placement/date format, cover-content toggles; vault-resident). The whole `ifyiConfig.filer.*` subtree *is* the profile. *(Deferred sub-item: `{ref}` anchored to the Case Details Matter ref + stray-ref strip from `{name}` — see §4.1.)* | **In progress · 2026-05-11** |
| 12 | **ZIP order prefixes + size-limit splitting.** 4-digit per-section order prefix on every ZIP file (`0001 …`) so the extracted listing keeps bundle order (§3.b). Case Details → Output: "split into parts when it exceeds a size limit" tickbox + MB field; bin-pack whole documents into `<slug> (Part n of N).pdf` / `.zip` parts, each with the full cover (`Part [n] of [n]` on page 1) + full index (`Part N` column / link only for in-part docs); approximate sizing by source-file size (§3.d). | **Done · 2026-05-12** |
| 13 | **WinAnsi safety net · ZIP HTML index · split-PDF as one ZIP · scan downsampling.** pdf-lib's standard-font text methods wrapped to transliterate/strip non-WinAnsi characters instead of throwing `WinAnsi cannot encode "…"` (§5). `<slug>-Index.html` added to every ZIP — same index as the PDF but with plain working `<a href>` links (§3.b). A split bound PDF is now delivered as one ZIP holding the part PDFs + the HTML index, rather than N separate downloads (§3.d). Image scans downsampled to ~200 DPI before A4-wrapping (§2.4). | **Done · 2026-05-12** |
| 14 | **HTML-only index in ZIPs · split-part PDFs lose their index page · ZIP-level encryption.** The standalone `01 Index.pdf` is dropped from ZIP outputs — `<slug>-Index.html` is the only index there (rows[6]/[10] above amended accordingly). A split bound PDF's part PDFs are now *cover → documents* with no internal index page (a one-line `Part [n] of [n]` banner page if Include-cover is off). "Lock for distribution": a single bound PDF still gets PDF-level qpdf encryption; a ZIP output (package, or the split-PDF wrapper) is delivered as an **AES-256 password-protected ZIP** via zip.js (`@zip.js/zip.js`, new dep, inlined offline) using the read password — the files inside aren't separately encrypted (§3.b / §3.d / §5). | **Done · 2026-05-12** |
Phases 6 + 7 completed 2026-05-11; phases 8–11 added 2026-05-11 (v0.5) after two real caseworker folder structures came in — drag-drop of *whole case folders*, non-PDF/image content, and per-caseworker filing styles. **Remaining (post-spec backlog):** the `{ref}`-anchoring refinement (§4.1); JPEG/PNG byte-level metadata strip (dormant — images are always wrapped to PDF, which is the scrub); caseworker-folder auto-listing (File System Access API, pending the secure-origin flag on this box); the manual smoke test (load `offline/filer-offline.html` in Brave, import the gmiau-testing fixture + a real-ish folder tree, Compile both formats, round-trip a profile JSON).
---
## 9. Provenance
- **Captured** 2026-05-06 from vault plan
`~/Notes/00 Inbox/Filer plan 2026-05-06.md`.
- **v0.2** 2026-05-06 — five matter tabs (Counsel Brief / File Transfer
/ LAA Compliant / File Review / Full File). Snapshot at
`gmiau-backup/filer-redesign-2026-05-10/FILER-SPEC.md.v0.2`.
- **v0.3** 2026-05-10 — simplified to a single matter-agnostic File tab
with three upload sections; matter-toggle dropped; per-recipient
variants out of scope.
- **v0.4** 2026-05-11 — Phases 3–7 marked done; ZIP-package output (§3.b)
documented with the per-file metadata-scrub rule; Extract pane Filer-ised
(§6); §5 split into bound-PDF vs loose-file metadata.
- **v0.5** 2026-05-11 — driven by the first two real caseworker folder
structures. Added: whole-case-folder drop with glob folder→section
routing + default section + ignore-list (§4.6); non-PDF visibility +
image→A4-PDF wrapping (§2.4, phase 8); ZIP now carries non-PDF files with
per-format metadata scrubs (§3.b, phase 10); caseworker-profile
export/import as `filer-profile/1` JSON + per-section pattern overrides +
`{ref}` anchored to the matter ref (§4.1, §4.7, phase 11); §4 reframed as
the "caseworker profile". v0.4 snapshot at
`gmiau-backup/filer-profiles-routing-nonpdf-2026-05-11/FILER-SPEC.md`.
- **v0.6** 2026-05-12 — ZIP files get a 4-digit per-section order prefix
(`0001 …`) so the extracted listing keeps bundle order (§3.b); new
size-limit **splitting** (§3.d) — Case Details tickbox + MB field,
bin-packs whole documents into `<slug> (Part n of N).pdf` / `.zip` parts,
each carrying the full cover (`Part [n] of [n]` on page 1) + the full
index (`Part N` for out-of-part docs). v0.5 snapshot at
`gmiau-backup/filer-split-ziporder-2026-05-12/FILER-SPEC.md`.
- **v0.7** 2026-05-12 — robustness + recipient-friendliness pass (phase 13):
pdf-lib's standard-font text methods now transliterate / strip non-WinAnsi
characters instead of throwing `WinAnsi cannot encode "…"` mid-compile
(§5, prompted by a real Arabic client name); every ZIP gets a
`<slug>-Index.html` alongside `01 Index.pdf` — same rows, plain `<a href>`
links that work in any browser / file manager (§3.b); a split bound PDF is
delivered as **one ZIP** of the part PDFs + the HTML index rather than N
separate downloads (§3.d); large image scans are downsampled to ~200 DPI
before A4-wrapping (§2.4). v0.6 snapshot at
`gmiau-backup/filer-winansi-htmlindex-2026-05-12/FILER-SPEC.md`.
- **v0.8** 2026-05-12 — index / encryption tidy-up (phase 14): the standalone
`01 Index.pdf` is dropped from ZIP outputs, `<slug>-Index.html` is the only
index there (in-browser viewers were swallowing the PDF index's
cross-document links); a split bound PDF's part PDFs are now cover →
documents with **no internal index page** (a one-line `Part [n] of [n]`
banner page if there's no cover); "Lock for distribution" encrypts the
single bound PDF at the PDF level as before, but for a ZIP output (package
or split-PDF wrapper) it now password-protects the **ZIP** (AES-256, via the
new `@zip.js/zip.js` dep, inlined offline) rather than the PDFs inside it
(§3.a / §3.b / §3.d / §5). v0.7 snapshot at
`gmiau-backup/filer-htmlindex-only-ziplock-2026-05-12/FILER-SPEC.md`.
- **Tracked** in `immigrationfyi-tools/docs/TODO.md` and project memory
`project_filer.md`.
Run Backup before Clear or Restore — both wipe state. Backups are gmiau-bundle/1 JSON envelopes.
PDF body text is always 12 pt.
Two cover types — pick one per case from Case Details → Cover page. Both pages are headed PRIVATE & CONFIDENTIAL; both end with Date of request + Date actioned (footer is always shown). Untick rows below to omit them. Most fields populate from the imported Case Summary; missing fields render as empty rows.
Teaches Filer how to parse uploaded filenames into {date},
{ref}, {seq}, {name}. Add a
? after a token to mark it optional (e.g. {ref}?).
Default: {date} {ref}? {seq} {name} (GMIAU EXPORT-SPEC).
For the File tab’s Sort a whole case folder drop zone, in order:
(1) folders whose name matches an ignore glob are pruned (subtree and all) wherever they appear;
(2) each remaining file is sent to a section by the folder rules — a rule’s pattern is a glob (* = anything) matched against a folder name at any depth (deepest match wins); no match → the default section;
(3) then the file rules can override that — a glob matched against the filename moves the file to its section’s root, wherever it was filed (use these for file-opening / legal-aid papers sitting loose in a casework-stage folder).
The three section drop zones ignore all of this — what you drop there goes there.
Everything on this tab — filename pattern, folder routing, sort/placement/date
defaults, cover-content toggles — is this caseworker’s Filer profile.
Export it as a portable filer-profile/1 JSON to carry it between machines, or
to set up a colleague from the folder structure they send you. Keep the file in the vault
(e.g. ~/Documents/Work/Config/Filer-profiles/).
Changes save automatically. Numbering scheme applies to bundles built after the change. Exported file is named immigrationfyi-tools-<caseworker>.conf.
This tab edits a subset of the shared One File. ⚙ Edit the full configuration in Configuration Editor →