DooPlex gates at the pushed commit796a9fdf: rc=0, all 18 OK. A plain rebuild there left git status --porcelain -- marketing/ empty, so the committed PNGs are exactly what the committed script produces on the build machine; both controls fired in that run. CI gates.yml #849 for796a9fdf40is green. Recorded how it was read, because both obvious ways are wrong here: the Gitea API still 401s on every credential in the store, and the run's own page redirects to the internal id and renders through JavaScript, so its HTML carries "success", "failure", "running" and "cancelled" as template strings whatever the outcome. The conclusion comes from the list page, from the same flex-item row that holds the commit link -- and splitting that list on class="flex-item" chops the row, because the child divs share the prefix, which would attribute the icon to a neighbouring run.
12 KiB
REPORT — R-919: the Facebook cover rebuilt to the measured phone view (2026-10-09)
A REPORT-<topic>.md sibling; the shared REPORT.md was not touched. Evidence for the measurement it
builds on: documentation/audits/facebook-page-setup-2026-10-08/.
1. Baseline and commits
| Baseline | felhom.eu main = a76207945e, clean on the Windows tree and on DooPlex |
| Pushed | 796a9fdf to main (no branch, explicit paths, no Co-Authored-By line) |
| Repos touched | felhom.eu only — marketing/facebook/, marketing/CHANGELOG.md, documentation/backlog/OPEN-ITEMS.md, this report |
| Built on | DooPlex (Pillow 11.1.0), not the workstation — see §4 |
2. Scenario A — the red-proof, with its numbers
The measured geometry was run against the three covers as committed at a76207945e, by importing
build.py unchanged and overriding only the constants, so the content was today's. All three were
convicted. Phone window: the centre 938,3 px of 1640 (x 351 … 1289). New SAFE (391, 40, 1249, 584).
New phone QUIET (613, 345, 1054, 624).
| cover | content box | past the left edge | past the right edge | content pixels under the phone circle |
|---|---|---|---|---|
| cover-a.png | (368, 184, 1271, 303) | 368 < 391 — 23 px | 1271 > 1249 — 22 px | 0 |
| cover-b.png | (328, 44, 1307, 567) | 328 < 391 — 63 px | 1307 > 1249 — 58 px | 1017 |
| cover-c.png | (328, 108, 1331, 455) | 328 < 391 — 63 px | 1331 > 1249 — 82 px | 1778 |
Those content boxes are identical to the ones the unmodified build prints for the committed covers, which is the control that the red-proof really did run against today's content and not against a redrawn one.
The red-proof is now permanent, not a one-off. control_old_window() runs on every build, beside the
circle check's own control: it draws the headline where the old 640 × 360 assumption put it (x 328) and
the safe check must reject it. The build prints the two numbers that differ, because they are easy to
confuse: the phone's crop edge is x 351, so 23 px of the headline were actually cut; the measured
safe edge is x 391, 63 px further in, which is the 40 px MARGIN on top of the crop.
3. Scenario B — the rebuilt covers
Canonical build (DooPlex). BAND = (408, 48, 1249, 340) — inside SAFE, right of the computer circle,
above the phone circle — derived from the constants, so a re-measurement moves the designs with it.
| cover | content box | headline size → capital height | under the computer circle | under the phone circle |
|---|---|---|---|---|
| cover-a.png | (424, 172, 1231, 287) | 57 → 45 px | 0 | 0 |
| cover-b.png | (420, 52, 1235, 575) | 58 → 45 px | 0 | 0 |
| cover-c.png | (420, 52, 1243, 339) | 47 → 37 px | 0 | 0 |
CAP_MIN is 25 px (4 % of 624); the smallest headline is 37 px, about 48 % clear of the rule. Measured
clearance inside the phone's own crop edges: 69–73 px on the left, 49–57 px on the right — the 40 px
margin plus the layout's own air.
What changed in each design:
- A — calm. One centred line, now fitted to the band rather than the old wide safe area, with the domain under it. Nothing else.
- B — the idea. Headline left; the home, its server and its app tiles right, scaled to the band. Two of the seven tiles sit in the lower-right strip (x ≥ 1054), which is the one area below the band that a phone still shows beside the profile circle — so the composition keeps its lower half instead of ending at y 340.
- C — the product. Headline left; the dashboard right, its screen 470 → 370 px wide so the frame and
its base fit between the text and the right crop edge. The screenshot's text was never legible at cover
size (it renders ~280 px wide on a 1250 px Facebook cover, and did before at ~358 px); it still reads as a
real product dashboard, with the sidebar, the tables and the status badges visible. It was not dropped
— §8's fallback — but the operator picks, and
out/preview.htmlshows all three.
The computer view was checked by eye on the rendered PNGs as well as by the numbers: the band sits in the cover's upper half by necessity (the phone hides the bottom middle), so each cover's glow was moved down to carry the lower half instead of leaving it flat.
4. Profile pictures — untouched, proven
before 936cb0386c8fb46c6f9f7ba7ac97b91c1d89fafef05d86f6a7ad9a4d951ec471 profile-dark.png
f528e0820ff4e527e03d4a0a909ae74ca09d715d0ae4faff89cd3c01ea4fc5b7 profile-white.png
after 936cb0386c8fb46c6f9f7ba7ac97b91c1d89fafef05d86f6a7ad9a4d951ec471 profile-dark.png
f528e0820ff4e527e03d4a0a909ae74ca09d715d0ae4faff89cd3c01ea4fc5b7 profile-white.png
Identical, and neither file appears in the diff. This only holds because the build ran on DooPlex. The
workstation has Pillow 12.3.0; DooPlex has 11.1.0, and the two render the same text a pixel or two
differently — a rebuild on Windows rewrites all six PNGs, profile pictures included, and shifts the
measured content boxes (cover-a's right edge 1235 vs 1231, cover-c's fitted headline 45 vs 47). DooPlex
reproduces the committed bytes exactly (a rebuild there leaves git status clean), so it is the build
machine; the README now says so. Pillow was installed on the workstation for this work and used only to
iterate on the design, never for the committed output.
5. The rebuilt files
| file | size | Gitea |
|---|---|---|
marketing/facebook/out/cover-a.png |
1640 × 624, 59 KB | gitea.dooplex.hu/admin/felhom.eu/src/branch/main/marketing/facebook/out/cover-a.png |
marketing/facebook/out/cover-b.png |
1640 × 624, 54 KB | …/marketing/facebook/out/cover-b.png |
marketing/facebook/out/cover-c.png |
1640 × 624, 97 KB | …/marketing/facebook/out/cover-c.png |
marketing/facebook/out/preview.html |
1295 KB, self-contained | …/marketing/facebook/out/preview.html |
Each PNG's size was read back from the file by the build, not taken from the constants.
6. What the operator does, in plain words
- Upload the new cover. Open
out/preview.html(download it from Gitea; it opens on its own), pick a cover — the same option as now unless the preview gives you a reason to change — and upload it to the Page by hand, the way you did last time. About a minute. The pictures cannot be set by robot (R-914). - Then check it on your phone, in the Facebook app. Open the Page and look at the cover: is the second line „saját szabályaid" whole, and is „felhom.eu" whole? This is the real test. What was measured was the phone website in a simulator, on one device. If the app cuts it differently, say so and the numbers move — the build follows them.
Nothing else changed: the profile picture, the texts, the button and the Page name are as they were.
7. Register
Rows before 140, after 140, opened 0, closed 0 — counted the way register_shape_gate.py
counts. R-919 moved OPEN → VERIFY and stays in OPEN-ITEMS.md deliberately: the build is fixed, the
result is not proven until the operator looks in the Facebook app, so it is not a finished row and does
not belong in CLOSED-ITEMS.md yet. Its What now carries the fix and the red-proof numbers; its Next action
is the operator's two steps and what to do if the app still cuts the cover.
8. Observations
- The computer profile circle no longer overlaps the cover at all (measured 2026-10-08: the cover ends
at y 531 and the circle starts at y 547).
DESK_CIRCLEstill reserves the bottom-left corner as if it did. NOT-A-FINDING: deliberate. The brief said to keep the computer constants unchanged, and the reservation is conservative in the safe direction; it costs only a corner the designs do not use. It is now written down as a conservatism rather than as a fact, in the constant's own comment. QUIETused one formula for both circles, and that formula was only right for a left-anchored one. Applied to the measured centred phone circle it would have blanked everything from x 0 to x 1054 below y 345 — about two-thirds of the cover's lower half, for no reason. NOT-A-FINDING in the register: fixed in this session (the size rule), and it never shipped, because the phone circle was wrong anyway.- The build is not byte-reproducible across Pillow versions. See §4. NOT-A-FINDING: not a defect, and it costs nothing once the build machine is named — which the README now does. It would become a finding only if the build moved into CI on a different image.
- The preview page claimed a phone shows 640 × 360 and drew its phone panel at that aspect. It now takes its shape, its circle and its caption from the measured constants and says MEASURED, with the source-pixel and phone-pixel diameters both spelled out (393 cover-pixels, 173 px on a 412 px screen) so neither can be read as the other. NOT-A-FINDING: fixed in this session, part of the same change.
- Nothing was uploaded, no Graph API call was made, no browser touched Facebook, and
FACEBOOK_APIwas never read.
9. Gates, commit, CI
Gates. python3 scripts/repo_gates.py --fast on DooPlex, at the pushed commit 796a9fdf: rc = 0,
„all felhom.eu gates OK”, all 18 gates OK — including register-shape, one-register, closed-register
and observations, which are the ones this change could have broken. It was run twice: once on the working
tree before committing, once after DooPlex pulled the pushed commit.
The gates were not run on the Windows workstation this session. The two failures they produce there
(instructions, from the deliberate E:\git\CLAUDE.md divergence, and script-tests, from fcntl, symlink
privilege and C:/E: mount paths) are platform artifacts documented in REPORT-facebook-page-setup.md §9;
nothing in this change touches either.
Build reproducibility, proven at the pushed commit. After DooPlex pulled 796a9fdf, a plain
python3 marketing/facebook/build.py exited 0 and left git status --porcelain -- marketing/ empty —
the committed PNGs are exactly what the committed script produces on the build machine. Both controls fired
in that run: the circle check convicted today's profile shape, and control_old_window convicted the
pre-R-919 layout.
Commit. 796a9fdf40c469670269b7ae16833156a9f6dd0e, pushed to main. No branch, explicit paths staged,
no Co-Authored-By line, --no-verify not used (this clone is unarmed, which is why the DooPlex run above
was done by hand).
CI — green, for this commit. gates.yml run #849, commit 796a9fdf40, status
tw-text-green octicon-check-circle-fill.
Two notes on how that was read, because the obvious ways are both wrong here:
- The Gitea API still refuses every credential in the store (HTTP 401; tried last session with
DOCKER_USERNAME+DOCKER_PASSWORD,DOCKER_USERNAME+PASSWORD,admin+PASSWORD), so thehead_sharecipe inCLAUDE.mdcannot be run. The web UI serves this repo's Actions list unauthenticated, so it was read from there withcurl. - The run's own page is useless to
curl:/actions/runs/849redirects to the internal id (/actions/runs/1580, R-417's offset again) and renders through JavaScript, so its HTML carries every status word as a template string —grepfinds „success”, „failure”, „running” and „cancelled” on a page whatever the outcome. The conclusion was therefore taken from the list page, and from the sameflex-itemrow container that holds the commit link:flex-item-leadingcarries the icon,flex-item-mainthe „gates.yml #849” label and the/commit/796a9fdf40link. Splitting the list onclass="flex-itemdoes not work — the child divs share that prefix, so the row gets chopped and the icon is attributed to a neighbour. Reading the icon off a neighbouring row is exactly how a red run gets reported green, so it is written down.
10. Teardown
Nothing on any host, the hub or Facebook. The work ran in the two git working trees only; the throwaway
red-proof and phone-simulation scripts live in this session's scratchpad and /tmp on DooPlex, and the
DooPlex copy was removed. No guest, no container, no service was started or stopped.