Filer assembles an outbound case file from the sections you've organised on disk. It starts with three defaults, 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), but the section list is yours to shape: add, rename, remove and reorder sections, add a stage section (Legal Help, Controlled Legal Representation, …) and as many as the case has, or just drop the matter folder and let Filer create a section per numbered stage folder (01 LH, 02 CLR, …) automatically. It 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 engine 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.
NN CODE stage folder. A casework stage (from Add stage or a whole-folder drop) appears as a paired sub-folder inside both Casework and Administration & Financial: so a stage's casework papers and its legal-aid / billing papers sit under matching folders; rename / remove / reorder a stage from its folder header and both mirrors follow. Add / rename / remove / reorder sections from the toolbar and section headers. Sort at three levels: a section's own Sort dropdown (Name / Date oldest-first / Date newest-first / Upload / Manual) covers its loose files and any folder without its own sort; each folder's header has its own sort dropdown that overrides the section for that folder alone; and Sort all folders in the toolbar applies one order to every section and folder at once (clearing per-folder sorts). Each dropped folder (subsection) has its own header controls: a / collapse toggle, a per-folder sort dropdown, a delete that removes the whole folder + its files, and the header is a drag handle: drag a folder onto another folder in the same section to reorder it (e.g. put PFC before CLR before LH), or onto another section to move it there wholesale.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, .zip and other non-PDF files show a "not a PDF" badge: in the bound PDF each becomes a one-page placeholder that names the document (the original is kept in the backup JSON); the ZIP carries the real file (metadata-scrubbed).<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, both default to today; plus a Case closed tickbox: tick it to reveal the closure date, which defaults to today), 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 a section 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); numbered stage folders (01 LH, 02 CLR, …) each become a mirrored stage sub-folder (paired under Casework + Administration & Financial) automatically, in order. Below it, your section drop zones, each with reorder (↑/↓), rename () and remove () controls, a sort dropdown and a grouped list; add more with Add stage or + Add section. You can have repeated stages and sections of the same name: pick the same stage twice (e.g. two Public Funding Certificates) or add two like-named sections and Filer keeps them as separate folders (a second instance is suffixed (2) so the output folders never collide; rename either afterwards). Removed a standard section (Administration & Financial / Casework / Documents) by accident? A Restore section dropdown appears in the toolbar listing whichever defaults are missing, pick one to drop it back in its usual place. The section zones are literal: what you drop there goes there (a folder's sub-folders become subsections). Sort a single folder with its own header dropdown (chronological / reverse chronological / name / upload / manual), a whole section with its Sort dropdown, or everything at once with Sort all folders. Re-order within a subsection in Manual mode. Image scans are wrapped into A4 PDF pages; other non-PDF files (.docx, .zip, .wav, …) are flagged and, in the bound PDF, become a one-page placeholder naming the document (original kept in the backup JSON), while the ZIP carries the real file. 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.
Rebuild editable bundle takes the same dropped PDF and loads its documents straight back into the File tab: split out and reconstructed into their sections, so you can re-order, add or remove documents and re-Compile to regenerate the cover + index. Because Filer stamped the case identity into the PDF when it built it, Rebuild reads the reference, client name, preparer and cover title straight back out of the metadata and re-fills Case Details. The clean, lossless way to re-open a case is its backup JSON (Backup & Restore); Rebuild is the safety net for when only the finished PDF survives. The only things it can't bring back are what the PDF never held, the workflow dates (date of request / actioned / closed) and the originals of any non-PDF files (a placeholder page comes back, not the original), and sub-folders are flattened to the section root, so glance over Case Details before compiling.
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 (GMIAU Light / GMIAU Dark) 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, and numbered stage folders (01 LH, 02 CLR, …) become mirrored stage sub-folders (paired under Casework + Administration & Financial) in order, a stage's loose legal-aid papers land under its Admin & Financial mirror. Your 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 / .zip / .wav files didn't make it into the bound PDF as their real content.
A. By design: the bound PDF only holds PDF pages (and image scans, which Filer wraps to A4 pages). A non-PDF file instead becomes a one-page placeholder in the bound PDF that names the document, so it still appears in the bundle and its index; the original file is preserved in the backup JSON. To deliver the real file, switch Output ZIP package, the ZIP carries it in its 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, and your stage folders aren't named NN CODE (e.g. 01 LH, 02 CLR): Casework is the default for unmatched folders. Rename stage folders to the NN CODE convention so they auto-split, or 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. I only kept the finished PDF: can I re-open it and regenerate the cover / index?
A. Yes: drop it into Extract and press Rebuild editable bundle. Filer splits the documents back out and loads them into the File tab in their sections, ready to re-Compile (which rebuilds the cover and index). It even re-fills Case Details, the reference, client name, preparer and cover title are read back out of the metadata Filer stamped into the PDF. The only things it can't restore are what the PDF never carried, the workflow dates and the originals of any non-PDF files (you get their placeholder page back, not the original), and sub-folders are flattened. For a complete, lossless re-open, keep and load the backup JSON instead (Backup & Restore).
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 (~/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.
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.9 · 2026-06-16**
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. Also offers ** Rebuild editable bundle**, loads the dropped PDF's documents back into the File tab as an editable bundle so Compile regenerates the cover + index (§2.5). |
| **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, caseworker-defined sections
The File tab is the core. Each section is an independent drop zone +
sortable list. The **section list is dynamic and per-case** (v0.9): it
starts with three defaults and the caseworker can **add, rename, remove and
reorder** sections, add a **stage** by name (a mirrored sub-folder under
Casework + Administration & Financial, §2.1.2), or let a whole-folder drop
auto-create those stage folders.
### 2.1 Sections
The three **default** sections (a fresh case opens with these):
| 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.0 Dynamic sections + stages (v0.9)
The fixed three-section model became a single ordered, caseworker-defined
list (`FILER_SECTIONS`, `{ id, label, kind }`, `kind` ∈
`default | stage | custom`). It is the source of truth for the File tab,
the Config panes (§4.1 / §4.6), the build pipeline (dividers / index /
bookmarks / ZIP folders, §3) and the backup envelope (§6). UI:
- **Reorder** any section with the header / controls (Documents is not
pinned, order is entirely caseworker-controlled, and a section's PDF
divider / ZIP subfolder follow its position).
- **Rename** () / **remove** () any section. Removing a non-empty section
moves its files to the nearest remaining section (Casework Documents
first) under a subsection bearing the old section's name, so removal never
loses files; the last section can't be removed.
- **Add stage**, a picker of the `naming.yaml` stage codes (LH, CLR, CLR2,
PFC, MCC, HO, + modifier combos). The stage's name is the **full glossary
name** (full-name-only; modifiers in parens, e.g. *Legal Help (Exceptional
Case Funding)*). A casework stage is a **mirrored sub-folder** (§2.1.2),
not a top-level section: it appears, paired, inside **both Casework and
Administration & Financial**. One case can hold any number of stages.
- **Add section**, a free-text custom section (id `custom:<slug>`).
- **Auto-detect on whole-folder drop** (§4.6), folders matching the
`naming.yaml` stage pattern `<NN> <CODE>` (`01 LH`, `02 CLR`, …) each
register a mirrored stage sub-folder, created **in `NN` order**, labelled
from the glossary. The stage's casework papers file under its Casework
mirror; its legal-aid / billing papers (its `LAA/` etc.) file under its
Administration & Financial mirror.
##### 2.1.2 Mirrored stage folders
`FILER_STAGE_FOLDERS` is an ordered per-case list of stage labels, the single
source of truth for the pair. Each stage renders as a sub-folder inside **both**
`casework` and `admin-financial` (`FILER_STAGE_SECTIONS`), as an always-present
(droppable) placeholder even while empty, and the stage label doubles as the
files' `subsection` key. The pair stays in lockstep:
- **Rename** ( on the stage folder header) relabels the list entry and rewrites
every file's `subsection` in both mirrors.
- **Remove** () drops the stage from the list and clears both mirrors (with a
confirm + file-count warning when either holds filed docs, never a silent
loss).
- **Reorder** (drag a stage folder header onto another) reorders
`FILER_STAGE_FOLDERS`, so both mirrors follow. Stage folders are pinned to
their two sections (they can't be dragged into a third).
In the output (§3) a populated stage folder nests one level under its section
(`Section/Stage/…` in the ZIP; a section divider stage sub-divider documents
in the bound PDF; a sub-heading in both indexes). Empty stage placeholders carry
no files, so they drop out of the output. The list travels in the backup
envelope (§6) as `filer.stageFolders`; an older envelope that modelled stages as
top-level `kind:'stage'` sections is migrated, those sections' files fold into
Casework under a sub-folder named for the stage.
The section set is **per-case** (it travels in the `gmiau-bundle/1` backup,
§6, as `filer.sectionDefs`); a fresh page-load starts from the three
defaults, and an older envelope with no `sectionDefs` restores into the
three defaults. The `filer-profile/1` caseworker profile (§4.7) is
unchanged, routing rules carry section ids and apply once a matching
section exists.
### 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 applies independently**: a folder can
carry its **own sort**, overriding its section's (see §2.2); manual
drag-reorder is constrained to the same subsection, moving an individual
file out of its subsection isn't supported (move the whole folder, or
remove + re-add).
**Subsection (folder) controls.** Each subsection header carries four
controls, so a dropped folder is managed as a unit:
- **Collapse / expand** ( / ), folds a long folder away to a single
header row. The collapsed state persists across re-renders for the
session (it's keyed per section + folder).
- **Per-folder sort**, a sort dropdown in the header sets *this folder's*
order alone (the same five modes as §2.2), overriding the section sort.
Stored in `fileState[sec].subSort[folder]` and travels in a Backup.
A folder with no override follows its section's sort. On a **stage
folder** the override applies to *both* mirrors (Casework + Admin &
Financial), in step with rename / remove / reorder.
- **Delete** (), removes the whole folder *and every file in it* after a
confirm. (Removing one file is still the per-row .)
- **Reorder within a section**, drag a folder header onto another folder
in the *same* section to drop it immediately before that folder.
Subsections otherwise render alphabetically; the first drag switches the
section to a **manual folder order** (`fileState[sec].subOrder`) that
persists (and travels in a Backup). The root group always stays first.
- **Move to another section**, drag the header onto a *different* section
to move the entire folder there. The moved files keep their subsection
grouping (the folder reappears as a subsection in the destination) but
are **re-parsed under the destination section's filename pattern** and
are appended to that section's folder order.
The **root group** (files at the dropped folder's top level, shown above
the subsection headers) has no header, so it has none of these controls.
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 Sort controls (three scopes)
Sorting is available at **three scopes**, each using the same five modes:
- **Name**, alphabetical on the parsed *document name* (not the raw filename).
- **Date (oldest first)**, chronological on the parsed date. Same-date
ties break by the filename `{seq}` (filing-slot order), then mtime.
- **Date (newest first)**, reverse chronological: the exact reverse of
the above (date, then `{seq}`, then mtime all flipped).
- **Upload**, insertion order (the order files were dropped in).
- **Manual**, drag-and-drop reordering. Becomes the active sort the
moment the user drags anything.
In every mode the parsed/unparsed split is **not** reversed, files with
no parsed date keep their configured placement (§4.3) at the top or bottom
in both date directions; only the dated rows flip.
The three scopes:
1. **As a whole**, a **Sort all folders** control in the section toolbar
applies one mode to *every* section and folder at once, and clears all
per-folder overrides (the whole pane adopts a single order).
2. **As a section**, each section's **Sort** dropdown orders its loose
(root) files and any folder *without* its own sort.
3. **Individually**, each folder's header dropdown (§2.1.1) overrides the
section for that one folder.
The built output (bound PDF / ZIP / index / bookmarks) walks the same
grouped, per-folder-sorted order as the on-screen list, so what you see is
what compiles.
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 sort dropdowns, 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. The token accepts three written forms,
all **year-first**, and normalises them to `YYMMDD` for the sort key and
the index:
- `YYMMDD` (the EXPORT-SPEC house form);
- ISO `YYYY-MM-DD`;
- **separated** dates `YY.MM.DD` / `YY-MM-DD` / `YY_MM_DD` and their
four-digit-year variants (`YYYY.MM.DD`, …), so a caseworker whose
house style writes `24.09.19` still gets a parsed date, a populated
index column and a working *Sort: Date*, with no batch-rename.
**Year-first is assumed.** Day-first (`DD.MM.YY`) is **not** auto-detected
and would misparse (`24.09.19` is always 2024-09-19, never 24 Sep 2019);
a per-profile *date order* toggle in Config is the future escape hatch if a
firm writes day-first. Implementation note: the `{date}` token stays a
**single capture group** (the pattern compiler indexes groups positionally),
so separators are a literal character class, not an inner group or
back-reference: and the raw match is reduced to `YYMMDD` after parse.
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), and the separated variants
`YYYY.MM.DD` / `YYYY_MM_DD` `{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`,
`.zip`, `.wav`, and any unrecognised type. Their real content **cannot be
embedded in the bound PDF**, so in **bound-PDF** output Filer inserts a
**one-page A4 placeholder** for each: the document name (the same name the
index shows, e.g. *"Twitter SAR"*) as the headline, a note that it cannot
be converted to PDF, and the original filename + size. The placeholder is
then treated like any other document, it gets an index entry, a
section-divider listing and a bookmark, so the bundle records that the file
exists even though its payload isn't inside. The original bytes are always
preserved in the backup JSON, and (when a ZIP is also produced) carried
verbatim there. In the File-tab list the row carries a *"not a PDF"* badge,
and the bound-PDF Compile status reports how many placeholders were inserted.
In the **ZIP package** these files are carried through into their section
folder with a best-effort metadata scrub (see §3.b), no placeholder is used
there, since the real file travels. (Placeholders are generated by
`_filerPlaceholderToPdf`.)
So: a `.jpg` of a degree transcript ends up in the bound case file as a
real page; a `.docx` working copy of a witness statement, or a `.zip` SAR
export, appears in the bound PDF as a named placeholder page (its real
content in the ZIP / backup JSON); the ZIP is the "true file copy" output.
### 2.5 Rebuild an editable bundle from a bound PDF
The lossless way to re-open a case is its **backup JSON** (the `gmiau-bundle/1`
envelope, Backup & Restore restores every tab, including the File-tab files).
**Rebuild** is the safety net for when only the finished **bound PDF** survives.
In the Extract tab, ** Rebuild editable bundle** reuses the Extract outline
parse (the Section Document bookmark tree with page ranges), splits each
document back out into its own PDF (`PDFDocument.copyPages`), and loads them
into the File tab: a fresh section set is reconstructed from the section
bookmarks (the three standard labels map back to their canonical ids; any other
label becomes a custom section, in the PDF's order), and each document is placed
in the section whose page range contains it. Each rebuilt document's index name
+ date are recovered from its bookmark title (`Name, <date>` is reversed through
the three configured date formats; a title that doesn't end in a parseable date
keeps its whole text as the name). Sections are set to **manual** sort so the
PDF's order is preserved verbatim. Filer then switches to the File tab; the user
glances over Case Details and re-Compiles to regenerate the cover + index.
**Case Details are recovered from the PDF metadata.** Because the bound PDF was
stamped by `gmiauApplyPdfMetadata` (§3), the identity is deterministic and read
straight back: **reference** and **client name** from the Subject
(`Case File · Ref: <ref> · <clientName>`), **preparer** from the Author, and the
**cover title** from the Title prefix (`<coverTitle>, Case File, …`, only when
it's a recognised value). Each is written back to `shared` + the Case Details
inputs (then `saveShared()` / `pushToAll()`), but only when non-empty, so a
scrubbed or foreign PDF leaves the form untouched.
Limits (it can only recover what the PDF carries, surfaced to the user): the
**workflow dates** (date of request / actioned / closed) aren't in the page tree
or metadata, so they fall back to defaults and should be checked; **non-PDF
originals** live only in the backup JSON, so a non-convertible file comes back as
its one-page placeholder (§2.4), not the original; and **sub-folders**
(subsections) are flattened to the section root. A PDF with no Filer outline
(flattened / re-saved by other software) can't be rebuilt, the user is directed
to the backup JSON.
---
## 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). A populated **mirrored stage folder**
(§2.1.2) nests one level deeper, `Administration & Financial/Public Funding
Certificate/…`, `Casework/Public Funding Certificate/…`, with its own
`NNNN ` order-prefix + name-dedupe restarting inside each sub-folder. (A
dropped sub-folder that the caseworker organised by hand nests the same way.)
- `<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 `~/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 foldersection routing rules (§4.6). It all
persists to the One File config (`~/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 with an optional trailing alpha suffix (`RB12345`, `A12345678`, `26542`, `26542A`). The suffix (`…A`, `…B`) marks a linked sub-file of the same case, so `12345` and `12345A` parse to the same case root. |
| `{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 (oldest first)** (default, Filer
organises chronologically out of the box) · **Date (newest first)** ·
**Upload** · **Manual** (stored as `name` / `date` / `date-desc` /
`upload` / `manual`). The user can still flip per-session via the sort
dropdowns (§2.2).
### 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.
**Built-in folder routing** (2026-06-16): two hard GMIAU conventions are
wired into the walker, so they work even with no folder rule configured (they
survive a stale/empty `routing.rules`), both checked after user folder-rules
(a user rule still overrides) and before stage-folder detection:
- a folder whose name starts with `LAA` (e.g. `LAA/`, `LAA and opening
stuff/`) **Administration & Financial** (so an `LAA/` inside `01 LH/`
lands there, not in the Legal Help stage section);
- a `Documents` (or `Docs`) folder the **Documents** section (the client's
documents, not casework). `rules` still also ships with
`[{pattern:'LAA*', section:'admin-financial'}]` (2026-05-18, Compiler/Filer
parity) so the rule is visible/editable in Config. 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:
`~/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.10** 2026-06-24, **mirrored stage folders** (§2.1.2). A casework stage
is no longer a top-level section but a sub-folder that appears, paired, inside
**both Casework and Administration & Financial** (`FILER_STAGE_FOLDERS` /
`FILER_STAGE_SECTIONS`), created from the **Add stage** picker or auto-detected
on a whole-folder drop, kept in lockstep on rename / remove / reorder, and
droppable (per-folder drop zone) even while empty. The output nests a populated
stage one level under its section in the ZIP and as a section-divider stage
sub-divider documents run in the bound PDF, with a sub-heading in both
indexes; the bound-PDF page-target math counts the new sub-dividers in lockstep.
Backup envelope gains `filer.stageFolders`; legacy `kind:'stage'` sections are
migrated into Casework sub-folders.
- **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 foldersection
routing + default section + ignore-list (§4.6); non-PDF visibility +
imageA4-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`.
- **v0.9** 2026-06-16, **dynamic, caseworker-defined sections** (§2.1.0).
The fixed three-section model became one ordered per-case list
(`FILER_SECTIONS`) driving the File tab, Config, the build pipeline and the
backup envelope. Caseworkers add / rename / remove / reorder sections; an
**Add stage** picker inserts a stage section named from the `naming.yaml`
glossary (full-name-only), one case can hold several; dropping a whole case
folder **auto-detects `<NN> <CODE>` stage folders** and creates a section
per stage in `NN` order. Section set is per-case (backup envelope
`filer.sectionDefs`; legacy envelopes the three defaults). v0.8 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.
01 LH, 02 CLR) each become a mirrored stage sub-folder (paired under Casework + Administration & Financial) automatically, in order. Ignored folders (Archive, Unused, …) are skipped. To place files precisely, use the section drop zones below instead.
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).
Your section drop zones ignore all of this, what you drop there goes there. Numbered stage folders (01 LH, 02 CLR, …) auto-add a section each, in order.
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. ~/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 →