diff --git a/REPORT-facebook-first-post.md b/REPORT-facebook-first-post.md new file mode 100644 index 00000000..0df033fd --- /dev/null +++ b/REPORT-facebook-first-post.md @@ -0,0 +1,165 @@ +# Facebook — the Page's first post, drafted and scheduled + +2026-10-09 · Page `1360018983863273` · evidence `documentation/audits/facebook-first-post-2026-10-09/` + +Own file on purpose: the shared `REPORT.md` belongs to whoever else is in this clone. + +--- + +## In plain words + +The Page's first post is **written, approved by the operator, and scheduled for Monday 2026-10-12 at +19:00** Budapest time. It is **not public yet** — it sits in Meta Business Suite → Tervező (Planner), +where it can still be changed or deleted. The robot cannot publish anything immediately; that is +enforced in the code, not left to care. + +One thing the operator should know: the post repeats the website's "56 alkalmazás" figure, and I +nearly changed it. The apps page carries **57** cards while saying 56 — which looks like an off-by-one +until you read the category line, *„6 alkalmazás + 1 beépített"*. The 57th card is FileBrowser, built +into every box and deliberately not counted. **56 is correct.** + +**The operator's one remaining click:** after the post goes out on Monday evening, pin it — +„…" → „Kiemelés". + +--- + +## 1. Baselines and commits + +| | | +|---|---| +| baseline | task named `fe80548144`; `main` was already at `96f68f1b44` when pulled (other sessions) | +| drafts + `schedule-post` | **`3a77ed73aa`** | +| records (this report) | see §9 | + +No architecture document covers marketing, and none should — it is a business tool, not part of the +product. The decision home is `CONTEXT.md` (2026-10-08 Facebook entry). + +## 2. Scenario A — the dry check + +A throwaway scheduled post, created **with the link**, read back, listed, deleted, listed again: + +| step | result | +|---|---| +| create with `link=https://felhom.eu/` | **accepted** — the open question in §3 of the brief is answered: `link` is not refused on a scheduled post | +| read-back | `is_published=False`, `scheduled_publish_time` equal to what was sent, message **hex-equal** | +| which list call answered | **`GET /{page}/scheduled_posts`** — the documented edge; the `feed?is_published=false` fallback was not needed | +| present before delete | **True** | +| after delete | **absent: True** | + +The removal proof comes from the **list**, not from an error after `DELETE` — which is what R-914 asks +for, and the reason `list_scheduled()` returns `None` rather than a guess when both routes are refused. + +`documentation/audits/facebook-first-post-2026-10-09/probe/S*.json`. + +## 3. The post + +| | | +|---|---| +| version | **6.2 Közepes** (operator's choice at the STOP) | +| length | **638 Unicode characters** | +| language | Hungarian only (operator declined an English paragraph) | +| emoji / hashtags | **0 / 0** — see below | +| link | `https://felhom.eu/` | +| scheduled | **2026-10-12 19:00 Europe/Budapest** = epoch `1791824400` = 17:00 UTC | +| post id | `1360018983863273_122096547315511222` | +| permalink (after it publishes) | `https://www.facebook.com/122096546283511222/posts/122096547315511222` | + +Zero emoji and zero hashtags although the brief allowed two of each: the Felhom design system uses no +emoji (the website gate holds it at 0), and two hashtags would have served no real search. + +The operator chose Monday over today on the reasoning offered: it was Friday 15:08, and a Friday +evening is the weakest slot of the week for a first post — three days in Planner is also review time. + +## 4. Read-back + +| check | result | +|---|---| +| `is_published` | **False** | +| `scheduled_publish_time` sent vs read | **equal** (`1791824400`) | +| message hex-equal to `COPY.md` §6.2 | **True** | +| sha256, recomputed **outside** the probe | COPY.md `887514383eff997c` = posted `887514383eff997c` | +| epoch → wall clock, checked with the tz database | `2026-10-12 19:00 CEST (Monday)` | +| in `scheduled_posts` | **True** | +| Planner, a different channel from the API | the card sits on **H 12 at 19:00** with the link preview attached (`A1-planner-monday-19-00.jpg`) | + +## 5. Published yet? + +**No — the time has not come.** Monday 19:00 Budapest is in the future at the time of writing. The +next session (or this one, if still open then) reads it back: `is_published` must become `true` and +the permalink must resolve. **If it did not publish, report it — do not post again.** + +## 6. The operator's next click + +After it goes out on Monday evening: open the post on the Page, „**…**" → „**Kiemelés**" to pin it to +the top. Not done by API: the pin endpoint is unproven and one click is enough. + +## 7. `schedule-post` — what it is and what it refuses + +A third sub-command on `fb_probe.py`, reusing its token loader (R-453), redaction, Bearer call and +evidence writer. The guards, each with a test: + +- **It cannot publish immediately.** `published` is always `"false"` and **no argument can change it** + — the test enumerates every keyword `schedule_form()` accepts and asserts none of them flips it. + The operator's review in Planner is the safety net, so an immediate post has to be *unreachable*, + not merely not-the-default. +- **The body is read from `COPY.md` by section name**, never passed through a shell or an argv string + (brief §9.6). The caller names `6.2`; the Hungarian stays in the file. +- **`check_when()` refuses** a time under Meta's 10-minute floor or over its 6-month ceiling, *before* + the call, so a bad time is a readable local refusal rather than a Graph error. +- **`budapest_to_epoch()` uses the real tz database** and **refuses** if `Europe/Budapest` is missing + rather than falling back to a hardcoded `+01:00`/`+02:00`. Guessing the offset is how a post goes + out an hour wrong across a DST boundary. +- **`list_scheduled()` records which route answered** and returns `None` when both are refused, so a + caller says "unproven" instead of claiming a removal it never saw. + +**Tests: 30.** On DooPlex **all 30 pass, none skipped**. On Windows 2 skip — this interpreter ships no +tz database; the command runs on the Linux host, which has the system zoneinfo. + +**Red-proof of the guard that matters**, as the brief requires. `schedule_form` was given a +`published=...` argument, `ScheduleForm` was run, and the test failed: + +``` +AssertionError: 'true' != 'false' : published changed published +``` + +Guard restored, all 30 green again. + +## 8. Secret scan + +``` +plant EAAfakeprobe → grep -rl EAA --exclude=README.md = 1 +delete → grep -rl EAA --exclude=README.md = 0 +access_token in evidence = 0 +run.log in evidence = 0 +``` + +The token comes only from the credentials file on DooPlex, is never printed and never appears in a +logged URL. No `run.log` is committed — the probe's stdout carries the key's length and prefix. + +## 9. Register + +- **R-917 → VERIFY.** The text is scheduled; close when the operator confirms it published and is + pinned. Post id and time recorded in the row. +- **R-914 → noted**, not closed: `schedule-post` exists for **text + link only**. The full skill still + needs the photo path (the spike could not prove a scheduled photo stays hidden), the pin, comment + moderation, and the insight read-back after a post. +- Nothing closed, nothing else opened. + +## 10. Observations + +- **NOT-A-FINDING: the apps page says 56 while carrying 57 cards.** Deliberate — the category line + reads „6 alkalmazás + 1 beépített" and the extra card is FileBrowser, built into every box. + `index.html` says „56 telepíthető alkalmazás" too. Checked before copying the number into a public + post; a "fix" here would have made a live page wrong. +- **NOT-A-FINDING: the link preview on a scheduled post.** The brief's edge case asked what to do if + the preview card is empty. It is not: Planner shows the card with the og-image, and the Sharing + Debugger was re-scraped earlier today for this exact URL. +- **FILED earlier today, still open: R-887** — the lost-job CI flake bit this session's earlier + commits once (`#875`, "Set up job" 11m48s then every step 0s) and passed on re-run. + +## 11. Teardown + +The dry-check post was deleted and its removal proved from the scheduled-posts list. The real post is +**deliberately left in place** — that is the deliverable. Nothing on any box, the hub, or any other +Page. The DooPlex worktree used to run the command was removed; nothing was written into the shared +clone. The browser tab was closed. diff --git a/documentation/audits/facebook-first-post-2026-10-09/A1-planner-monday-19-00.jpg b/documentation/audits/facebook-first-post-2026-10-09/A1-planner-monday-19-00.jpg new file mode 100644 index 00000000..f65f667b Binary files /dev/null and b/documentation/audits/facebook-first-post-2026-10-09/A1-planner-monday-19-00.jpg differ diff --git a/documentation/audits/facebook-first-post-2026-10-09/probe/A1-debug-token.json b/documentation/audits/facebook-first-post-2026-10-09/probe/A1-debug-token.json new file mode 100644 index 00000000..a32f887f --- /dev/null +++ b/documentation/audits/facebook-first-post-2026-10-09/probe/A1-debug-token.json @@ -0,0 +1,63 @@ +{ + "api_version": "v26.0", + "at_utc": "2026-10-09T13:10:21Z", + "error": null, + "files": {}, + "form": {}, + "headers": { + "facebook-api-version": "v26.0", + "x-business-use-case-usage": "{\"4713349378884589\":[{\"type\":\"business_integration_system_user_platform_endpoints\",\"call_count\":1,\"total_cputime\":1,\"total_time\":1,\"estimated_time_to_regain_access\":0}]}", + "x-fb-rev": "1049833609", + "x-fb-trace-id": "AeidMitXgcP" + }, + "http_status": 200, + "method": "GET", + "ok": true, + "path": "debug_token", + "query_keys": [ + "input_token" + ], + "response": { + "data": { + "app_id": "2273465403490709", + "application": "felhom.eu", + "data_access_expires_at": 0, + "expires_at": 0, + "granular_scopes": [ + { + "scope": "pages_show_list" + }, + { + "scope": "business_management" + }, + { + "scope": "pages_read_engagement" + }, + { + "scope": "pages_read_user_content" + }, + { + "scope": "pages_manage_posts" + }, + { + "scope": "pages_manage_engagement" + } + ], + "is_valid": true, + "issued_at": 1791474515, + "scopes": [ + "read_insights", + "pages_show_list", + "business_management", + "pages_read_engagement", + "pages_read_user_content", + "pages_manage_posts", + "pages_manage_engagement", + "public_profile" + ], + "type": "SYSTEM_USER", + "user_id": "122094150717513084" + } + }, + "step": "A1-debug-token" +} diff --git a/documentation/audits/facebook-first-post-2026-10-09/probe/B1-me.json b/documentation/audits/facebook-first-post-2026-10-09/probe/B1-me.json new file mode 100644 index 00000000..c4dce5e6 --- /dev/null +++ b/documentation/audits/facebook-first-post-2026-10-09/probe/B1-me.json @@ -0,0 +1,25 @@ +{ + "api_version": "v26.0", + "at_utc": "2026-10-09T13:10:22Z", + "error": null, + "files": {}, + "form": {}, + "headers": { + "facebook-api-version": "v26.0", + "x-app-usage": "{\"call_count\":0,\"total_cputime\":0,\"total_time\":0}", + "x-fb-rev": "1049833609", + "x-fb-trace-id": "Ew9S4iGRy8e" + }, + "http_status": 200, + "method": "GET", + "ok": true, + "path": "me", + "query_keys": [ + "fields" + ], + "response": { + "id": "122094150717513084", + "name": "felhom-cc" + }, + "step": "B1-me" +} diff --git a/documentation/audits/facebook-first-post-2026-10-09/probe/B2-me-accounts.json b/documentation/audits/facebook-first-post-2026-10-09/probe/B2-me-accounts.json new file mode 100644 index 00000000..63410073 --- /dev/null +++ b/documentation/audits/facebook-first-post-2026-10-09/probe/B2-me-accounts.json @@ -0,0 +1,44 @@ +{ + "api_version": "v26.0", + "at_utc": "2026-10-09T13:10:23Z", + "error": null, + "files": {}, + "form": {}, + "headers": { + "facebook-api-version": "v26.0", + "x-app-usage": "{\"call_count\":1,\"total_cputime\":0,\"total_time\":0}", + "x-fb-rev": "1049833609", + "x-fb-trace-id": "GYcBRwfgp+7" + }, + "http_status": 200, + "method": "GET", + "ok": true, + "path": "me/accounts", + "query_keys": [ + "fields" + ], + "response": { + "data": [ + { + "id": "1360018983863273", + "name": "Felhom.eu", + "tasks": [ + "CREATE_CONTENT", + "MODERATE", + "MESSAGING", + "ADVERTISE", + "ANALYZE", + "MANAGE_LEADS", + "VIEW_MONETIZATION_INSIGHTS" + ] + } + ], + "paging": { + "cursors": { + "after": "QVFIVG1yVkVJTjcyYWRLNlN4ZAXNSY2FwQnVIa01JY2hjazlNTG94Y0oydkh3aC1hR1pvYW5hcm40elhQTTBndVQwTTV2U1VJeUtLOHVESWlXNlZA1WC1QbFlR", + "before": "QVFIVG1yVkVJTjcyYWRLNlN4ZAXNSY2FwQnVIa01JY2hjazlNTG94Y0oydkh3aC1hR1pvYW5hcm40elhQTTBndVQwTTV2U1VJeUtLOHVESWlXNlZA1WC1QbFlR" + } + } + }, + "step": "B2-me-accounts" +} diff --git a/documentation/audits/facebook-first-post-2026-10-09/probe/B3-debug-page-token.json b/documentation/audits/facebook-first-post-2026-10-09/probe/B3-debug-page-token.json new file mode 100644 index 00000000..d6c072ec --- /dev/null +++ b/documentation/audits/facebook-first-post-2026-10-09/probe/B3-debug-page-token.json @@ -0,0 +1,64 @@ +{ + "api_version": "v26.0", + "at_utc": "2026-10-09T13:10:23Z", + "error": null, + "files": {}, + "form": {}, + "headers": { + "facebook-api-version": "v26.0", + "x-business-use-case-usage": "{\"4713349378884589\":[{\"type\":\"business_integration_system_user_platform_endpoints\",\"call_count\":1,\"total_cputime\":1,\"total_time\":1,\"estimated_time_to_regain_access\":0}]}", + "x-fb-rev": "1049833609", + "x-fb-trace-id": "HFCjoKcq073" + }, + "http_status": 200, + "method": "GET", + "ok": true, + "path": "debug_token", + "query_keys": [ + "input_token" + ], + "response": { + "data": { + "app_id": "2273465403490709", + "application": "felhom.eu", + "data_access_expires_at": 0, + "expires_at": 0, + "granular_scopes": [ + { + "scope": "pages_show_list" + }, + { + "scope": "business_management" + }, + { + "scope": "pages_read_engagement" + }, + { + "scope": "pages_read_user_content" + }, + { + "scope": "pages_manage_posts" + }, + { + "scope": "pages_manage_engagement" + } + ], + "is_valid": true, + "issued_at": 1791551422, + "profile_id": "1360018983863273", + "scopes": [ + "read_insights", + "pages_show_list", + "business_management", + "pages_read_engagement", + "pages_read_user_content", + "pages_manage_posts", + "pages_manage_engagement", + "public_profile" + ], + "type": "PAGE", + "user_id": "122094150717513084" + } + }, + "step": "B3-debug-page-token" +} diff --git a/documentation/audits/facebook-first-post-2026-10-09/probe/F1-headers.json b/documentation/audits/facebook-first-post-2026-10-09/probe/F1-headers.json new file mode 100644 index 00000000..ff03a733 --- /dev/null +++ b/documentation/audits/facebook-first-post-2026-10-09/probe/F1-headers.json @@ -0,0 +1,7 @@ +{ + "facebook-api-version": "v26.0", + "x-app-usage": "{\"call_count\":0,\"total_cputime\":0,\"total_time\":0}", + "x-business-use-case-usage": "{\"4713349378884589\":[{\"type\":\"business_integration_system_user_platform_endpoints\",\"call_count\":1,\"total_cputime\":1,\"total_time\":1,\"estimated_time_to_regain_access\":0}]}", + "x-fb-rev": "1049833609", + "x-fb-trace-id": "AeidMitXgcP" +} diff --git a/documentation/audits/facebook-first-post-2026-10-09/probe/P1-create.json b/documentation/audits/facebook-first-post-2026-10-09/probe/P1-create.json new file mode 100644 index 00000000..7828c967 --- /dev/null +++ b/documentation/audits/facebook-first-post-2026-10-09/probe/P1-create.json @@ -0,0 +1,27 @@ +{ + "api_version": "v26.0", + "at_utc": "2026-10-09T13:10:24Z", + "error": null, + "files": {}, + "form": { + "link": "https://felhom.eu/", + "message": "A fotóid, a papírjaid és a médiatárad a saját gépeden maradnak – nem egy nagy techcég szerverén.\n\nA Felhom egy otthoni szerver, amit mi telepítünk és üzemeltetünk. Te csak használod:\n– a telefonod fotói maguktól feltöltődnek rá,\n– a papírjaid kereshetővé válnak,\n– a mappái ott vannak a Windows Intézőben és a Mac Finderben,\n– 56 alkalmazás telepíthető pár kattintással,\n– ha elmegy a net, otthon is eléred a fájljaidat.\n\nMinden éjjel mentés készül három helyre. A távoli mentés kulcsát egyedül te ismered.\n\nMost néhány magyar háztartással zárt tesztet indítunk. Nem kötelez semmire, először csak beszélgetünk: https://felhom.eu/kapcsolat", + "published": "false", + "scheduled_publish_time": "1791824400" + }, + "headers": { + "facebook-api-version": "v26.0", + "x-business-use-case-usage": "{\"1360018983863273\":[{\"type\":\"pages\",\"call_count\":1,\"total_cputime\":1,\"total_time\":1,\"estimated_time_to_regain_access\":0}]}", + "x-fb-rev": "1049833609", + "x-fb-trace-id": "CRQ6HdbJd1s" + }, + "http_status": 200, + "method": "POST", + "ok": true, + "path": "1360018983863273/feed", + "query_keys": [], + "response": { + "id": "1360018983863273_122096547315511222" + }, + "step": "P1-create" +} diff --git a/documentation/audits/facebook-first-post-2026-10-09/probe/P2-readback.json b/documentation/audits/facebook-first-post-2026-10-09/probe/P2-readback.json new file mode 100644 index 00000000..e289d135 --- /dev/null +++ b/documentation/audits/facebook-first-post-2026-10-09/probe/P2-readback.json @@ -0,0 +1,29 @@ +{ + "api_version": "v26.0", + "at_utc": "2026-10-09T13:10:25Z", + "error": null, + "files": {}, + "form": {}, + "headers": { + "facebook-api-version": "v26.0", + "x-business-use-case-usage": "{\"1360018983863273\":[{\"type\":\"pages\",\"call_count\":1,\"total_cputime\":1,\"total_time\":1,\"estimated_time_to_regain_access\":0}]}", + "x-fb-rev": "1049833609", + "x-fb-trace-id": "B/1RyWJY8YW" + }, + "http_status": 200, + "method": "GET", + "ok": true, + "path": "1360018983863273_122096547315511222", + "query_keys": [ + "fields" + ], + "response": { + "created_time": "2026-10-12T17:00:00+0000", + "id": "1360018983863273_122096547315511222", + "is_published": false, + "message": "A fotóid, a papírjaid és a médiatárad a saját gépeden maradnak – nem egy nagy techcég szerverén.\n\nA Felhom egy otthoni szerver, amit mi telepítünk és üzemeltetünk. Te csak használod:\n– a telefonod fotói maguktól feltöltődnek rá,\n– a papírjaid kereshetővé válnak,\n– a mappái ott vannak a Windows Intézőben és a Mac Finderben,\n– 56 alkalmazás telepíthető pár kattintással,\n– ha elmegy a net, otthon is eléred a fájljaidat.\n\nMinden éjjel mentés készül három helyre. A távoli mentés kulcsát egyedül te ismered.\n\nMost néhány magyar háztartással zárt tesztet indítunk. Nem kötelez semmire, először csak beszélgetünk: https://felhom.eu/kapcsolat", + "permalink_url": "https://www.facebook.com/122096546283511222/posts/122096547315511222", + "scheduled_publish_time": 1791824400 + }, + "step": "P2-readback" +} diff --git a/documentation/audits/facebook-first-post-2026-10-09/probe/P3-list.json b/documentation/audits/facebook-first-post-2026-10-09/probe/P3-list.json new file mode 100644 index 00000000..019cbcf6 --- /dev/null +++ b/documentation/audits/facebook-first-post-2026-10-09/probe/P3-list.json @@ -0,0 +1,36 @@ +{ + "api_version": "v26.0", + "at_utc": "2026-10-09T13:10:25Z", + "error": null, + "files": {}, + "form": {}, + "headers": { + "facebook-api-version": "v26.0", + "x-business-use-case-usage": "{\"1360018983863273\":[{\"type\":\"pages\",\"call_count\":1,\"total_cputime\":1,\"total_time\":1,\"estimated_time_to_regain_access\":0}]}", + "x-fb-rev": "1049833609", + "x-fb-trace-id": "DSfutM37vbx" + }, + "http_status": 200, + "method": "GET", + "ok": true, + "path": "1360018983863273/scheduled_posts", + "query_keys": [ + "fields", + "limit" + ], + "response": { + "data": [ + { + "id": "1360018983863273_122096547315511222", + "is_published": false + } + ], + "paging": { + "cursors": { + "after": "QVFIVHNPY0pYQUt2WTZAlVFRLOWwxbWt5a2dud0JHTmlPa1k5OEI0LVR3VGVhX2VfLWFmUTZAOdGtLRTllMi1KUUczNFY5dkVIRlpCd19fa0pqc25CbDhmQVRn", + "before": "QVFIVHNPY0pYQUt2WTZAlVFRLOWwxbWt5a2dud0JHTmlPa1k5OEI0LVR3VGVhX2VfLWFmUTZAOdGtLRTllMi1KUUczNFY5dkVIRlpCd19fa0pqc25CbDhmQVRn" + } + } + }, + "step": "P3-list" +} diff --git a/documentation/audits/facebook-first-post-2026-10-09/probe/P9-verdict.json b/documentation/audits/facebook-first-post-2026-10-09/probe/P9-verdict.json new file mode 100644 index 00000000..3e10d56a --- /dev/null +++ b/documentation/audits/facebook-first-post-2026-10-09/probe/P9-verdict.json @@ -0,0 +1,14 @@ +{ + "hex_equal": true, + "in_scheduled_list": true, + "is_published": false, + "list_route": "scheduled_posts", + "permalink_url": "https://www.facebook.com/122096546283511222/posts/122096547315511222", + "post_id": "1360018983863273_122096547315511222", + "read_hex": "4120666f74c3b369642c206120706170c3ad726a61696420c3a9732061206dc3a964696174c3a172616420612073616ac3a1742067c3a9706564656e206d617261646e616b20e28093206e656d20656779206e616779207465636863c3a96720737a6572766572c3a96e2e0a0a412046656c686f6d20656779206f7474686f6e6920737a65727665722c20616d6974206d692074656c6570c3ad74c3bc6e6b20c3a97320c3bc7a656d656c746574c3bc6e6b2e205465206373616b206861737a6ec3a16c6f643a0ae2809320612074656c65666f6e6f6420666f74c3b369206d6167756b74c3b36c2066656c74c3b66c74c591646e656b2072c3a12c0ae28093206120706170c3ad726a616964206b65726573686574c59176c3a92076c3a16c6e616b2c0ae280932061206d617070c3a169206f74742076616e6e616b20612057696e646f777320496e74c3a97ac59162656e20c3a9732061204d61632046696e64657262656e2c0ae2809320353620616c6b616c6d617ac3a1732074656c6570c3ad74686574c5912070c3a172206b617474696e74c3a17373616c2c0ae2809320686120656c6d6567792061206e65742c206f7474686f6e20697320656cc3a972656420612066c3a16a6c6a61696461742e0a0a4d696e64656e20c3a96a6a656c206d656e74c3a973206bc3a9737ac3bc6c2068c3a1726f6d2068656c7972652e20412074c3a1766f6c69206d656e74c3a973206b756c6373c3a174206567796564c3bc6c2074652069736d657265642e0a0a4d6f7374206ec3a968c3a16e79206d61677961722068c3a17a74617274c3a17373616c207ac3a17274207465737a74657420696e64c3ad74756e6b2e204e656d206bc3b674656c657a2073656d6d6972652c20656cc591737ac3b672206373616b206265737ac3a96c676574c3bc6e6b3a2068747470733a2f2f66656c686f6d2e65752f6b617063736f6c6174", + "readback_ok": true, + "scheduled_publish_time_read": 1791824400, + "scheduled_publish_time_sent": 1791824400, + "sent_hex": "4120666f74c3b369642c206120706170c3ad726a61696420c3a9732061206dc3a964696174c3a172616420612073616ac3a1742067c3a9706564656e206d617261646e616b20e28093206e656d20656779206e616779207465636863c3a96720737a6572766572c3a96e2e0a0a412046656c686f6d20656779206f7474686f6e6920737a65727665722c20616d6974206d692074656c6570c3ad74c3bc6e6b20c3a97320c3bc7a656d656c746574c3bc6e6b2e205465206373616b206861737a6ec3a16c6f643a0ae2809320612074656c65666f6e6f6420666f74c3b369206d6167756b74c3b36c2066656c74c3b66c74c591646e656b2072c3a12c0ae28093206120706170c3ad726a616964206b65726573686574c59176c3a92076c3a16c6e616b2c0ae280932061206d617070c3a169206f74742076616e6e616b20612057696e646f777320496e74c3a97ac59162656e20c3a9732061204d61632046696e64657262656e2c0ae2809320353620616c6b616c6d617ac3a1732074656c6570c3ad74686574c5912070c3a172206b617474696e74c3a17373616c2c0ae2809320686120656c6d6567792061206e65742c206f7474686f6e20697320656cc3a972656420612066c3a16a6c6a61696461742e0a0a4d696e64656e20c3a96a6a656c206d656e74c3a973206bc3a9737ac3bc6c2068c3a1726f6d2068656c7972652e20412074c3a1766f6c69206d656e74c3a973206b756c6373c3a174206567796564c3bc6c2074652069736d657265642e0a0a4d6f7374206ec3a968c3a16e79206d61677961722068c3a17a74617274c3a17373616c207ac3a17274207465737a74657420696e64c3ad74756e6b2e204e656d206bc3b674656c657a2073656d6d6972652c20656cc591737ac3b672206373616b206265737ac3a96c676574c3bc6e6b3a2068747470733a2f2f66656c686f6d2e65752f6b617063736f6c6174", + "text_field": "message" +} diff --git a/documentation/audits/facebook-first-post-2026-10-09/probe/S1-dry-create.json b/documentation/audits/facebook-first-post-2026-10-09/probe/S1-dry-create.json new file mode 100644 index 00000000..7a0a7ca8 --- /dev/null +++ b/documentation/audits/facebook-first-post-2026-10-09/probe/S1-dry-create.json @@ -0,0 +1,27 @@ +{ + "api_version": "v26.0", + "at_utc": "2026-10-09T13:07:24Z", + "error": null, + "files": {}, + "form": { + "link": "https://felhom.eu/", + "message": "Felhom teszt – árvíztűrő tükörfúrógép. Ez a bejegyzés törlődik.", + "published": "false", + "scheduled_publish_time": "1792156040" + }, + "headers": { + "facebook-api-version": "v26.0", + "x-business-use-case-usage": "{\"1360018983863273\":[{\"type\":\"pages\",\"call_count\":1,\"total_cputime\":1,\"total_time\":1,\"estimated_time_to_regain_access\":0}]}", + "x-fb-rev": "1049833609", + "x-fb-trace-id": "HJrlEvLMmxk" + }, + "http_status": 200, + "method": "POST", + "ok": true, + "path": "1360018983863273/feed", + "query_keys": [], + "response": { + "id": "1360018983863273_122096546265511222" + }, + "step": "S1-dry-create" +} diff --git a/documentation/audits/facebook-first-post-2026-10-09/probe/S2-dry-readback.json b/documentation/audits/facebook-first-post-2026-10-09/probe/S2-dry-readback.json new file mode 100644 index 00000000..82c228ed --- /dev/null +++ b/documentation/audits/facebook-first-post-2026-10-09/probe/S2-dry-readback.json @@ -0,0 +1,28 @@ +{ + "api_version": "v26.0", + "at_utc": "2026-10-09T13:07:25Z", + "error": null, + "files": {}, + "form": {}, + "headers": { + "facebook-api-version": "v26.0", + "x-business-use-case-usage": "{\"1360018983863273\":[{\"type\":\"pages\",\"call_count\":1,\"total_cputime\":1,\"total_time\":1,\"estimated_time_to_regain_access\":0}]}", + "x-fb-rev": "1049833609", + "x-fb-trace-id": "BYC1cnzGUKk" + }, + "http_status": 200, + "method": "GET", + "ok": true, + "path": "1360018983863273_122096546265511222", + "query_keys": [ + "fields" + ], + "response": { + "id": "1360018983863273_122096546265511222", + "is_published": false, + "message": "Felhom teszt – árvíztűrő tükörfúrógép. Ez a bejegyzés törlődik.", + "permalink_url": "https://www.facebook.com/122096546283511222/posts/122096546265511222", + "scheduled_publish_time": 1792156040 + }, + "step": "S2-dry-readback" +} diff --git a/documentation/audits/facebook-first-post-2026-10-09/probe/S3-list-before.json b/documentation/audits/facebook-first-post-2026-10-09/probe/S3-list-before.json new file mode 100644 index 00000000..58bcf4d0 --- /dev/null +++ b/documentation/audits/facebook-first-post-2026-10-09/probe/S3-list-before.json @@ -0,0 +1,36 @@ +{ + "api_version": "v26.0", + "at_utc": "2026-10-09T13:07:26Z", + "error": null, + "files": {}, + "form": {}, + "headers": { + "facebook-api-version": "v26.0", + "x-business-use-case-usage": "{\"1360018983863273\":[{\"type\":\"pages\",\"call_count\":1,\"total_cputime\":1,\"total_time\":1,\"estimated_time_to_regain_access\":0}]}", + "x-fb-rev": "1049833609", + "x-fb-trace-id": "Bg7+tE0Q8vy" + }, + "http_status": 200, + "method": "GET", + "ok": true, + "path": "1360018983863273/scheduled_posts", + "query_keys": [ + "fields", + "limit" + ], + "response": { + "data": [ + { + "id": "1360018983863273_122096546265511222", + "is_published": false + } + ], + "paging": { + "cursors": { + "after": "QVFIVGc0bW9aLWhGdkY2VGxNcXNTejl2VTQ0aUhMSzdYNEpudW9hQm1xRVJFeDhieFJEQXBRVDgyazZA0Vmc3X1BDTXRYT1pfeklUM0g5OXNxRUplTm5GVm1n", + "before": "QVFIVGc0bW9aLWhGdkY2VGxNcXNTejl2VTQ0aUhMSzdYNEpudW9hQm1xRVJFeDhieFJEQXBRVDgyazZA0Vmc3X1BDTXRYT1pfeklUM0g5OXNxRUplTm5GVm1n" + } + } + }, + "step": "S3-list-before" +} diff --git a/documentation/audits/facebook-first-post-2026-10-09/probe/S4-dry-delete.json b/documentation/audits/facebook-first-post-2026-10-09/probe/S4-dry-delete.json new file mode 100644 index 00000000..b37ad8dc --- /dev/null +++ b/documentation/audits/facebook-first-post-2026-10-09/probe/S4-dry-delete.json @@ -0,0 +1,22 @@ +{ + "api_version": "v26.0", + "at_utc": "2026-10-09T13:07:30Z", + "error": null, + "files": {}, + "form": {}, + "headers": { + "facebook-api-version": "v26.0", + "x-business-use-case-usage": "{\"1360018983863273\":[{\"type\":\"pages\",\"call_count\":1,\"total_cputime\":1,\"total_time\":1,\"estimated_time_to_regain_access\":0}]}", + "x-fb-rev": "1049833609", + "x-fb-trace-id": "EjgMscjGNBT" + }, + "http_status": 200, + "method": "DELETE", + "ok": true, + "path": "1360018983863273_122096546265511222", + "query_keys": [], + "response": { + "success": true + }, + "step": "S4-dry-delete" +} diff --git a/documentation/audits/facebook-first-post-2026-10-09/probe/S5-list-after.json b/documentation/audits/facebook-first-post-2026-10-09/probe/S5-list-after.json new file mode 100644 index 00000000..52daa2e2 --- /dev/null +++ b/documentation/audits/facebook-first-post-2026-10-09/probe/S5-list-after.json @@ -0,0 +1,25 @@ +{ + "api_version": "v26.0", + "at_utc": "2026-10-09T13:07:30Z", + "error": null, + "files": {}, + "form": {}, + "headers": { + "facebook-api-version": "v26.0", + "x-business-use-case-usage": "{\"1360018983863273\":[{\"type\":\"pages\",\"call_count\":1,\"total_cputime\":1,\"total_time\":1,\"estimated_time_to_regain_access\":0}]}", + "x-fb-rev": "1049833609", + "x-fb-trace-id": "CmRC/3o7Gjb" + }, + "http_status": 200, + "method": "GET", + "ok": true, + "path": "1360018983863273/scheduled_posts", + "query_keys": [ + "fields", + "limit" + ], + "response": { + "data": [] + }, + "step": "S5-list-after" +} diff --git a/documentation/audits/facebook-first-post-2026-10-09/probe/S9-dry-verdict.json b/documentation/audits/facebook-first-post-2026-10-09/probe/S9-dry-verdict.json new file mode 100644 index 00000000..9e5ca406 --- /dev/null +++ b/documentation/audits/facebook-first-post-2026-10-09/probe/S9-dry-verdict.json @@ -0,0 +1,15 @@ +{ + "absent_after_delete": true, + "delete_ok": true, + "hex_equal": true, + "is_published": false, + "list_route": "scheduled_posts", + "post_id": "1360018983863273_122096546265511222", + "present_before_delete": true, + "read_hex": "46656c686f6d207465737a7420e2809320c3a17276c3ad7a74c5b172c5912074c3bc6bc3b67266c3ba72c3b367c3a9702e20457a20612062656a6567797ac3a9732074c3b6726cc59164696b2e", + "readback_ok": true, + "scheduled_publish_time_read": 1792156040, + "scheduled_publish_time_sent": 1792156040, + "sent_hex": "46656c686f6d207465737a7420e2809320c3a17276c3ad7a74c5b172c5912074c3bc6bc3b67266c3ba72c3b367c3a9702e20457a20612062656a6567797ac3a9732074c3b6726cc59164696b2e", + "text_field": "message" +} diff --git a/documentation/backlog/OPEN-ITEMS.md b/documentation/backlog/OPEN-ITEMS.md index 74dd5d97..d6dcd123 100644 --- a/documentation/backlog/OPEN-ITEMS.md +++ b/documentation/backlog/OPEN-ITEMS.md @@ -270,9 +270,9 @@ stopping line that lies. | **R-813** | Business & legal | P2 | **[P2] The website collects personal data but publishes no privacy notice, no terms and no imprint.** CHECKED 2026-10-03 (read-only): `website/` holds nine Hungarian pages and one English page; none is an ÁSZF, an adatkezelési tájékoztató or an impresszum, and no page links to one (ASCII-fragment search `aszf`, `adatkezel`, `impresszum`, `impressum`, `privacy` over `website/`; positive control: the same search finds `adatkezel` in the contact form). The contact form makes the visitor tick a data-processing consent (`website/kapcsolat.html:123-128`) whose text names no controller, no retention and no rights, and links nowhere. The papers around it — contract, data-processing agreement, billing — are the intention **R-809** in `ROADMAP.md`. **2026-10-08 (website refresh): the English twins are PUBLIC since today** (`felhom.eu/en/…`, operator choice B) — **the legal pages are needed in English too**, or an English line saying the legal texts are Hungarian, linked from every English page. The English contact form translates the consent text as it is. The refresh CUT the FAQ's „önálló modell" (no row backs it, brief Part A) and LEFT the GDPR answer („nem harmadik félnél") for R-900; the English FAQ carries the same answer. | **NARROWED 2026-10-09 — the closed-test set is PUBLISHED.** Live: (`website/adatkezeles.html`) and (`website/feltetelek.html`), Hungarian only, version „Zárt teszt — 1.0 verzió, hatályos: 2026. október 9.” Text of record: `documentation/legal/{adatkezelesi-tajekoztato,feltetelek}-1.0.md`, **derived from the published HTML** so it cannot drift from the page. All 18 pages carry the operator, `info@felhom.eu` and both links in the footer (counted: 18 found = 18 with both links = 18 structurally valid); the English footers say the legal texts are Hungarian, and `site_gates.py` `NO_TWIN` holds the two pages deliberately — an unreviewed English legal text would be worse than an honest pointer. The contact form's consent text was replaced in both languages: the old one claimed „az adatokat harmadik félnek nem adjuk ki” while Resend, Cloudflare and Google carry the message. **Operator rulings 2026-10-09:** no company yet, so the operator is named as a PRIVATE PERSON (Nagyfenyvesi Viktor) with **no postal address and no phone**; publish before the lawyer, because the site was collecting data with no notice at all; and keep three retentions honestly open-ended (website statistics, web server logs, contact messages) rather than promise a date nothing enforces. Four load-bearing claims were MEASURED, not copied from a vendor or a README: zero cookies and no local storage (browser check with a positive control, after the tracker fired; no `Set-Cookie` on any response); the Umami beacon's exact payload (site id, screen, language, title, url, referrer — no visitor identifier); Cloudflare is DNS only (the public A record 37.191.56.193 is not a Cloudflare address); `fsn1` = Falkenstein, Germany. Also measured: `felhom-ep0-copy-gc.timer` is installed and ran successfully (2026-10-09 08:00, exit 0), so the „deleted within 30 days” line is now true where on 2026-10-08 it was written but not switched on. **WHAT REMAINS, which is why this is narrowed and not closed:** the full ÁSZF and the impresszum are still unpublished (they need company data that does not exist); there is no English translation of either legal text; and **no lawyer has reviewed any of it** (R-802). Owner stays operator. | — | **(a) DONE 2026-10-09** — the two URLs are in the Meta app's Basic settings and the app is **Live** (R-915 closed). **(b)** when the company exists, commission the lawyer's review (R-802) and the full ÁSZF + impresszum. If nothing is done: the closed-test set stands as published and unreviewed | operator | | **R-89** | Business & legal | P4 | Retention as a per-customer **commercial** policy on the hub | READY (increment 2) | — | Policy object + reconciler → ep0 prune job; keep box tokens write-only | CC | | **R-794** | Business & legal | P4 | **[P3-LOW] redis 7.4 (RSALv2 / SSPL, not OSI) runs as a private cache in seven apps: dawarich, docmost, immich, nextcloud, outline, paperless-ngx, romm.** READ 2026-10-02 (`audits/licences-2026-10-02/TABLE.md`). Read as permitted (a private cache only its app uses is not Redis offered as a service — inferred). Valkey (BSD-3) or redis 8 (AGPL option) removes the question. **Needs:** a ladder step per app to valkey or redis 8, through the harness — no hurry. | **READY — rank P3-LOW; owner: CC** **Re-ranked 2026-10-03: P3→P4: the row itself says no hurry; usage read as permitted.** | — | — | CC | -| **R-914** | Business & legal | P4 | **Write the Felhom Facebook Page skill from the spike's findings.** Spike 2026-10-08 (`audits/SPIKE-facebook-page-api-2026-10-08.md`): key valid, never expires; the Page (`1360018983863273`) is reached with CREATE_CONTENT/MODERATE/ANALYZE; a scheduled text post and a scheduled photo were created, read back byte-equal and deleted (removal proven). Probe `scripts/facebook/fb_probe.py`. Gap to close in the skill: a scheduled photo's publish state was not read (no `post_id` returned). **2026-10-08 (Page pictures task) — two more for the skill:** (1) **the removal proof was not a proof**: the text post's after-DELETE answer was (#10) „does not exist, cannot be loaded due to missing permission…" — that message also means a permission gap. The skill proves removal by listing the Page's scheduled posts before and after the delete (present, then absent); the operator's Planner view on 2026-10-08 showed nothing on 15 October (a different channel, by eye). (2) **pictures by API** (READ, developers.facebook.com/docs/graph-api/reference/page/picture and …/reference/page/): `POST /{page}/picture` needs the `MANAGE` task, which the robot does not have (measured task list); the cover is `POST /{page}` `cover=`, „only by the Page Admin or Page Editor with `EDIT_PROFILE`" + `business_management`. Neither names `pages_manage_metadata`. Today the pictures are uploaded by hand (`marketing/facebook/README.md`). | **READY — owner: CC** | — | Write the skill (drafts scheduled for operator review by default); read a scheduled photo back through the Page's scheduled-post listing | CC | +| **R-914** | Business & legal | P4 | **Write the Felhom Facebook Page skill from the spike's findings.** Spike 2026-10-08 (`audits/SPIKE-facebook-page-api-2026-10-08.md`): key valid, never expires; the Page (`1360018983863273`) is reached with CREATE_CONTENT/MODERATE/ANALYZE; a scheduled text post and a scheduled photo were created, read back byte-equal and deleted (removal proven). Probe `scripts/facebook/fb_probe.py`. Gap to close in the skill: a scheduled photo's publish state was not read (no `post_id` returned). **2026-10-08 (Page pictures task) — two more for the skill:** (1) **the removal proof was not a proof**: the text post's after-DELETE answer was (#10) „does not exist, cannot be loaded due to missing permission…" — that message also means a permission gap. The skill proves removal by listing the Page's scheduled posts before and after the delete (present, then absent); the operator's Planner view on 2026-10-08 showed nothing on 15 October (a different channel, by eye). (2) **pictures by API** (READ, developers.facebook.com/docs/graph-api/reference/page/picture and …/reference/page/): `POST /{page}/picture` needs the `MANAGE` task, which the robot does not have (measured task list); the cover is `POST /{page}` `cover=`, „only by the Page Admin or Page Editor with `EDIT_PROFILE`" + `business_management`. Neither names `pages_manage_metadata`. Today the pictures are uploaded by hand (`marketing/facebook/README.md`). **-- 2026-10-09: the first slice exists.** `scripts/facebook/fb_probe.py` gained a **`schedule-post`** sub-command (text + link, scheduled only) reusing the probe's token loader, redaction, Bearer call and evidence writer. Guards, each with a test (30 tests; all 30 pass on DooPlex, 2 skip on Windows for want of a tz database): **it cannot publish immediately** — `published` is always `false` and the test enumerates every keyword `schedule_form()` takes to prove none flips it (RED-PROOFED: the guard was removed and the test seen failing, `'true' != 'false'`); the body is **read from `COPY.md` by section**, never through a shell; `check_when()` refuses a time outside Meta's 10-minute..6-month window before the call; `budapest_to_epoch()` uses the real tz database and **refuses** rather than guessing a DST offset; `list_scheduled()` records which route answered and returns `None` when both are refused, so a caller cannot claim a removal it never saw. **What the full skill still needs:** the photo path (the spike could not prove a scheduled PHOTO stays hidden — deliberately not reused), the pin, comment moderation, and the insight read-back after a post. | **READY — owner: CC** | — | Write the skill (drafts scheduled for operator review by default); read a scheduled photo back through the Page's scheduled-post listing | CC | | **R-916** | Business & legal | P4 | **The logo has no usable vector master: `website/assets/logo.svg` sets „felhom.eu" as live text in the fonts „M+ 2c" and „Vremena Grotesk", which DooPlex does not have, so every renderer here draws other letters.** SEEN 2026-10-08 (Facebook pictures task): librsvg drew the lettering in DejaVu; `fc-match` resolves the family to DejaVu Sans. The PNG (645 x 408) is the only faithful copy, which caps every picture made from it at about that size (`marketing/facebook/README.md`). **-- MEASURED 2026-10-09, and the premise is WRONG for the file as it stands today:** the lettering in `website/assets/logo.svg` is **already converted to outlines**. All five wordmark elements are `` with real `d=` geometry (`path1 path3 path5 path7 path8`), and **no `` element has any content** — the two that exist are empty leftovers. The `font-family:'M+ 2c'` / `'Vremena Grotesk'` strings that this row cites are Inkscape METADATA left behind on converted paths (`-inkscape-font-specification`), not live text; a grep for `font-family` finds them and reads as live text, which is the likeliest way the original diagnosis went wrong. **Proof from a renderer that does NOT have those fonts:** Chrome draws `logo.svg`'s „felhom.eu” identical to `logo.png`'s, side by side (2026-10-09). **NOT re-tested:** librsvg specifically — DooPlex has no `rsvg-convert`, `inkscape` or `cairosvg` installed today, so the original librsvg/DejaVu observation could not be reproduced either way. It does not change the structural fact: there is no font to substitute. **Consequence:** the 645 x 408 PNG is NOT the only faithful copy and does not cap picture size; `logo.svg` can be rendered at any size, and `website/assets/logo-mark.svg` was cut from it this day (defs + path6 + g2) to fix the website header. | **READY — re-check before doing any work: this may need nothing from the operator at all.** The row asked for an Inkscape pass on „the machine with the fonts”; the measurement above says the file is already outlined and the operator may have nothing to do | nothing — the dependency on „the machine with the fonts” appears to be void (see the measurement) | CC or operator: confirm the outlines render correctly at a large size in one renderer that is not a browser, then CLOSE this row — or, if something really is still live text, say which element. If nothing is done: an operator task sits on the list that probably does not exist | operator | -| **R-917** | Business & legal | P4 | **`COPY.md` §2, the Page's longer description (867 characters), has nowhere to go: Facebook's current Pages experience has no long-description field at all.** FOUND 2026-10-08 (Page setup task, `audits/facebook-page-setup-2026-10-08/`). Looked in four places, all on the live Page as its admin: the Page's „Névjegy" tab (Rövid áttekintés / Személyes adatok / Részletek — only a 255-character „Bemutatkozás" and the pinned category), Business Suite's „Oldal módosítása" dialog (profile picture, cover, Bemutatkozás, category, phone, e-mail, address, website, social links — and nothing else), Facebook settings → „Oldal adatai" (redirects to the same Névjegy tab) and settings → „Oldal beállítása" (name, access, type, history, status, recommendation, messaging, data sharing). MEASURED by Graph with the Page token: `description`, `general_info` and `bio` all read `null`. Writing `description` by API would need `pages_manage_metadata`; the robot key's scopes are `read_insights, pages_show_list, business_management, pages_read_engagement, pages_read_user_content, pages_manage_posts, pages_manage_engagement, public_profile`, and the task's fences forbid adding a permission. So §2 is written, reviewed and unplaceable. **Options for the operator:** (a) leave §2 unused and let the 99-character intro plus the website carry it; (b) shorten §2 to ≤ 255 characters and make it the „Bemutatkozás" instead of §1 — but §1 was written for exactly that slot, so this is really „rewrite one of the two"; (c) publish §2 as the Page's first pinned post once the app is Live (R-915), which is where a long text actually gets read; (d) ask Meta support whether the field still exists for this Page type. Recommended (c) — the text reads like a post already. | **DEFERRED — operator chose (c) on 2026-10-09:** park the text and publish it as a post once posting starts. Not a defect, and not waiting on CC | nothing — **UNBLOCKED 2026-10-09**: R-915 is closed, the app is Live, so posting can start whenever the operator wants | When posting starts: publish `COPY.md` §2 (the longer description) as the first pinned post. If nothing is done: the text stays in `COPY.md` unused, which the operator has accepted | operator | +| **R-917** | Business & legal | P4 | **`COPY.md` §2, the Page's longer description (867 characters), has nowhere to go: Facebook's current Pages experience has no long-description field at all.** FOUND 2026-10-08 (Page setup task, `audits/facebook-page-setup-2026-10-08/`). Looked in four places, all on the live Page as its admin: the Page's „Névjegy" tab (Rövid áttekintés / Személyes adatok / Részletek — only a 255-character „Bemutatkozás" and the pinned category), Business Suite's „Oldal módosítása" dialog (profile picture, cover, Bemutatkozás, category, phone, e-mail, address, website, social links — and nothing else), Facebook settings → „Oldal adatai" (redirects to the same Névjegy tab) and settings → „Oldal beállítása" (name, access, type, history, status, recommendation, messaging, data sharing). MEASURED by Graph with the Page token: `description`, `general_info` and `bio` all read `null`. Writing `description` by API would need `pages_manage_metadata`; the robot key's scopes are `read_insights, pages_show_list, business_management, pages_read_engagement, pages_read_user_content, pages_manage_posts, pages_manage_engagement, public_profile`, and the task's fences forbid adding a permission. So §2 is written, reviewed and unplaceable. **Options for the operator:** (a) leave §2 unused and let the 99-character intro plus the website carry it; (b) shorten §2 to ≤ 255 characters and make it the „Bemutatkozás" instead of §1 — but §1 was written for exactly that slot, so this is really „rewrite one of the two"; (c) publish §2 as the Page's first pinned post once the app is Live (R-915), which is where a long text actually gets read; (d) ask Meta support whether the field still exists for this Page type. Recommended (c) — the text reads like a post already. **-- SCHEDULED 2026-10-09, option (c) carried out.** The text was shortened from §2 into `COPY.md` §6 (two versions; the operator chose **6.2 Közepes**, 638 Unicode characters, Hungarian only, 0 emoji, 0 hashtags). Scheduled through the robot, **not published**: post `1360018983863273_122096547315511222`, due **2026-10-12 19:00 Europe/Budapest** (epoch 1791824400 = 17:00 UTC, checked against the tz database). Read back: `is_published` **False**, scheduled time sent == read, message **hex-equal** to `COPY.md` §6.2 — and the sha256 recomputed OUTSIDE the probe agrees (`887514383eff997c`). Present in `GET /{page}/scheduled_posts`, and visible in Planner on H 12 at 19:00 with the link card attached (a different channel from the API). It can still be changed or deleted there. The number in the post was checked, not copied: the apps page carries 57 cards while saying 56, which is DELIBERATE — „6 alkalmazás + 1 beépített”, the 57th being FileBrowser, built into every box; `index.html` says „56 telepíthető alkalmazás” too. | **VERIFY -- scheduled on `main` 2026-10-09, due 2026-10-12 19:00. Not public yet; the operator reviews it in Planner and confirms after it goes out** | the scheduled time passing, then the operator's look | Operator: after it publishes on Monday evening, pin it — „…” → „Kiemelés”. Close on that word. CC (next session): read the post back — `is_published` must be **true** and the permalink must resolve; **if it did not publish, report it and do NOT post again**. If nothing is done: the post goes out anyway at 19:00 and simply is not pinned | operator | | **R-919** | Business & legal | P3 | **On a phone the Facebook Page cuts the left edge of the cover: the „s” of „saját szabályaid” and the „f” of „felhom.eu” are gone.** MEASURED 2026-10-08 on the live Page in Chrome DevTools device mode, Pixel 9 (412 × 924, mobile user agent, after a reload so Facebook serves the mobile bundle): `audits/facebook-page-setup-2026-10-08/C2-phone-headline-cut-closeup.png`. The mobile Page header is **412 × 274 = 1,504:1**, so Facebook keeps our cover's full height and shows only the centre **938 px of its 1640 px width (57,2 %)** — **351 px cut from each side**. `marketing/facebook/build.py` builds to a safe area of the centre **1028 × 544** (306 px clear of each edge), which is **45 px wider per side than the phone actually shows**; the light text in `out/cover-c.png` runs from x 333 to x 997, and the left crop edge is x 351, so the first **18 px** of the text are cut. Covers A and B are built from the same safe area and will have the same edge. Two further facts this measurement establishes: **Meta's own help page is wrong about its own rendering** — it states the mobile cover is 2,4:1 where the Page header measures 1,504:1 — and the mobile profile circle is far bigger than assumed (172 px, centred, overlapping the bottom 112 px of the 274 px cover, i.e. source x 637–1030 × y 369–624 is hidden). Not re-cropped on Facebook, per the task's fence. **-- 2026-10-09, FIXED in the build:** `marketing/facebook/build.py` now takes the phone view from the measurement (`PHONE_HDR = (412, 274)` -> the centre 938 px), so SAFE is (391, 40)-(1249, 584) and the phone profile circle is the measured CENTRED box (637, 369, 393) rather than a left-anchored one. Every cover is drawn in a derived `BAND` (408, 48)-(1249, 340), and a `cap` check holds each headline's capital at >= 4 % of the cover height. The red-proof runs on EVERY build (`control_old_window`): it draws the headline where the old 640 x 360 assumption put it, x 328, and the check must reject it -- the phone's crop edge is x 351, so 23 px were cut. Against the three covers as committed at `a76207945e` the new check convicted 3 of 3 (A 23/22 px over the left/right edges, B 63/58 px plus 1017 content pixels under the phone circle, C 63/82 px plus 1778). The profile pictures are untouched -- sha256 identical before and after. **-- 2026-10-09 (operator refinements, same day):** the profile picture now carries the logo MARK only (the lettering was unreadable at 176 px; the mark grew 69 % -> 76 % of the circle, canvas 932 -> 648), and the covers set the headline the way `site.css` sets `.page-index .hero-text h1` (Bold 700, letter-spacing -0.03em, not ExtraBold 800 untracked) with „felhom.eu” drawn from the logo's OWN lettering instead of typed. No geometry changed; every R-919 check and the red-proof stand. **-- 2026-10-09 (second measurement):** a phone has TWO views and they disagree. SIGNED IN the sides are cut (confirmed on the operator's REAL phone, Chrome/Android: the window solves to x 351..1298 against the emulator's 351..1289 - the left edge to the pixel). SIGNED OUT the FULL width is shown but the cover is top-anchored and only the top 525 px survives (the bottom 99 px is cut; the file's blue top rule is still visible, which is how the side was established), with a much bigger, higher circle at x 486..1150 from y 232. `build.py` now carries both views, models the circles as DISCS rather than rectangles running to the bottom, and splits the artwork into a READ layer (must survive every view) and a DECOR layer (may be cropped or covered; the build reports the cost - B 39 %, C 62 %). The two-view geometry convicted all three then-current covers before the redraw (A 908 px under a circle, B 1715, C 1962). Covers redrawn: C is the operator's laptop idea with the dashboard at 640 px (was 370), B's motif grown to match. **-- 2026-10-09 (the APP measured):** the operator checked the live Page in Facebook Lite and in Chrome on his phone. Solved against the laptop frame, both give a visible window of x 349..1290 / 349..1291 - the LITE APP CROPS EXACTLY LIKE SIGNED-IN MOBILE WEB, and both agree with the emulator's 351..1289. Their circle is at x 645..1021 from y 451, LOWER than the emulator's 369, so that figure was pessimistic rather than wrong. Five views measured; the signed-out one stays the binding constraint. Cover C redrawn to the operator's layout (wordmark 88 px on top, gap, catchphrase) - one line, not his two, because two measured 392 text pixels behind the signed-out circle („saját szabályaid” read „saját szab”) and sizing them to fit drops the capital to the 25 px floor. | **VERIFY -- rebuilt on `main` 2026-10-09. The app IS now measured and the cover renders correctly there; what is left is the operator's look at the NEW cover in the app after uploading it.** | — | Operator: upload the rebuilt cover-c (and the profile picture if not already), then confirm in the Facebook app that the wordmark and the catchphrase are whole. Close on that word | CC | | **R-920** | Business & legal | P4 | **The Messenger „Gyakori kérdések" automation does not exist for this Page, so `COPY.md` §5 — four questions with answers condensed from `gyik.html` — has nowhere to go.** FOUND 2026-10-09 (Page details task, `audits/facebook-page-details-2026-10-09/`). SEARCHED, not assumed absent: the create-automation catalogue („Az összes automatizálás") holds exactly THREE templates — Automatikus válasz, Távolléti üzenet, A megválaszolatlan üzenetek azonosítása (`B5-no-faq-template-all-three.jpg`); the template search for „kérdés" answers **„Nincs a keresésnek megfelelő automatizálási sablon."** while the POSITIVE CONTROL „üzenet" returns two, so the search works and the term genuinely misses; the existing instant-reply automation carries only channel, message and media — no FAQ and no quick replies; and the business-portfolio settings have no messaging/FAQ entry. The copy is written, sourced line by line to `gyik.html` and committed as `marketing/facebook/COPY.md` §5, ready to paste unchanged the day the feature appears. **Same shape as R-917** (§2 has nowhere to go), and the same cause: Meta removed a Page field this project had planned copy for. **Options for the operator:** (a) leave §5 unused until Meta brings the feature back; (b) fold the four answers into the Messenger welcome message (§3) — it holds 500 characters and today uses 120, so one or two would fit, not four; (c) publish them as a pinned FAQ post once the app is Live (R-915); (d) ask Meta support whether the FAQ automation still exists for this Page type. Recommended (a) with (c) later — the welcome message stays short, and the website's own `gyik.html` already answers these. | **DEFERRED — operator chose (c) on 2026-10-09:** park the text and publish it as a post once posting starts. Not a defect, and not waiting on CC | nothing — **UNBLOCKED 2026-10-09**: R-915 is closed, the app is Live, so posting can start whenever the operator wants | When posting starts: publish `COPY.md` §5 (the four Messenger FAQ answers) as one later post. If nothing is done: the text stays in `COPY.md` unused, which the operator has accepted | operator | diff --git a/marketing/CHANGELOG.md b/marketing/CHANGELOG.md index b01ecfeb..e757989e 100644 --- a/marketing/CHANGELOG.md +++ b/marketing/CHANGELOG.md @@ -1,5 +1,35 @@ # marketing — CHANGELOG +## facebook — the Page's first post, drafted and SCHEDULED (2026-10-09) + +R-917 option (c) carried out. The post is **not public**: it sits in Planner until Monday. + +- **`COPY.md` §6** — two versions shortened from §2, Hungarian, tegező, no price. 6.1 = 347 + characters, 6.2 = 638 (Unicode characters, counted). Every claim carries a source comment naming the + line of `website/index.html` it rests on; the first line of each carries the point alone, because + Facebook cuts after about three lines. **Zero emoji, zero hashtags** though two of each were allowed + — the design system uses no emoji and two hashtags would serve no real search. +- **The operator chose 6.2**, Hungarian only, for **2026-10-12 19:00** (over today: it was Friday + 15:08, the weakest evening of the week for a first post, and three days in Planner is review time). +- **Scheduled**: post `1360018983863273_122096547315511222`, epoch 1791824400. Read back + `is_published` **False**, scheduled time sent == read, message **hex-equal** to `COPY.md` §6.2 — and + the sha256 recomputed **outside** the probe agrees. Visible in Planner on H 12 at 19:00 with the link + card, which is a different channel from the API. +- **`fb_probe.py schedule-post`** — text + link, scheduled only. **It cannot publish immediately**: + `published` is always `false` and no argument can change it, because the operator's review in Planner + is the safety net. The body is read from `COPY.md` by section, never through a shell. Time outside + Meta's 10-minute..6-month window is refused locally. `budapest_to_epoch()` uses the real tz database + and **refuses** rather than guessing a DST offset. +- **Scenario A, the dry check**: a throwaway scheduled post **with the link** was accepted (so `link` + is not refused on a scheduled post — the open question), read back hex-equal and unpublished, seen + **present** in `GET /{page}/scheduled_posts`, deleted, and seen **absent** in the same list. The + removal proof comes from the list, not from an error after DELETE. +- **30 tests**, all passing on DooPlex (2 skip on Windows for want of a tz database). The + cannot-publish guard was **red-proofed**: removed, test seen failing `'true' != 'false'`, restored. +- **Checked, not copied:** the post repeats „56 alkalmazás". The apps page carries **57** cards while + saying 56 — deliberate: „6 alkalmazás + 1 beépített", the 57th being FileBrowser, built into every + box. `index.html` says „56 telepíthető alkalmazás" too. A „fix" here would have made a live page wrong. + ## facebook — the Meta app is LIVE; the Page can post in public (2026-10-09) R-915 is closed. What it had waited on for a day was R-813's Terms of Service URL, which did not