# Deck & walkthrough — provenance, the version, and what is not in them

**Build pictured:** `v36-b11` · portal cache tag `makki-portal-v36-b11` · the portal's demo date is 2026-08-22.
**Build on disk today:** `v37-b5` (batch 5, time & crew — built and independently graded after the pictures were taken).
**Build published:** `v32-nb6`. Nothing in either artifact is live, and nothing was published, deployed or sent.
**For:** finish line 1, presentation readiness. Separate from, and not blocked by, launch readiness.

**On the folder name.** This work sits in `materials/deck-v37_2026-10-10/`. The `v37` in that name is the disk tag on the day the folder was made, not the build the deck shows. **The deck shows `v36-b11`.** The folder was not renamed because scripts and the frozen tree reference its absolute path; every delivered file carries `v36-b11` in its own name instead.

---

## The version statement — where it now appears

Omar's words: *"It uses the frozen v36 demo, so clearly identify that version and any later Batch 5 features it doesn't show."* The statement is in four places, three of them asked for:

| Where | What it says |
|---|---|
| **The deck, slide 2** ("How to read this") — a bordered strip under the lede, before the three label columns | *Every screenshot in this deck is of private build v36-b11, on the demonstration machine. The published site is a different, older build, v32-nb6. Nothing in this deck is live. Work built after v36-b11 is on disk today and is not pictured; the honest ledger names it under a fourth label, built since.* The slide title now reads **"Four labels. Three on every claim in this deck, one about the build."** |
| **The deck, slide 27** ("Built since this build, not pictured") — a new slide straight after the honest ledger | The headline is the version: *Every picture in this deck is private build v36-b11. The build on disk has already moved on.* The left column repeats that the published site is v32-nb6 and nothing is live; the right column is the "built since" list below. Deck is now **30 slides** (was 29). |
| **The walkthrough, top** — a paragraph in "Before you start", before the first timed section, headed *"The build you are showing — say this out loud before the first click"* | Same statement, in the presenter's voice, plus: if the presenter runs from the current disk build rather than the frozen one, Start Day and Payroll will show more than the deck's pictures. The label list under it is now **four labels**, with **BUILT SINCE** defined. The walkthrough's masthead carries a one-line version too. |
| **This file**, this section | — |

Also: the Start Day section of the walkthrough (0:20) carries a one-line aside saying it is the v36-b11 Start Day; the limits section (0:50) has a new heading *"Built since this build, not pictured"*; and the Q&A at the end has a new answer for *"Is this the latest version?"*

---

## Built since this build, not pictured — the fourth label

Since `v36-b11` was frozen at 17:10 UTC, batch 5 (`batch5-time-crew`, disk tag `v37-b5`) was built and independently graded: round 1 `SCORE ok=128 fail=1` (the one fail a defect in the Builder's own test, not the app), round 2 after a test-only repair `SCORE ok=129 fail=0` — the round-2 figure is the Builder's claim and the Checker's r2 grade is the word. Read from `hive/checks/2026-10-10_batch5-time-crew.md` (the criteria) and `hive/checks/2026-10-10_batch5-time-crew.r1.grade.md` (the grade), these exist on disk today and **are not in any screenshot in the deck or the walkthrough**:

1. **Start Day — a signature on the timesheet itself.** The site lead signs a man's line with a PIN. The PIN is checked and never kept; the record shows who signed, when, and the last two digits only (*"Signed Dale Ferris · 2026-10-10 18:26 · PIN ending 17"* in the grade). A wrong PIN leaves the row unsigned. (F13; rows 58–63; browser-proved in the Checker's `r1c` probe and the Builder's r2 suite.)
2. **Start Day — the bulk crew-time control** with the company's cost codes on it: one control puts the hours and a cost code on every man on the sheet; the codes offered are `LAB, EQP, DSP, ABT, SUB, PRM, OTH`; another crew's sheet is refused. (V39; rows 40–57.)
3. **Start Day — the three head starts come through the gated reader.** From the board, from the previous day, or flat: the men, their hours, their cost codes and the basis they came from, printed on the page. The board's per-slot man list is now data in a seed (`portal/seed-time-crew.js`), gated like the roster, rather than a note kept in one device's localStorage. (GL-001 · M-HEAD · F24 · V38; rows 24–39.)
4. **Payroll — four more filters** on the payroll screen (`app-ops.js`): office, project type, verify status and approval status, beside crew; filtering by office narrows the list (browser-confirmed *"Airdrie yard (demo): 12 -> 4"*). Payroll by cost code now splits across codes instead of one `LAB` line. The 32 / 40-hour overtime thresholds are read from the seed and are overridable, instead of being literals in `payrollWeek`. (GL-103 · F11 · A5 / A11; rows 16–23, 85–94.)
5. **Job costing — two new owner-only columns**, *Temporary labour* and *Contracted labour*, partitioning the job's existing `LAB` spend by hours share — never an addition on top. (F18 · A10 / F10; rows 69–77.)
6. **The day link — a man enters his own hours** on his no-login page; another man's line is refused; an expired link still refuses; no money key reaches the page. (F54; rows 78–84.)
7. **A time record carries more fields:** cost code, break, per diem per man per day, asset time, labour kind, signature. (F14 · A8 · F8; rows 40–49, 64–68.)
8. **Readers with no screen of their own yet:** a per-man week reader (`myWeekFor`, still *"not sent"* / *"In-app only"*), and an estimated-hours fixture with a *"near estimated hours"* reader (`nearingBidHours`) — the fixture is labelled synthetic and F5 / F6 stay PARTIAL behind the owner's workbook.

Every one of those figures is a **synthetic demo fixture that says so on its own row**; the batch did not touch `portal/data.js`. The real crew assignments, cost codes per man, estimated hours, per-diem policy and signature policy are still the owner's to give. Nothing in batch 5 sends anything; GL-023's texting half is Omar's.

**The Start Day on slide 14 is the `v36-b11` Start Day** — the un-started screen with the board's men ticked and the three start buttons. It has the cost-code dropdown per man and the "same as previous day" head start that existed before batch 5; it does **not** show the signature panel or the bulk control, which only appear on the started day and only in `v37-b5`.

The slide and the walkthrough carry items 1–7 (the slide folds 7 into 6). Item 8 is in the walkthrough only — it has no screen to picture even on the current build.

---

## Where the screenshots came from

Every screenshot was shot from a **frozen copy**, never from `portal/` — a Builder was editing `portal/` throughout.

| | |
|---|---|
| Frozen source | `materials/deck-snapshot-v36-b11_2026-10-10_1710/` (219 files) |
| Tree actually served | `materials/deck-v37_2026-10-10/_tree/v36-b11/portal/` — a byte-for-byte copy of that snapshot **plus** `brand/`, which the snapshot did not carry |
| Proof it is the same build | `roles.js` md5 `a3cdce0e97b1ad9277805bb547381891` in both; `sw.js` reads `makki-portal-v36-b11` in both |
| Shooter | `materials/ali-guide/shoot.cjs`, reused unchanged, pointed at the frozen tree |
| Browser discipline | one Chromium at a time, `the box's browser lock` held for each run (see the lock note below for the one defect in that) |

**Why `brand/` had to be added.** The frozen snapshot has no `brand/` folder, so the first run rendered a **broken logo image and fallback system fonts** on every page. Those shots were discarded and the run restarted against a tree carrying `portal/brand/` (logo + the two brand woff2 files). Nothing in `portal/` was written to; the brand folder was copied out of it, read-only.

---

## Could not be captured, and why

| | |
|---|---|
| **The owner's Safety page at phone width** | ~50,000 px tall at 390 px wide. Beyond any full-page capture on this box. The shooter tiles and caps it; the deck uses the **desktop** Safety screen instead |
| **The tasks page at phone width** | ~62,000 px tall. Same limit. Not used in either artifact |
| **Full-page phone shots generally** | several needed viewport-tiled stitching, and the log records repeated "unpainted band" retries. Where a tall phone page was wanted, the deck uses a **section or the viewport**, not a full-page shot |
| **Anything live** | the published site is still `v32-nb6` and nothing has been published since 2026-10-04. Every shot is of the **private build on this box**, not of anything on the internet |

---

## The 404 in the render log — identified and removed

The first render log recorded `console: Failed to load resource: 404` on the deck's PDF render and nothing on the walkthrough's. A static walk of every `src=`, `href=` and `url()` in the deck HTML found every referenced file present. The render script was then given server-side logging of every path it answers with 404, and re-run:

```
server: 404 for /favicon.ico  (requested by the browser; not in the page's own asset list)
```

**It was Chromium's automatic request for `/favicon.ico`.** The deck ships no favicon and the little render server had nothing to answer. It never affected a rendered page: a favicon is not drawn into a PDF, and the walkthrough page (same origin, loaded second) showed no 404 because the browser remembers a missing favicon per origin for the session. The request is made by the browser process, which is why the page-level `response` listener never saw a 4xx — only the server log could name it.

**Fixed, not just explained:** both HTML sources now carry `<link rel="icon" href="data:,">`, which tells the browser there is nothing to fetch. The final render log reads, for both files, *"no console errors, no failed requests, no HTTP status ≥ 400"*, with the server logging no 404 at all.

---

## For Omar — two findings in `v36-b11`, both `portal/` edits for the Builder on his word

Neither was fixed here: this work writes only under `materials/`, and `portal/` has another writer. Both are present unchanged in today's `v37-b5` (grep-confirmed; the line numbers below are identical in `portal/` as of 18:48 UTC).

### (a) "Fieldflo" and "EazyLife" in user-facing text

A competitor's name and the agency's name appear in the app's own copy, on screens a client would be shown. **Twelve strings in source, fifteen name occurrences** (comments excluded). A browser scan of the frozen build — every login, every nav page, every nested tab, plus the site lead's started day — rendered **21 occurrences in 19 distinct sentences**, because the same string prints on more than one page. The scan is in `build/wordscan.md` / `wordscan.json`.

| # | File : line | Text (abridged) | Where it renders — browser-verified |
|---|---|---|---|
| 1 | `app-dash.js:283` | *"Fieldflo's four tabs · what is running today, the safety register, the machines and the pipeline"* | **Owner › Dashboard**, the sub-line under the page title |
| 2 | `app-board.js:114` | *"Nothing is texted — Fieldflo texts him, here the office phones him."* | the roll-call panel on **Owner › Dashboard**, **Site lead › Dashboard** and **Site lead › Start Day** |
| 3 | `app-board.js:125` | *"Fieldflo texts the no-show; here the office phones him."* | the **Roll Call modal** (opened from that panel; the scan does not open modals, attribution is from source) |
| 4 | `app-ops.js:352` | *"Text-out is simulated. Fieldflo texts each man his week… Texting is switched on later by EazyLife."* — **two names** | **Owner › Schedule Board** and **Site lead › Crew Board**, under the week grid |
| 5 | `app-ops.js:385` | *"When EazyLife switches texting on, this same button sends these and logs the replies."* | the text-preview sheet opened from the board's text-out button (source attribution) |
| 6 | `app-ops.js:393` | *"In-app only — the text version is switched on later by EazyLife."* | **Crew › My Schedule** |
| 7 | `app-ops.js:219` | *"This is the question Fieldflo sends by text… Texting comes later, once EazyLife switches it on"* — **two names** | **Crew › End of Day**, the band over the question |
| 8 | `app-ops.js:177` | *"In-app demo. Fieldflo texts the man… Texting comes later, once EazyLife switches it on"* — **two names** | **Site lead › Start Day, once the day is started**, the End-of-day confirmations panel |
| 9 | `app-ops.js:158` | *"32+ is the early overtime flag (same rule as Fieldflo's)."* | **does not render** — `app-day.js` replaces that timesheet panel with its own on both the un-started and the started day. Dead copy, still worth deleting |
| 10 | `app-ops.js:578` | *"Same map Fieldflo showed on the call — plus what our seats do with it overnight."* | **Owner › Money › QuickBooks**, "What would sync" panel head |
| 11 | `ops.js:167` | *"CSV import … works without the link (same as Fieldflo's fallback)."* | **Owner › Money › QuickBooks**, the Fallback line |
| 12 | `ops-forms2.js:67` | *"The asbestos-specific set Fieldflo showed: the manometer, the two-stage waste load-out and the AHERA credential check."* | **Owner › Safety** (inspection sets) and **Site lead › Start Day** |

Fieldflo 11 occurrences, EazyLife 4 — fifteen. One further Fieldflo mention was in the **walkthrough script itself** (the presenter's line at Team & Access, *"This is the Fieldflo 'toggle it live' move"*); it was reworded today to *"the 'toggle it live' move from the sales call we sat through"*. The deck carries neither name (grep: 0).

**What the deck does about it:** the owner's dashboard is cropped below the sub-line, the Crew Board is cropped above its note, the End of Day shot is cropped to the question block, and the QuickBooks screenshot was dropped entirely (slide 23 makes the point in words). The Start Day shot is the un-started screen, which carries none of them.

### (b) An oversized icon in `.muted` notes — five screens, one cause

An inline SVG from `icon()` (`app.js:60`: `<svg viewBox="0 0 24 24" …>` with **no width or height**) is placed directly inside a `div.muted`. `app.css` sizes `svg` only inside specific containers (`.live-note svg`, `.gate-note svg`, …); there is no rule for `.muted svg`, so the icon takes the container's full width and renders **1070 × 1070 px** at desktop width, pushing everything under it about a thousand pixels down. Browser-measured on the frozen build:

| Login › page | Panel | Source |
|---|---|---|
| **Owner › Money › Collections** | "Completed and eligible to bill" — the *Synthetic demo fixtures* note | `app.js:912` |
| **Bookkeeper › To Invoice** | "The rule" — the same note | `app.js:1215` |
| **Owner › Safety** and **Site lead › Start Day** (and again on the started day) | "End-of-day verify — meal / break and injured" — *"Nothing is texted. The man answers in the app…"* | `app-forms2.js:389` |
| **Site lead › Start Day, day started** | "Evacuation plan — J-240175" — *"Edited on the project — Project Book → the job → Emergency"* | `app-jha.js:82` (`div.muted.jh-note`) |
| **Crew › End of Day** | the verify block — *"Answered on this device only. Nothing is texted or sent…"* | text `ops-forms2.js:147` (`EOD_NOTE`), rendered through `roles.js:3113` |

The first two were seen while shooting; the other three came out of the browser scan. **One rule fixes all of them** — something like `.muted svg{width:14px;height:14px;vertical-align:-2px;flex:0 0 auto}` in `app.css` (charts and maps carry their own classes and `role="img"`, so they are untouched). Not applied here. The deck crops to the tables below the icon, so no slide shows it.

### Also worth a decision, not a defect

The owner's personal name appears in roughly fifty user-facing strings of `v36-b11` (fifteen in the app files outside `data.js`, the rest in the data), including the signature line on printable proposals and receipts (*"Makki Demolition — The owner"*). Correct on a real document; worth a deliberate choice on a demo. The deck carries no owner name; the walkthrough does, because it is the presenter's own script.

---

## The browser lock — a defect in my own render script, disclosed

The Builder's round-2 log records the lock *"vanished twice (~18:26:29 and ~18:27:58) and was re-taken… nothing on disk under tools/, hive/, portal/_qa/ deletes it."* The first of those is the runner's own first take after its suite released; the second is very likely mine. The render script used at 18:28 had two faults: it released the lock on process `exit` **unconditionally**, so the path *"another workload took the lock first — stopping"* (`exit 2`) deleted the other seat's lock on its way out; and it decided the box was free on a single zero-browser reading, which a suite that relaunches Chromium between tables can produce for a moment. The render log for that run is overwritten by the next run, so I cannot show the exit-2 attempt itself; the timing fits and the code fault is certain. My deck PDF finished at 18:28:31 while the Builder's measure ran to 18:28:26, so **a short overlap of two Chromiums is possible**; neither side crashed and both runs completed clean, but it should not have been possible.

**Replaced:** `build/boxlock.cjs` creates the lock atomically (`wx`) with the owning pid, requires lock-free **and** zero foreign browser processes held for 20 s before taking it, and releases **only a lock carrying its own pid**. `render_pdf.cjs` and `wordscan.cjs` use it; the three browser runs this afternoon (18:41 scan, 18:44 and 18:45 renders) each logged *"taken … after 20 s"* and *"released"*. The lock is free and no Chromium of mine is running.

---

## Deliberate choices in the deck

- **No date, no price, no owner's personal name** anywhere in the deck.
- **EazyLife is not named.** The deck is Makki's, in Makki's colours, under Makki's logo.
- **Dollar figures are kept out of the body copy** and left to appear only inside screenshots, which carry the app's own `MOCK DATA` chip and `DEMO DATA` band. Counts (4 eligible / 8 awaiting, 578 jobs, 18 reports) are used instead, because they are verifiable and cannot be mistaken for real money.
- **Four labels** — working, simulated, planned, and now **built since** (on disk after the pictured build, not pictured). The first three are on every claim; the fourth is on slide 2 and slide 27 only, and is never used to call anything working.
- The walkthrough names the owner because it is the presenter's own script; the deck does not.

---

## Figures used, and where each was read

Every number below was read from the frozen build by running its own code, not from memory or an older document.

| Claim | Source |
|---|---|
| Six logins: owner, book, est, lead, crew, manager | `roles.js` `ROLES` and `MATRIX_ROLES` |
| 23 owner pages in 7 groups · book 4 · est 5 · lead 12 · crew 5 · manager 6 | `roles.js navFor()` / `navGroups()` — and the browser scan walked exactly those counts |
| 18 in-app reports; manager 17 | `roles.js reportList()` |
| 14 job panes | `roles.js PANES` |
| 9 ticket types | `certs.js` types |
| 578 jobs · 1,396 quotes · 14 people · 3 crews · 54 certificates | `data.js` / `certs.js` |
| 4 eligible to bill · 8 awaiting completion | `roles.js billingSummary()` |
| 4 interface languages · 6 voice-note languages | `lang.js LANGS` · `roles.js voiceLangs()` |
| "MOCK data — figures are illustrative, not real dollars" | `data.js meta.note`, the source workbook's own wording |
| The batch 5 list above | `hive/checks/2026-10-10_batch5-time-crew.md` §0–§3 and `.r1.grade.md`; nothing in it was read off a screenshot, because there is none |

---

## The packaged files

Handoff folder: `materials/handoff/2026-10-10_<HHMM>/` (the stamp is the UTC minute the folder was written). `zip` is not installed on this box, so the files are handed over individually; each is well under 50 MB. Sizes and md5s are in `MD5SUMS.txt` beside them.

| File | What it is |
|---|---|
| `DRAFT_Makki_Company_Portal_deck_v36-b11_2026-10-10.pdf` | the deck, 30 slides, 1920×1080 |
| `DRAFT_Makki_Company_Portal_deck_v36-b11_2026-10-10.html` | the same deck as one self-contained HTML file (fonts, logo and every screenshot inlined; opens offline from any folder; arrow keys to move) |
| `DRAFT_Makki_One-Hour_Walkthrough_v36-b11_2026-10-10.pdf` | the presenter's script, A4 |
| `DRAFT_Makki_One-Hour_Walkthrough_v36-b11_2026-10-10.html` | the same script as a self-contained HTML file |
| `DRAFT_PROVENANCE_v36-b11_2026-10-10.md` | this file |

The earlier folder `materials/handoff/2026-10-10_1710/` holds the superseded `_v1_` files from 18:30 (no version slide, the favicon 404, the competitor's name in the script). It is left in place as history and should not be sent.

---

## v2 — the delivery folder and one layout repair

**The delivery folder is `materials/handoff/2026-10-10_1710/`**, the folder the brief named. Its files carry `_v2_v36-b11` in the name. The earlier `_v1_` files that sat in that folder (18:30 — no version slide, the favicon 404, the competitor's name in the script) were moved, not deleted, into `2026-10-10_1710/superseded_v1/`. The `2026-10-10_1849/` folder is the 18:49 finish and is left in place as history; its walkthrough files are byte-identical to v2, its deck differs from v2 only by the repair below.

**The repair:** on a second visual check of the rendered PDF (slides 5, 7, 11, 17, 22, 30 rendered to PNG), **slide 17, the crew's phone slide, overflowed its canvas** — the eyebrow was clipped off the top edge and the End of Day caption ran into the footer. The render script's "fits the canvas" check had not caught it. Fixed in the deck's CSS only (the phone slide's heading, lede and list sizes brought down one step, the End of Day shot narrowed from 430 to 340 px), re-rendered under the browser lock, and re-checked by eye. No words changed. The other five checked slides were clean.

**What this seat did not write:** `hive/READ_LOG.md` and `hive/EXECUTION_LOG.md`. The brief for this work confined writes to `materials/`; the read of `vault/STATUS.md` (18:09 UTC version, batch 5 graded r1) and of the frozen `roles.js` happened, and the Chief of Staff logs it.

**Browser discipline on this pass:** two renders, each through `boxlock.cjs`. The first waited 60 s because the Checker's `r2c wheel_test` held `the box's browser lock`; it took the lock only after that run released it and the box had been quiet for 20 s. Both released their own lock; the lock is free and no Chromium of this seat is running.

---

## What neither artifact does

Nothing here was published, deployed, sent, or shown to anyone. Live stays `v32-nb6`. The files sit in the handoff folder and go no further without Omar's word.

---

## v3 — 2026-10-10, after Codex's review

**Why.** Codex's local review of the v2 PDFs (relayed by Omar at 22:10 UTC) found six things the deck and the guide claimed that this build cannot support. Every one of them is a wording correction; **no screenshot changed, none was re-shot, and the build pictured is unchanged: still `v36-b11`**, the same 22 JPEGs in `assets/shots/` with the same md5s as v2. Nothing in `portal/` was touched.

**What changed, and where.**

| Codex finding | Where it was | What it says now |
|---|---|---|
| 1 · Deck p4/6/7/29 and guide p10/12 promised that forbidden fields cannot be read from a browser console, in a static browser-only prototype | deck slides 4, 6, 7, 29; walkthrough 0:39 (the bookkeeper) and the Q&A | Each place now describes a **demonstrated role-specific projection, built in the browser, with no server behind it**. The universal "cannot leak / cannot be read / console" guarantee is gone from both files (grep: 0 "console" in the deck text, 0 in the walkthrough). Server-enforced authorisation is named **not built and not verified**. The p6 and p29 chips read "Not built — server-side …" |
| 2 · Deck p29 and guide p10/12 called backend, database and sign-in hosting decisions rather than build gaps; deck p30 said only the real data and "a place to put it" remained | slide 29, slide 30, the walkthrough's planned table and "Is this live?" answer, the close quote | Server-side sign-in, database and backup are a **build gap needing engineering, deployment and verification**, as well as the owner's decisions. Slide 30 and the close now name the remaining work: **AI implementation (the seats), integrations, security, pilot testing, real data and human inputs** |
| 3 · Deck p16 guaranteed nobody enters a site without a ticket and spoke of live tickets | slide 16 | The title is now the check itself ("The ticket is checked before a man is put on a crew"); the bullets say **assignment check, in this build, against demo data**; the QR line says it reads the demo data on this machine. The slide was also **enlarged for projection**: its own scoped class `xl` (image column 1060 px, bullets 30 px, caption 26 px) — no other slide's layout changed |
| 4 · Guide p12 called outgoing texts a decision, not a feature; deck p26 and guide p10/12 said QuickBooks "needs login" | slide 23, slide 26, slide 28; walkthrough 0:12 QuickBooks, 0:20 day links, 0:33 messages, the planned table, two Q&A answers | Outbound texts and emails are **deferred, unbuilt engineering scope**. QuickBooks: **the owner authorises a supported connection on the QuickBooks side and never shares a password or credential; Makki implements and tests the integration; provider and setup details are pending validation** |
| 5 · Keep the 60-minute six-role flow and the v36 disclosure; enlarge p16 | — | All nine timed sections are unchanged; the version statement on slides 2 and 27 and in the walkthrough is unchanged; p16 enlarged as above |
| 6 · Editable exports, runnable demo, dependency checklist, review evidence | the handoff folder | New in v3: a `.pptx` (one slide per deck section, same order, the deck's own screenshots, walkthrough passages as speaker notes), a `.docx` and `.md` of the walkthrough, a private-demo zip of the frozen `v36-b11` tree with a `README_RUN.md`, a dependency checklist and a review-evidence table |

Smaller wording in the same spirit, not on Codex's list: slide 2's planned column says "not built" / "unbuilt"; slide 5's "what it will never be sent" is now "what this build leaves out of its projection"; slide 8's lede says "in this build"; the walkthrough's opening line no longer says "not a feature list" (the phrase "not a feature" is one the review flagged in another sentence, and the guide should not carry it anywhere); the 0:00 script line about seats says "left out of the projection … in the browser; there is no server behind it yet".

**One pipeline change, disclosed.** `render_pdf.cjs` printed its all-clear as *"no console errors, no failed requests, no HTTP status ≥ 400"*. The v3 criteria read the render log for the words "failed request", so the all-clear line itself would have counted as a failure. The line now reads *"clean: no console errors, no request failures, no HTTP status ≥ 400"*. Nothing else in the script changed; the lock, the server, the 404 logging and the overflow check are as in v2. The instrument was not touched.

**Not verified here.**
- Whether the `.pptx` opens in PowerPoint or Keynote and whether the `.docx` opens in Word — there is no Office on this box. Both are built as valid OOXML packages (the pptx by pptxgenjs 4.0.1, the docx by hand with jszip) and the checks read their XML, nothing more.
- The private-demo zip was built from the frozen tree and its file list, `roles.js` md5 and `sw.js` tag were checked; it was **not** served and clicked through again after zipping. The screenshots were shot from that same tree earlier today, so a served copy should match them.
- The enlarged slide 16 was rendered to `_check/slide-16.png` and looked at by this seat; that is one pair of eyes on one render, not a projection test in a room.
- Server-side authorisation, a database, a backup, QuickBooks, outbound sending: none of it exists in this build, and nothing in v3 claims otherwise.

Nothing in v3 was published, deployed, sent, or shown to anyone. Live stays `v32-nb6`.
