# Lane 1 — End-to-end user-flow matrix

Fusion Tables PPC measurement / reporting / dashboard / strategy / action-review system.
Reviewed 2026-08-30 on `lifeos-devbox`, repo `/home/matt/mirror/Fusion`, branch `round-13-takeover` @ `8351b59`.
Read-only. No live platform, CRM, WordPress or Git mutation. No test form submitted. Spend USD 0.

## Column legend

Each flow is one table. One row per step. Columns:

| Key | Column |
| --- | --- |
| **A** | Actor and business intent |
| **B** | Entry point and prerequisites |
| **C** | Critical data required |
| **D** | User action or system trigger |
| **E** | Expected state transition |
| **F** | **ACTUAL observed transition** (with evidence, or `NOT IMPLEMENTED`) |
| **G** | Visible feedback and proof |
| **H** | Next logical action |
| **I** | Failure / empty / loading / stale / retry / duplicate / recovery behavior |
| **J** | Necessary? Understandable? Optimally ordered? |
| **K** | Severity + business consequence when it fails |
| **L** | Smallest safe improvement |
| **M** | Decisive retest |

**Path shorthand** — `REPO/` = `/home/matt/mirror/Fusion/`; `ARCH/` = `REPO/outputs/loop-runs/20260830-fusion-ppc-architecture-orchestration/`;
`ORCHB/` = `/tmp/claude-1000/-home-matt-mirror-Fusion/3429ff84-e206-4a61-8d4c-770f9bcc2c74/scratchpad/orchbranch/`;
`RPT30/` = `ORCHB/outputs/loop-runs/2026-08-28-fusiontables-report/`;
`AUDIT/` = `REPO/deliverables/fusion-ppc-2026-07-30/`.

**Severity scale** — `P0` measurement integrity broken or the decision is impossible · `P1` decision is materially degraded or misleading · `P2` operator friction, recoverable · `P3` cosmetic or documentation.

**Standing precondition (`ORCH-CORR-1`, orchestrator-verified, not a Lane 1 discovery):** the reviewed
branch `round-13-takeover` is missing 41 files including all of `ads-control/`,
`deliverables/fusion-meta-ads-review-2026-08-24/` and the whole `2026-08-28-fusiontables-report/`
package except `ledger.json`. All eight child worktrees were cut from that branch. Wherever a flow
below says `NOT IMPLEMENTED — no artifact exists`, the resume point is the named `launch-prompt.md`
**and** a fixed-point repair, in that order.

---

## Flow 1 — Advertising impression / reach to click

| # | A | B | C | D | E | F | G | H | I | J | K | L | M |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 1.1 | Google/Meta ad platform on behalf of Fusion; intent: put the pool-table offer in front of in-market buyers | Live campaign with budget; approved creative; `configured` | Campaign, ad set/asset group, creative, audience, placement, bid | Auction serve | Impression recorded at platform, attributable to campaign + creative + placement | **WORKS at channel-total grain only.** `RPT30/report_data.json` `ppc.totals.impressions` 105,555 (prev 13,697); `meta_ads.totals.impressions` 63,461 (prev 18,286). `observed` | Numbers reach the client at `RPT30/report.md:14` and `:27` | Break impressions down by the dimension that drives spend | Platform-side retries invisible to us. If the Meta session expires the whole read fails: `source_health[meta_ads].status = "degraded"`, caveat "The configured Meta API session is invalid (190/467)" — recovered by an authenticated browser export, not by the API | Necessary and correctly first. **Not optimally decomposed**: 100% of Google delivery came from one PMax campaign (`ppc.campaigns`: `2605-Fusion-Tables_Pmax` 4,410 clicks; two Search and two Display campaigns 0), so a campaign-grain impression number carries no decision information | `P1` — spend is concentrated in the one channel type that exposes the least structure, and nothing surfaces that | Add asset-group and placement grain to the Google collector; add ad-level grain to the Meta collector | Re-run the collector and assert `ppc.campaigns[*].metrics` is joined by an asset-group array, and `meta_ads` returns >1 ad row |
| 1.2 | Prospect; intent: find a pool table | Sees the ad | Ad copy, creative, offer clarity | Clicks | Click recorded, browser navigates to landing URL with UTM + click ID | **WORKS.** `ppc.totals.clicks` 4,410 (prev 452); `meta_ads.totals.clicks` 825, of which `actions.link_click` 727 — a **17.5% gap between "clicks (all)" and link clicks** that nothing in the system explains to a reader. `observed` | `report.md:15`, `:28`. The clicks-all vs link-click distinction is noted only in `report_data.json` `meta_ads.notes[0]` and never surfaced to the client | Compare cost per link click, not cost per click | No retry semantics; a lost click is simply absent | Necessary. **Not fully understandable**: two different click definitions coexist and the report publishes the larger one | `P1` — CPC published to the client (109 Ft) is computed on clicks-all; on link clicks it is 123 Ft. The client is shown the flattering denominator without being told | Publish link-click CPC beside clicks-all CPC with both labels | Recompute `meta_ads.totals.cpc` against `actions.link_click` and confirm the report renders both |
| 1.3 | Analyst; intent: know the true reach base | Platform report access | Reach vs impressions | Read report | Reach (people) available as the funnel's first person-grain step | **NOT IMPLEMENTED.** No reach/person field is collected anywhere. `report_data.json` `meta_ads.totals` has spend, impressions, clicks, ctr, cpc only. `observed` | None | Decide whether the funnel's x-axis is events or people | N/A — never requested | Necessary **for Matt's literal ask** ("the number of people reached that funnel step"). Missing | `P1` — the funnel Matt described cannot have a person-grain y-axis from current data; building it anyway would be a silent event→person substitution, which `ARCH/children/t3-decision-dashboard/goal-v1.md` row `T3-03` explicitly forbids | Add Meta `reach` to the collector (one field, already in the Insights API) and label every other step as events | Assert `meta_ads.totals.reach.current` exists and is `< impressions` |

---

## Flow 2 — Click to landing-page arrival with source, campaign, creative/keyword, placement, audience, landing page and click-ID persistence

| # | A | B | C | D | E | F | G | H | I | J | K | L | M |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 2.1 | Prospect's browser | Click on ad | Full URL with `utm_source/medium/campaign/content/term` + `gclid`/`gbraid`/`wbraid`/`fbclid` | Navigation | Landing page served, parameters intact | **WORKS.** The exact paid URL is recorded: `report_data.json` `ppc.landing_pages[0].url` = `https://fusiontables.hu/billiardasztal/?utm_source=google&utm_medium=cpc&utm_campaign=2605-Fusion-Tables_Pmax&utm_content=Fusion_tables_biliard&utm_term=biliard`, `in_paid_inventory: true`. `observed` | The URL is in the report data, not in any client-visible surface | Persist the parameters into the form | Cache layer sits between — see 2.2 | Necessary, correctly ordered | — | — | — |
| 2.2 | CDN / page cache | Anonymous GET | Cache key must include query string | Page served from cache | A cache `MISS` per unique query, or a cache key that excludes ad parameters | **DEFECT, directly observed.** `AUDIT/evidence/tracking-audit.md:66-77`: a clean, query-less GET of `/billiardasztal/` returned cache `HIT` whose **three CF7 form action URLs carried a previous advertising visitor's UTM parameters and Google click ID**. The same non-empty click ID persisted under `Cache-Control: no-cache`. A unique audit query produced a `MISS` whose form action contained only the audit query. Graded **P0** by the auditors. `defect` | None. Nothing in any dashboard or report surfaces this | Fix the cache key before trusting any attribution | **This is the recovery-behavior failure**: the system's "recovery" from a cache miss is to serve another visitor's identifiers | Necessary step, **badly implemented**. A visitor who arrives organically can be stamped with a paid click ID | `P0` — every downstream attribution claim, and any future offline-conversion upload keyed on `gclid`, may carry the wrong person's identifier. This poisons the one repair (offline conversion) that would unlock revenue measurement | Exclude the ad-parameter query from the HTML cache key, or render click-ID fields client-side only | Re-run the exact three-request probe in `tracking-audit.md:66-77` (clean GET, `no-cache` GET, unique-query GET) and assert all three form actions carry no foreign click ID |
| 2.3 | Landing page (CF7 form ×3) | Page loaded | Hidden fields | Page render | Hidden fields populated from the current URL | **CONFIGURED and present.** `tracking-audit.md:24-26`: form ID `3154`, three instances on the production landing page; hidden click-ID fields `gclid, gbraid, wbraid, dclid, fbclid, msclkid, ttclid, li_fat_id`; hidden `landing_page` field. On first HTML the click-ID fields were empty. `configured` | Invisible to the user by design | Carry the values through to the CRM row | If the page is cached, the fields may hold a stale value (2.2) | Necessary. Three identical form instances on one page is a duplicate-control smell and a duplicate-event risk | `P1` (rises to `P0` combined with 2.2) | Reduce to one form instance per page, or give each a distinct `form_location` | Load the page and assert exactly one `#3154` instance, or three with distinct locations |
| 2.4 | GTM container | Page load | Consent state | Tag fire | `page_view` to GA4, Ads tag, Meta Pixel | **PARTIAL, with a named oddity.** `tracking-audit.md:52-62`: in a clean browser with collectors blocked, `page_view` fired **and so did an analytics event named `ads_conversion_Purchase_Page_load_http_1`** on page load. No Meta collector request before consent interaction. No cookie banner appeared. `observed` | Nothing user-visible | Verify what a "Purchase" conversion action fires on | Consent baseline undocumented: `tracking-audit.md:78-89` lists five open questions | Necessary. **Not understandable**: a conversion action named `Purchase` firing on page load is the single most misleading configuration in the stack | `P0` — `report_data.json` `ppc.conversion_actions` confirms a live action `Purchase (Page load https://fusiontables.hu/billiardasztal/billiardasztal)`. A page-load "Purchase" inflates the 49 conversions and would corrupt any revenue signal | Demote every page-load and time-on-site action to Secondary; leave exactly one Primary business submission action | Read the Ads conversion-actions list and assert exactly one Primary action, whose source event is a form submission |

---

## Flow 3 — Landing-page engagement to form start and successful submission

| # | A | B | C | D | E | F | G | H | I | J | K | L | M |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 3.1 | Prospect; intent: request a quote | On landing page | Field focus | Click into name/email/phone/message | One `form_start` per session | **WORKS but over-counts.** `tracking-audit.md:32`: trigger is `gtm.click` on a key field → GA4 `vhk_form_start` and Meta `VHK_form_start`, **once per page load**, and the Google Ads action is **Primary and counts every occurrence**. Volume: `meta_ads.actions...VHK_form_start` = 14 current, 8 previous. `observed` | Nothing visible to the user | Complete and submit | A user who clicks a field on three page loads generates three form starts | Necessary. **Wrongly weighted**: an interaction micro-event is a Primary Ads conversion | `P1` — Meta optimizes toward form starts, not submissions. `report.md:33` says so plainly to the client; the bidding still runs on the wrong target | Change the Meta results event to a verified submission and demote the Ads form-start action to Secondary (requires Matt's approval — `M-20`) | After the change, assert Meta `Results` count equals the submission event count for the same window |
| 3.2 | Prospect | Form filled | Valid input | Submit | Exactly one lead event per submission | **DUPLICATION DEFECT, proven by configuration.** `tracking-audit.md:37-51`: one successful CF7 submit pushes **two** dataLayer events (`generate_lead` with a `hero`/`cta`/`chat` location, and `wpcf7mailsent` with form ID 3154). Under current GTM rules that produces GA4 `VHK_lead` **plus** one of `hero_generate_lead`/`cta_generate_lead`/`chat_generate_lead` **plus** a Meta standard `Lead`. Multiple of those GA4 lead actions are Primary in Ads. `defect` | The user sees a CF7 success message (not verified in this review — no test submission was authorized) | Land the enquiry in the CRM | **No dedupe rule exists anywhere.** The audit stops short of asserting the 49/5 counts are duplicated, correctly: `tracking-audit.md:51` says the composition of the PMax conversions is not visible | Necessary. **Not optimally ordered**: two success events for one business outcome | `P0` — one submission can become several Google Ads conversions. This is the mechanical explanation for platform 49 vs register 13 (`report.md:7`) and it makes cost-per-lead unusable for reallocation | Collapse to one Primary conversion action fed by `wpcf7mailsent` only | Fire one real submission in a consented test environment and assert exactly one Primary Ads conversion, one GA4 lead event and one Meta `Lead` |
| 3.3 | Prospect | Submission accepted | — | — | Clear confirmation + expectation of response time | **NOT VERIFIED.** No test submission was authorized in this review or in the July audit (`tracking-audit.md:106`: "CRM megkapta a tesztleadet — Nem teszteltük"). `hypothesis` that the CF7 default confirmation shows | None held | — | Unknown | Necessary; state unknown | `P2` — an unverified confirmation is a plausible silent drop-off point | With Matt's approval, run one marked test submission end to end | Submit one `QA` -marked lead and trace it to the Sheet, the Gmail notification and the platform events |

---

## Flow 4 — Form submission to CRM creation and deduplication

| # | A | B | C | D | E | F | G | H | I | J | K | L | M |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 4.1 | CF7 / WordPress; intent: notify the owner | Successful submit | Contact fields, UTM, click IDs | `wpcf7mailsent` | Email notification sent | **WORKS, historically proven.** `AUDIT/evidence/crm-reconciliation.md:9-13`: ten timestamped non-test submission records 2026-07-16..07-28, each matched **1:1** to exactly one Gmail notification by date and contact identifier. `observed` | Owner's inbox | Record it | Ten of ten matched — no observed loss in that window | Necessary, correct | — | — | — |
| 4.2 | Google Sheet `FusionTables - Ajánlatkérések`; intent: be the CRM | Notification/integration | Date, contact, and ideally source + click ID | Row append | Row created with source attribution | **PARTIAL — the attribution field does not exist.** `RPT30/crm_connector_receipt.json`: spreadsheet `1L9YZG54Zyo7dTzRihp0qy5Qs-Fl4uyuaY770uCzOMPw`, 5 worksheets, `Régi Billiárd Ajánlatkérések` 13 current / 7 previous, the other four worksheets 0/0. `report_data.json` `crm.source_column = null` and `crm.no_source_column_note`: "this spreadsheet has no source or campaign column, so enquiries cannot be attributed to a traffic source." `observed` | `report.md:53-57` shows the by-form breakdown and states the attribution limit to the client | Add the missing columns | **Empty-state defect:** four of five worksheets read 0/0 in both periods. That is a **real** zero for a form that exists but received nothing, and it is presented identically to a form that was never wired. Nothing distinguishes them | Necessary. **Badly ordered**: the click IDs are captured in the form (2.3) and in the email, then discarded before the CRM row | `P0` — this is the single break that makes `M-10` ("where to allocate more of the budget") unanswerable. Channel spend is known, channel outcome is not | Add three columns to the Sheet: `source`, `gclid_or_fbclid`, `landing_page`, populated from the fields the form already carries. `crm-reconciliation.md:70` names this exact repair | Submit one test enquiry and assert the new row carries a non-empty click ID matching the ad click |
| 4.3 | Report collector; intent: not count test rows | Scheduled read | A QA marker | Read + filter | Test rows excluded, real rows counted once | **WORKS.** `crm_connector_receipt.json` `qa_exclusion`: worksheet `Termékoldal Ajánlatkérés`, 7 rows, `marker_verified: "QA"`. Reported at `report.md:57` and `:88`. `observed` | Client sees why the number is 13 and not 20 | Make the marker permanent | Recovery is **manual and repeated every month** — `recommendations.json` `R-2026-07-02` exists precisely to make it structural | Necessary. Understandable. **Should not need to exist** | `P2` — a monthly manual correction that will eventually be forgotten | Add a dedicated `test` column or a separate test worksheet | Re-run the collector and assert `qa_exclusion.rows` is 0 because no QA row reaches the business sheet |
| 4.4 | Report collector | Read the legacy worksheet | A parseable date header | Column read | Standard header detection succeeds | **DEGRADED, with a working fallback.** `report_data.json` `source_health[crm_sheet].status = "degraded"`: the blank date header defeated the standard collector, so an authenticated read of column A was used. `limitations[4]` states it. `observed` | Disclosed to the client at `report.md:88` | Fix the header | This is the system's **best recovery behavior**: it degraded, disclosed, and still produced a correct number | Necessary. Good | `P3` | Add the header | Re-run and assert `source_health[crm_sheet].status == "ok"` |

---

## Flow 5 — CRM lead to qualified lead, quote, sale, purchase and revenue

| # | A | B | C | D | E | F | G | H | I | J | K | L | M |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 5.1 | Márta / sales; intent: work the enquiry | Row in the Sheet | Contact + context | Call/email, write a note | Structured status `contacted` with a date and an owner | **PARTIAL — free text only.** `crm-reconciliation.md:27-40`: of 10 records, 9 have a free-text follow-up note, 1 has none, 1 documents a failed call attempt, 1 records an unqualified/price-only enquiry. No structured status field. `observed` | Only readable by opening the sheet | Qualify | No state machine, so no retry, no stale detection, no reopen | Necessary. **Not structured**, so not measurable | `P1` | Add a `status` enum column with a date and owner (`crm-reconciliation.md:65-67`) | Read the sheet and assert every row in the window has a non-empty status and status date |
| 5.2 | Sales; intent: send a quote | Contacted lead | Quote value | Send offer | `quote_sent` with an amount | **PARTIAL — count only, no value.** 6 of 10 records document that an offer was sent (`crm-reconciliation.md:33`, `evidence-matrix.md:17`). The auditors explicitly refuse to call these six qualified leads or sales. `observed` | Free-text notes only | Track acceptance | **Data-integrity defect surfaced:** one offer note carries the date `2027.07.29` inside a 2026 record set — flagged as a probable entry error with the correct date unknown, deliberately not corrected (`date-account-caveats.md:34`) | Necessary. **Not measurable**: `evidence-matrix.md:17` — "Offer sent is not qualification, acceptance, purchase, or revenue" | `P0` for the business question — quote value is the first field that would let cost-per-outcome be computed at all | Add `quote_value_huf` and `quote_sent_date` columns | Assert every row with an offer note has a numeric quote value |
| 5.3 | Sales; intent: qualify | Contacted lead | Qualification criteria | Judgement | `qualified` / `disqualified` | **NOT IMPLEMENTED.** `evidence-matrix.md:25`: "Qualified leads, purchases, sales, and revenue — `UNAVAILABLE` … Never represent as zero." `observed` | None | — | N/A | Necessary and **entirely absent**. It is also the target `ARCH/goal-v1.md:76-77` names for optimization ("optimized toward qualified leads, quotes, purchases, and revenue") | `P0` — the frozen goal commissions optimization toward a field that does not exist in any system | Define the qualification rule with Márta and add one column | Assert the qualified count for a closed month is a non-null integer |
| 5.4 | Owner; intent: know revenue | Won deal | Amount, date, source | Record | `won` + revenue amount | **NOT IMPLEMENTED.** `crm-reconciliation.md:36`: "Lezárt értékesítésre vagy bevételre utaló strukturált mező és igazoló bejegyzés nincs." `report_data.json` has no revenue field; `ppc.totals.conversion_value` is 49.0 with `unit: "huf"` — a **count mislabelled as currency**, which drives `ppc.totals.roas` to 0.000532. The report correctly declines to publish either. `defect` | None reaches the client — correctly | — | N/A | Necessary. Absent. **The `conversion_value`/`roas` pair is actively misleading if any future dashboard renders it naively** | `P0` — no revenue anywhere means no ROAS, no true CPA, and no way to answer "better quality leads". The mislabelled unit is a trap for the T3 dashboard that was never built | Add `won` + `revenue_huf` + `won_date` columns; and null out `conversion_value`/`roas` in the data contract until a real value exists | Assert `report_data.json` `ppc.totals.roas` is `na` rather than a number, and that a closed month returns a revenue figure |

---

## Flow 6 — Offline conversion and revenue feedback to Google Ads and Meta

| # | A | B | C | D | E | F | G | H | I | J | K | L | M |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 6.1 | Operator; intent: teach the platforms what a good lead is | Qualified/won rows with click IDs | `gclid`/`fbclid` + outcome + value + timestamp | Export | Upload file produced | **NOT IMPLEMENTED — no artifact exists.** Missing artifact: the offline-conversion section of `ARCH/children/t2-measurement-audit/artifacts/event-conversion-taxonomy.md` (row `T2-04`). It appears in the system exactly once, as a future bullet: `AUDIT/evidence/tracking-audit.md:110-117` item 6, "offline minősített-lead és értékesítési visszacsatolás tervezése". `observed` | None | — | N/A | Necessary and correctly *last* in the chain — it depends on 4.2 (click ID in the CRM) and 5.3/5.4 (outcome fields), **both of which are missing** | `P0` for the long-term business outcome; `P2` today because two hard prerequisites block it anyway | Do nothing here yet. Fix 4.2 first — the click ID must survive into the CRM row before any upload is possible | After 4.2 ships, assert a CRM export contains ≥1 row with a non-empty `gclid` and a non-null outcome |
| 6.2 | Google Ads / Meta CAPI | Upload | Matched click IDs | Import | Offline conversions matched and attributed | **NOT IMPLEMENTED.** No connector, no schedule, no receipt anywhere in the repo. `observed` | None | — | N/A | Necessary eventually | `P0` long-term — this is the only mechanism that makes "more and better quality leads from the same budget" (`M-10`) mechanically achievable rather than aspirational | — | — |
| 6.3 | Bidding algorithm | Offline conversions imported | Sufficient volume | Automatic | Bids shift toward quality | **NOT IMPLEMENTED**, and **would be unsafe today**: the cache-poisoning defect at flow 2.2 means an uploaded `gclid` may not belong to the person who converted | None | — | N/A | Necessary eventually | `P0` — uploading offline conversions before the cache defect is fixed would actively train the algorithm on wrong identifiers | Sequence explicitly: fix 2.2 → fix 4.2 → fix 5.3/5.4 → then 6.1 | Re-run the 2.2 cache probe clean before any upload is authorized |

---

## Flow 7 — Dashboard refresh from raw sources to validated presentation

| # | A | B | C | D | E | F | G | H | I | J | K | L | M |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 7.1 | Matt/operator; intent: see current numbers | A dashboard exists | — | Run `refresh-dashboard.sh` | Collectors run, data written, dashboard re-renders | **NOT IMPLEMENTED — no artifact exists.** Missing: `ARCH/children/t3-decision-dashboard/artifacts/refresh-dashboard.sh` (row `T3-02`). Child state: `run-state.md` → `- Status: WAITING_DEPENDENCY`, `- Started: NOT STARTED`; no `artifacts/` directory. Resume point: `ARCH/children/t3-decision-dashboard/launch-prompt.md`. `observed` | None | — | N/A | Necessary | `P0` — the central deliverable of Matt's ask has no refresh path | Build the refresh around the **existing** report collectors rather than a new pipeline — the data contract already exists (`RPT30/report_data.json`) | `bash refresh-dashboard.sh && test -s dashboard/data.json` |
| 7.2 | Report generator (the pre-existing pipeline that *does* work) | Client config + credentials | Google Ads, Meta, Sheet, Notion access | Generator run | Raw reads → `report_data.json` → `report.md` → publish | **WORKS, and is the model to copy.** `RPT30/run_receipt.json` `"complete": true`, `verified_at 2026-08-29T01:16:57`, with SHA-256 for `evidence.json`, `report_data.json`, `report.md`, `narration.txt` all matching `report_manifest.json`; `notion_url` and `slides_url` recorded with `native_revision_id`. `observed` | Notion page + Google Slides deck + `.pptx` | Render the same data as a funnel | `report_manifest.json` shows `"complete": false` at manifest phase and `run_receipt.json` `"complete": true` at finalize — a **real two-phase state machine** | Necessary, well ordered, and the strongest engineering in the system | — | — | — |
| 7.3 | Validation layer | Raw reads done | Per-source health | Automatic | Every source graded, degradations disclosed | **WORKS.** `report_data.json` `source_health` — 7 rows, `google_ads` degraded, `meta_ads` degraded (190/467), `search_console` absent, `ga4` absent, `bigquery` disabled, `crm_sheet` degraded, `work_evidence` degraded — each with an explicit caveat and a client-facing note. `limitations` carries 5 entries. `observed` | `report.md:85-89` renders all five limits in Hungarian | Reuse this vocabulary in the dashboard | **This is the correct stale/partial/failure behavior**: the run completes and discloses rather than failing or guessing | Necessary, understandable, correctly ordered | — | Reuse `source_health` verbatim as the dashboard's state layer instead of inventing `T3-07` from scratch | Assert the dashboard renders one badge per `source_health` row |
| 7.4 | Meta creative dashboard (the one dashboard-shaped artifact that exists) | Page opened from repo root | `meta_ads.json`, `meta_ad_insights.json` | Page load | Creative table + targeting + decision queue render | **BROKEN ON EVERY CHECKOUT.** `ORCHB/deliverables/fusion-meta-ads-review-2026-08-24/index.html:395` sets `SOURCE_ROOT = '/.tmp/overnight-2026-08-10/data/'`; `.tmp/` is gitignored (`REPO/.gitignore:8`) and does not exist; `git ls-tree -r origin/codex/fusion-ppc-architecture-orchestrator` returns neither JSON file. The page always enters its catch block at `:550-551`. `defect` | The user sees: "Meta ad inventory failed to load. Serve this page from the Fusion repository root." | Repoint or regenerate the data | **The error message misdiagnoses the failure** — it blames the serving location for a missing file. An operator will waste time re-serving from different roots | Necessary artifact, **fatally wired**. The data source is outside version control by design | `P0` — the only creative/placement/demographic surface in the system is permanently empty, and its own test says it is fine (7.5) | Commit a small `data/` snapshot beside the HTML and repoint `SOURCE_ROOT` to a relative path | Serve the repo root, load the page, assert `#creative-table-body` has >0 `<tr>` rows |
| 7.5 | Regression check | — | — | `python3 -m pytest -q` | A failing data source fails the test | **FALSE GREEN — reproduced twice.** `ORCHB/deliverables/fusion-meta-ads-review-2026-08-24/test_dashboard.py:32-39` asserts only that the served HTML *contains the string* `"meta_ad_insights.json"`. It never fetches it. Result: `1 passed` while the page renders its failure state. `defect` | Green test output | Assert on data, not on markup | This is precisely the failure `ARCH/children/t3-decision-dashboard/goal-v1.md` row `T3-09` was written to prevent — already live | Necessary. **Actively harmful as written**: it manufactures false confidence | `P0` — a green check over a dead dashboard is worse than no check | Add one assertion that the two JSON files exist and parse | Delete a data file and assert the suite goes red |

---

## Flow 8 — Last 30 completed days versus the preceding 30 completed days

| # | A | B | C | D | E | F | G | H | I | J | K | L | M |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 8.1 | System; intent: define comparable windows | Report run | Timezone, completed-day boundary | Automatic | Both windows resolved per source | **WORKS.** `report_data.json` `windows` declares `current`, `previous` and `timezone` separately for `google_ads`, `meta_ads`, `bigquery`, `crm_sheet`, `work_evidence`. Ads `2026-07-25..2026-08-23`; CRM `2026-07-26..2026-08-24` vs `2026-06-26..2026-07-25`, `Europe/Budapest`. `observed` | Appendix `report.md:76-83` names each window | Fetch both | — | Necessary. **Correctly refuses a single global window** — the sources genuinely differ | `P3` | — | — |
| 8.2 | Collectors | Windows resolved | Both-period metrics | Fetch | Every metric returns current + previous | **WORKS, and is the reusable asset.** Every metric in `report_data.json` is `{current, previous, unit, na}` — the `na` slot is the honest-unavailable channel. Verified on `ppc.totals`, `meta_ads.totals`, `meta_ads.actions`, `crm.totals`, `ppc.landing_pages[0]`. `observed` | `report.md:11-18`, `:24-31`, `:47-49` render current / previous / change | Render as funnel steps | If a source is absent the envelope carries `na` rather than 0 | Necessary, well designed | — | Build `T3-01`/`T3-03` on this shape rather than re-deriving it | Assert the dashboard reads `report_data.json` and renders `na` cells distinctly from `0` |
| 8.3 | Reader; intent: judge direction | Both periods available | A meaningful baseline | Read the change column | Trustworthy percentage change | **PARTIAL — one misleading case published.** Google conversions render as `+4800,0%` (49 vs 1) at `report.md:18`. The evidence corpus knows the prior period is not a valid base — `evidence-matrix.md:11`: "Prior period had no spend. Percentage change is not applicable." — but the newer report still prints the percentage. `defect` | The client sees `+4800,0%` | Suppress the percentage when the base is degenerate | No stale-data or low-base guard exists | Necessary. **Not understandable as rendered** | `P1` — a 4800% headline against a base of 1 invites a budget decision that the data cannot support, and it is the first number a client's eye lands on | Suppress or footnote any change where the previous value is `< 5` | Recompute the report and assert changes with `previous < 5` render as "nem értelmezhető" |
| 8.4 | The only artifact with an explicit "30 vs prior 30" column | Stage-3 dashboard template | Per-keyword trend | Render | Real 30-vs-30 trend per row | **SYNTHETIC BY DESIGN.** `ORCHB/ads-control/template/stage-3-ai-advice-review.html:60` defines the column `['trend','30 vs prior 30']`; `:65-66` renders it from `trendPctDemo`, `recentClicksDemo`, `priorClicksDemo` behind a visible `DEMO/SYNTHETIC` badge. `observed` | The `DEMO/SYNTHETIC` badge is shown — honest | Wire it to real data | — | Necessary. **Correctly labelled**, which is the right behavior for a placeholder | `P1` — the system's only per-row period comparison is a placeholder, and it sits on the keyword grain Matt asked to replace (`M-11`) | Do not wire this column. Rebuild the comparison on the creative/targeting grain instead | Assert no `DEMO/SYNTHETIC` badge appears in any artifact shown to Matt or a client |

---

## Flow 9 — Filtering and comparing campaign, ad set/ad group, keyword/search term, audience, demographic, copy, creative, placement, geography, device, product and landing page

| # | A | B | C | D | E | F | G | H | I | J | K | L | M |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 9.1 | Matt; intent: find where the money works | Dashboard open | A breakdown per dimension for both periods | Pick a dimension | Table re-renders at that grain | **NOT IMPLEMENTED — no artifact exists.** Missing: the breakdown section of `ARCH/children/t3-decision-dashboard/artifacts/dashboard/index.html` (rows `T3-05`, `T3-06`). `observed` | None | — | N/A | Necessary — this is the operative half of `M-05`..`M-09` | `P0` | See 9.2-9.4 for the cheapest real coverage | — |
| 9.2 | — | — | — | — | Dimensions **present somewhere** | `campaign` — `report_data.json` `ppc.campaigns` (5 rows) and `meta_ads.campaigns` (1 row); `ad group` + `keyword` + `search term` — `ORCHB/ads-control/advice-bundles/*.json` (61 keyword rows, keys `criterion_id, campaign_name, ad_group_name, keyword_text, match_type`); `landing page` — `ppc.landing_pages` (**1 row**). `observed` | Only inside JSON | Render them | `ppc.search_terms` and `cro.landing_pages` are **empty lists** in the report data, and `ppc.change_events` is empty | Present but unrendered and unfiltered | `P1` | Render the four dimensions that already have data before collecting anything new | Assert the dashboard renders ≥4 dimension tables from existing JSON with no new API call |
| 9.3 | — | — | — | — | Dimensions **present but unreachable** | `creative` (`ORCHB/deliverables/fusion-meta-ads-review-2026-08-24/index.html:241-242`), `placement` and `age/gender` and `geo` (`:276-278`), `targeting` (`data-view="targeting"`, self-labelled **mixed-pool**) — all blocked by the missing `SOURCE_ROOT` (flow 7.4). `defect` | The failure empty-state | Fix 7.4 | Empty state is an error string, not a labelled "data unavailable" state | Present in design, absent in fact | `P0` — this is exactly the set Matt named in `M-05`..`M-09` | Fix 7.4 first; it unlocks four of the missing dimensions at once | Load the page and assert the targeting and demographic views render non-zero rows |
| 9.4 | — | — | — | — | Dimensions **absent everywhere** | `ad copy`, `audience`, `device`. No collector requests them; no artifact contains them. `observed` | None | Add to the Meta/Google collectors | N/A | `ad copy` is explicitly requested by `M-06`; `device` and `audience` come from `T3-05`/`T3-06` | `P1` | Add `ad_name` + `device` breakdowns to the Meta Insights call — both are single parameters on an API call already being made | Assert `meta_ads` returns an `ads[]` array with per-ad spend and results |
| 9.5 | Matt; intent: compare a dimension across the two periods | Breakdown rendered | Both-period values **per dimension row** | Toggle period | Each row shows current vs previous | **NOT IMPLEMENTED.** The `{current, previous}` envelope exists on totals and on `ppc.landing_pages[0]`, but **not** on `ppc.campaigns[*].metrics` (they carry `{current, previous}` for clicks only) and not on any Meta breakdown. `observed` | None | Extend the envelope down to breakdown rows | N/A | Necessary — a dimension without a period comparison cannot drive a reallocation | `P1` | Apply the same envelope to every breakdown row, not only to totals | Assert every breakdown row exposes `current` and `previous` |

---

## Flow 10 — Turning dashboard evidence into a budget reallocation decision

| # | A | B | C | D | E | F | G | H | I | J | K | L | M |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 10.1 | Matt; intent: move budget to what produces good leads | Dashboard with breakdowns | Spend + qualified outcome + cost per outcome, per dimension row | Scan for candidates | Candidates surface with an evidence-backed cue | **NOT IMPLEMENTED — no artifact exists.** Missing: decision cues, row `T3-06`. Nearest existing engine: `ORCHB/ads-control/advice-bundles/advice_output-*.json` with `needs_attention`, `promotion_candidate`, `exclusion_candidate`, `ai_note`, `cited_figures`. `observed` | None | — | N/A | Necessary — this is the *purpose* of everything above it | `P0` | — | — |
| 10.2 | — | — | — | — | The existing engine returns usable candidates | **STRUCTURALLY CANNOT.** `report_data.json` `kpc_snapshot.advice_summary` = `{approve_for_planning: 0, investigate_next: 0, wait_for_evidence: 61}`, `executed: false`, `criterion_coverage {current: 61, represented_prior: 61, missing: 0}`. All 61 rows say wait. Corroborated by `evidence-matrix.md:20`. `observed` | — | Point the engine at a delivering dimension | The engine's honest default is "wait for evidence" — correct behavior, useless output | The engine works. **It is aimed at the wrong dimension for this account** | `P0` — the advice surface is keyword-grain on an account where Search delivered a **source-verified zero** and PMax carried 4,410 of 4,410 clicks (`ppc.campaigns`). Keyword advice on a zero-delivery surface can never yield a budget decision | Retarget the advice engine's row grain from keyword to creative + placement + audience (this **is** `M-11`) | Regenerate an advice bundle at creative grain and assert `approve_for_planning + investigate_next > 0` |
| 10.3 | Matt; intent: sanity-check a candidate before moving money | A candidate row | A denominator that is a business outcome | Inspect | Cost per qualified lead / per quote visible | **NOT IMPLEMENTED and not derivable.** Qualified lead, quote value, sale and revenue are all `UNAVAILABLE` (flow 5.3, 5.4). The only available denominators are platform events, which `report.md:20` and `:33` both tell the reader not to trust as business outcomes | — | Fix flow 5 first | N/A | Necessary. **Blocked upstream** | `P0` — every reallocation decision available today is made on event counts, i.e. on the metric the system itself warns is not a business outcome | Sequence honestly: flow 4.2 + flow 5.3/5.4 are prerequisites for flow 10, not parallel work | Assert a candidate row can display a non-null cost-per-qualified-lead |
| 10.4 | Matt; intent: record the decision | Decision made | Persistence | Write a note | Decision stored and visible next month | **NOT IMPLEMENTED — and actively lossy where a control exists.** The stage-3 template's per-row note field is labelled "Local session only; not persisted" (`ORCHB/ads-control/template/stage-3-ai-advice-review.html:89`). `observed` | A textarea that silently discards input | — | **Duplicate-work behavior:** next month's operator re-derives the same judgement | Necessary. **A control that accepts input and throws it away is worse than no control** | `P1` — decision rationale is lost every cycle, guaranteeing repeated analysis | Either persist notes to a checked-in JSON or remove the textarea and label the surface read-only | Type a note, reload, assert it survives — or assert the control is gone |

---

## Flow 11 — Scenario A review and decision: one FusionTables pool-table landing page

| # | A | B | C | D | E | F | G | H | I | J | K | L | M |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 11.1 | Matt; intent: decide whether to keep concentrating on one page | Strategy A document | Verified combined baseline `B`, channel roles, allocation ranges, operating rules | Open and read | A decision-level strategy is readable | **NOT IMPLEMENTED — no artifact exists.** Missing: `ARCH/children/t4a-single-landing-strategy/artifacts/strategy-a-one-landing-page.md` and `artifacts/budget-baseline-and-allocation.md` (rows `T4A-01`, `T4A-02`). Child has five control files, no `artifacts/`; `run-state.md` `- Status: WAITING_DEPENDENCY`, `- Started: NOT STARTED`. Resume: `ARCH/children/t4a-single-landing-strategy/launch-prompt.md`, gated on parent acceptance of T2. `observed` | None | — | N/A | Necessary | `P1` — Matt cannot compare the two options he asked for | The baseline is one addition away: `ppc.totals.cost.current` 92,096 Ft + `meta_ads.totals.spend.current` 89,778 Ft = **181,874 Ft** for `2026-07-25..2026-08-23`. Freeze that as `B` before relaunching T4A | Assert `budget-baseline-and-allocation.md` states `B` with both source figures and the exact window |
| 11.2 | Matt | Strategy read | Current single-page performance | Compare | A grounded keep/expand judgement | **NOT IMPLEMENTED**, but the input exists: `ppc.landing_pages` has exactly **one** row, `/billiardasztal`, `in_paid_inventory: true`, 105,555 impressions / 4,410 clicks / 92,096 Ft. Scenario A's surface is literally one URL. `observed` | None | — | N/A | Necessary. The scenario is unusually cheap to write because the surface is a single page | `P1` | Write Scenario A against that one row rather than waiting for the full T2 audit | Assert the strategy cites `ppc.landing_pages[0]` |
| 11.3 | Matt | Both scenarios read | — | Approve / reject / defer | Decision recorded and dispatchable | **NOT IMPLEMENTED.** No decision surface exists (see flow 14). `observed` | None | — | N/A | Necessary | `P1` | — | — |

---

## Flow 12 — Scenario B review and decision: the wider product suite under the 1.5x combined budget ceiling

| # | A | B | C | D | E | F | G | H | I | J | K | L | M |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 12.1 | Matt; intent: decide whether to widen to six offers | Strategy B document | `B`, enforced cap `C = 1.5 × B`, six-offer inventory, anti-dilution rules | Open and read | Strategy readable with the cap visibly enforced in every allocation table | **NOT IMPLEMENTED — no artifact exists.** Missing: `ARCH/children/t4b-full-suite-strategy/artifacts/strategy-b-full-product-suite.md` and `artifacts/budget-cap-and-allocation.md` (rows `T4B-01`, `T4B-02`). Resume: `ARCH/children/t4b-full-suite-strategy/launch-prompt.md`. `observed` | None | — | N/A | Necessary | `P1` | `C` = `1.5 × 181,874` = **272,811 Ft/month** on the observed baseline. The system has never computed this number. Compute and freeze it first | Assert the strategy's allocation tables sum to `≤ C` and state `C` explicitly |
| 12.2 | Matt; intent: avoid spreading a small budget thin | Strategy B read | Per-offer minimum viable spend | Judge | Concentration rules make dilution visible | **NOT IMPLEMENTED.** The six offers (Diagonal pool tables, ping-pong, shuffleboard, football tables, chairs, the existing FusionTables offer, per `ARCH/goal-v1.md:112-113`) appear nowhere in this repo as an inventory. `observed` | None | — | N/A | Necessary — 272,811 Ft across six offers and two channels is roughly 22,700 Ft per offer-channel per month, below most learning thresholds. Nothing in the system says so | `P1` — approving Scenario B without a concentration rule is the most likely way to make performance worse while spending more | State the arithmetic above in the strategy and require a phased rollout | Assert the strategy contains a per-offer minimum monthly spend and a phase order |
| 12.3 | Matt | Both scenarios | Comparable evidence | Approve / reject / defer | Decision recorded | **NOT IMPLEMENTED.** See flow 14. `observed` | None | — | N/A | Necessary | `P1` | — | — |
| 12.4 | Anyone | — | An unapproved proposal must never read as a baseline | — | Proposals clearly separated from configured state | **HANDLED CORRECTLY.** `evidence-matrix.md:23` grades the 150,000 Ft / 60-30-10 planning proposal as `PARTIAL proposal only` with "Do not treat it as actual spend, budget, campaign state, or client decision." `observed` | In the corpus only | Keep this discipline in T4A/T4B | — | Good | `P3` | — | Assert no strategy document cites 150,000 Ft as the baseline |

---

## Flow 13 — Client reads the report and understands results, limits, confidence and next steps

| # | A | B | C | D | E | F | G | H | I | J | K | L | M |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 13.1 | Márta / Fusion Tables; intent: know whether the money worked | A link | Report published | Open the Notion page or the deck | Report opens in Hungarian | **WORKS.** `RPT30/run_receipt.json` records `notion_url` (page `3ca9cef986ad81d3…`) and `slides_url` (`docs.google.com/presentation/d/1CoIOvW9ZIe9UBpP0TXkqSA0JuDSBjqom5-1kXUqNMps`) with `native_revision_id: qNgt9degDB3TDA`, `verified_at 2026-08-29T01:16:57`. `observed` (receipt-level; I did not open the live pages — no live reads authorized) | Notion page + Slides + `.pptx` + narration | Read results | If publication failed, the receipt would not be `complete: true` | Necessary, correctly ordered | `P3` | — | Open both URLs and confirm the revision matches the receipt |
| 13.2 | Márta | Report open | Channel results with a comparison | Read | Understands what happened | **WORKS.** `report.md:9-33` gives Google and Meta tables with current / previous / change and a plain-language paragraph under each. `observed` | Rendered tables | Read the limits | See flow 8.3 — the `+4800,0%` conversions row is the one misleading cell | Necessary, understandable, well ordered | `P1` (inherited from 8.3) | Footnote the degenerate-base change | — |
| 13.3 | Márta | Results read | Honest statement of what is not measurable | Read | Understands confidence and limits | **WORKS, and this is the system's best client-facing behavior.** `report.md:35-37` (no Search Console → organic omitted), `:39-43` (no GA4 → landing funnel omitted, plus a precise explanation of what GA4 engagement actually measures), `:85-89` (five explicit measurement limits), `:20` (the 49 conversions are platform signals, not verified enquiries or revenue), `:33` (Meta results are form starts), `:57` (enquiries cannot be attributed to a source). `observed` | Dedicated `Mérési korlátok` section | Read next steps | Absent sources are named as absent, never rendered as zero | Necessary, understandable, correctly placed **after** results and **before** next steps | — | Nothing. This section is the template the dashboard should inherit | Assert every `source_health` row with status `absent`/`disabled` has a matching sentence in the report |
| 13.4 | Márta | Limits read | A concrete plan with owners | Read | Knows what happens next and who does it | **WORKS.** `report.md:63-74` / `recommendations.json`: six rows `R-2026-07-01`..`R-2026-07-06`, each with priority, evidence, expected leverage, effort, named owner role and a measurement condition; closing line `:74` states none were executed. `observed` | Rendered table | Approve or discuss | — | Necessary. **One flaw**: the table's last column is a full copy-pasteable *agent instruction* in English, embedded in a Hungarian client report | `P2` — an English machine prompt in a client deliverable reads as internal scaffolding leaking into client-facing copy | Move the agent-instruction column to an internal appendix; keep the client-facing columns only | Diff the client render against the internal render and assert no English agent prompt appears in the client version |
| 13.5 | Márta; intent: see the dashboard the agency promised | Report read | A dashboard link | Click | Dashboard opens | **NOT IMPLEMENTED — no artifact exists.** `T5-A07` commissions a dashboard section with direct links; no dashboard exists to link to. `observed` | None | — | N/A | Necessary once T3 exists | `P1` | — | Assert the report contains a working dashboard URL |

---

## Flow 14 — Matt reviews, approves, rejects or defers the action tasks and the voice review

| # | A | B | C | D | E | F | G | H | I | J | K | L | M |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 14.1 | Matt; intent: triage what to do next | Owner-review page published | All accepted artifacts + task states | Open `https://review.cfd-staging.com/2026-08-30-fusion-ppc-owner-action-review/` | Page loads with per-task cards | **NOT IMPLEMENTED — no artifact exists.** Missing: `ARCH/children/t6-owner-action-review/artifacts/action-plans/2026-08-30-fusion-ppc-owner-action-review.html` and its publication (rows `T6-A07`, `T6-A08`). Child `run-state.md`: `- Status: WAITING_DEPENDENCY`, `- Dependency gate: T1, T2, T3, T4A, T4B, T5, and T7 must all be parent-accepted`, `- Started: NOT STARTED`. Resume: `ARCH/children/t6-owner-action-review/launch-prompt.md`. `observed` | None | — | N/A | Necessary — this is the surface on which `M-12` becomes actionable | `P1` | The six tasks already exist as content in `recommendations.json`. A minimal review page over those six is buildable **without** T2-T5/T7, because they do not depend on the missing children | Publish a six-card page and assert each card exposes approve / reject / defer |
| 14.2 | Matt | Page open | Task state per item | Approve / reject / defer | State persists and drives dispatch | **NOT IMPLEMENTED.** No decision state exists anywhere. Note `T6-A05` requires completed work to be marked DONE — with 7 of 8 children never run, an honest plan would mark almost everything REVIEW, which is the correct but unflattering output | None | — | **Duplicate risk:** `report.md:74` already says none of the six were executed. Without persisted state, a later run cannot tell "not yet done" from "deliberately rejected" | Necessary | `P1` — Matt's decisions have nowhere to live, so they are re-litigated | Persist decisions to a checked-in JSON beside the plan | Decide on one card, reload, assert the decision survives |
| 14.3 | Matt; intent: understand by listening | Page open | Voice narration grounded in the plan | Play | Voice review answers questions about the plan | **NOT IMPLEMENTED** for T6 (rows `T6-A09`..`T6-A13`, including the two live Sonnet/Grok review sessions). A **different** voice artifact does exist for the 30-day report: `RPT30/tts_receipt.json` and `narration.md`/`narration.txt` (8,907 bytes), hashed in `run_receipt.json`. `observed` | The report narration exists; the action-plan voice review does not | — | N/A | Necessary per the frozen goal. **Questionable at the 80/20 line** (`M-17`): T6 commissions two live browser review sessions with two different model brains plus fake-media Chromium QA, to voice six tasks | `P2` | Reuse the working `tts` path that already produced `narration.txt` rather than the two-brain session apparatus | Assert one playable narration is attached to the review page |
| 14.4 | Matt | Decisions made | — | Dispatch | Approved tasks become work | **NOT IMPLEMENTED** — and correctly out of scope: `ARCH/goal-v1.md:152` forbids T6 from dispatching. `intended` | None | — | N/A | Correct boundary | `P3` | — | — |

---

## Flow 15 — Monthly refresh, source failure, expired credentials, unavailable dimensions and partial data

| # | A | B | C | D | E | F | G | H | I | J | K | L | M |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 15.1 | Operator/scheduler; intent: produce next month's view without re-deriving anything | Last month done | A trigger | Scheduled or manual run | Refresh starts automatically | **NOT IMPLEMENTED as a trigger.** No cron, no scheduled task, no `refresh-dashboard.sh` anywhere. The 30-day report pipeline runs, but this repo line contains no entry point for it (`ORCH-CORR-1` — the whole package except `ledger.json` is missing from `round-13-takeover`). `observed` | None | — | **Recovery is entirely human**: someone must remember | Necessary | `P1` — a monthly system with no monthly trigger is an ad-hoc system | Add one scheduled invocation of the existing report generator | Assert a scheduled run produces a new `report_id` for the next window |
| 15.2 | Report generator | Run starts | Valid credentials | Automatic | Auth succeeds or fails loudly | **DEGRADES CORRECTLY — proven under real failure.** Meta OAuth failed `190/467`. `report_data.json` `source_health[meta_ads]` records `degraded` with the exact error and the recovery method: totals were recovered from an already-authenticated Ads Manager session and reconciled against 30 daily rows. `limitations[0]` states it. `evidence-matrix.md:22` preserves both the failed attempt and the later success. `observed` | Disclosed to the client at `report.md:33` and `:87` | Fix the token | **This is the best failure behavior in the system**: it failed, fell back, reconciled, disclosed, and preserved the failure receipt rather than overwriting it | Necessary. Exemplary | `P2` — the fallback is a manual browser export, so it will not survive an unattended run | Make expired-credential detection a pre-flight check that names the exact re-auth step | Revoke a token in a fixture and assert the run reports `degraded` rather than crashing or emitting zeros |
| 15.3 | Report generator | A source is entirely absent | — | Automatic | Absent ≠ zero | **WORKS.** `source_health[search_console].status = "absent"`, `source_health[ga4].status = "absent"`, `source_health[bigquery].status = "disabled"`, each with a `declared absent in the client config` caveat. Rendered as full sentences at `report.md:37`, `:41`, `:88`. Never rendered as 0. `observed` | Explicit Hungarian sentences | — | Correct | Necessary. **The single most important behavior in the whole system, and it works** | — | Reuse this vocabulary in the dashboard verbatim | Assert no absent source ever renders a numeral |
| 15.4 | Report generator | A source returns a real zero | — | Automatic | Zero ≠ missing | **WORKS.** Search delivery is a source-verified numeric zero in the KPC window (`GROUND-TRUTH.md:73`, `evidence-matrix.md:19-20`); `ppc.campaigns` shows the two Search and two Display campaigns at 0 clicks in both periods; four of five CRM worksheets read 0/0. `observed` | Present in the data | — | Correct | Necessary. Correctly distinguished from 15.3 | `P2` — the *distinction* is correct in the data but **not visually distinct anywhere**, because no dashboard exists to render it. `T3-07` commissions exactly this and was never built | Give zero, unavailable, partial and not-applicable four distinct visual treatments in the first dashboard build | Render a fixture with all four states and assert four distinct treatments |
| 15.5 | Operator | Partial data | — | Publish anyway or hold | Publish with honest partial states | **WORKS.** All 7 `source_health` rows are `degraded`/`absent`/`disabled` — **not one source is clean** — and the report published anyway with every limit disclosed. `observed` | `report.md:85-89` | — | Correct | Necessary. Publishing degraded-but-disclosed beats holding | `P3` | — | — |
| 15.6 | Orchestration layer | A child stalls | Liveness | Should surface the stall | Run state reflects reality | **DEFECT.** `ARCH/run-state.md` reads `- Status: ACTIVE` and `- Child launch state: … T2 is active from release 01a05297-…` while `ARCH/children/t2-measurement-audit/run-state.md` reads `- Status: WAITING_DEPENDENCY`, `- Started: PENDING`, and `tmux ls` shows no session (`GROUND-TRUTH.md:22-24`). Three sources of truth, two of them wrong. `observed` | Nothing surfaces the stop | — | **No stale detection, no liveness check, no timeout.** The run has been silently stopped since 2026-08-30T12:14Z with its own file asserting otherwise | Necessary. **Broken** | `P0` for the operating system — a stopped run that reports `ACTIVE` guarantees the failure is discovered late, by a human, by accident. That is exactly how this review came to exist | Make the parent's status derived from the children rather than asserted, and stamp it with a heartbeat time | Kill a child and assert the parent status flips to `STALLED` within one interval |

---

## Flow 16 — A new operator takes over with zero prior context

| # | A | B | C | D | E | F | G | H | I | J | K | L | M |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 16.1 | New operator; intent: find out what this system is | Repo access | A single entry document | Open the repo | One canonical index points to everything | **PARTIAL.** Four competing entry points exist: `REPO/handoffs/2026-08-30-fusion-ppc-system-audit.md` (672 lines), `ARCH/goal-v1.md` (177), `ARCH/children/t1-evidence-corpus/artifacts/corpus-index.md` (54), and `REPO/AGENTS.md`. `corpus-index.md` is the best evidence index but covers only T1's sources. `observed` | Several README-shaped files | Determine current state | — | Necessary. **Not optimally ordered** — the newcomer must read ~1,000 lines before learning that 7 of 8 outputs do not exist | `P2` | One `STATE.md` at the run root: what exists, what does not, what the resume point is | Give the file to a fresh reader and time how long until they can name the missing artifacts |
| 16.2 | New operator; intent: know what actually got built | Entry doc read | Truthful status | Read `run-state.md` | Status reflects reality | **MISLEADING.** `ARCH/run-state.md` says `ACTIVE` and describes T2 as active. A newcomer trusting it would attach to a session that does not exist. The truth requires cross-checking eight child run-states, eight branch HEADs and `tmux ls` — the exact procedure `GROUND-TRUTH.md:9-24` had to perform. `defect` | Confidently wrong status | Verify independently | **No recovery affordance**: nothing tells the newcomer the status is stale | Necessary. **Actively harmful as written** | `P0` for onboarding — the first authoritative file a newcomer reads is wrong about the single most important fact | Derive status from child state (see 15.6); until then, add one dated line: "verified stopped, N of 8 produced" | Ask a zero-context reader "is this run running?" and assert they answer correctly from one file |
| 16.3 | New operator; intent: resume the work | State known | An exact resume point per task | Read the child package | Each stopped task names its restart command | **WORKS.** Every child directory carries `launch-prompt.md`, `goal-v1.md` (checksum-frozen), `acceptance-ledger.md`, `access-ledger.md`, `run-state.md`, plus a named worktree and branch. `ARCH/dependency-handoff-ledger.md` states the release rule explicitly: "A visible child in `WAITING_DEPENDENCY` is not a release receipt." `observed` | Five files per child | Fix the fixed point, then relaunch T2 | Resume is from disk, never re-derived — correct | Necessary. Well designed. This is the apparatus's real strength | `P3` | — | Hand a newcomer `t3`'s package and assert they can state the gate and the command |
| 16.4 | New operator; intent: understand the evidence | Resume point known | Provenance for every number | Read the corpus | Every claim traces to a source, window and account | **WORKS, unusually well.** `ARCH/children/t1-evidence-corpus/artifacts/evidence-matrix.md` (23 claims, each with window, account, evidence type, state and caveat), `date-account-caveats.md` (windows, account identities, grains, 11 contradictions), `created-file-inventory.md` (153 entries with Git blob hashes and byte counts). `observed` | 716 lines of indexed Markdown | Use it | Missing bytes are recorded as `UNAVAILABLE` with the exact path retained (`created-file-inventory.md`, the 12 `handoff-0xx` rows) | Necessary, understandable, correctly ordered | — | Nothing. T1 is the one child that delivered and it delivered well | — |
| 16.5 | New operator; intent: open the evidence the corpus cites | Corpus read | The files must exist in the working tree | `cat` a cited path | File opens | **FAILS ON THE REVIEWED BRANCH.** `ORCH-CORR-1`: `ads-control/`, `deliverables/fusion-meta-ads-review-2026-08-24/` and the whole `2026-08-28-fusiontables-report/` package (except `ledger.json`) are absent from `round-13-takeover`. T1's citations survive only because they are **Git blob SHAs readable out of commits on the other line** — I recovered the full report package with `git show 830df60:outputs/loop-runs/2026-08-28-fusiontables-report/<file>` and verified `git cat-file -s 8add1558…` = 14,729 bytes, matching `created-file-inventory.md`. `observed` | A newcomer following a path gets "No such file" | Repair the fixed point | **Recovery exists but is non-obvious**: the bytes are in the object store, not the working tree | Necessary. **This is where a zero-context takeover actually breaks** | `P0` — the evidence base is one branch away and nothing says so. A newcomer would reasonably conclude the data was lost | Merge or cherry-pick the 41 missing files onto the working line, or document the `git show 830df60:` recovery route in the entry doc | Check out `round-13-takeover` clean and assert `report.md` opens from the working tree |

---

## Cross-flow summary

**Steps assessed:** 66 across 16 flows.

| Actual state | Steps |
| --- | ---: |
| Works as intended (including the two correct degrade-and-disclose paths) | 20 |
| Partial / configured-only / not verified | 9 |
| `NOT IMPLEMENTED — no artifact exists` | 24 |
| Defect (implemented and wrong, implemented and lossy, or aimed at the wrong grain) | 13 |

**P0 findings, ranked by decision risk:**

1. **2.2** Cache serves a prior visitor's UTM and `gclid` inside the live form action URLs (`AUDIT/evidence/tracking-audit.md:66-77`). Poisons attribution and would poison any future offline-conversion upload.
2. **3.2 / 2.4** One form submission can create several Primary Google Ads conversions, and a conversion action named `Purchase` fires on page load (`tracking-audit.md:37-51`; `report_data.json` `ppc.conversion_actions`). Mechanically explains platform 49 vs register 13.
3. **4.2** The CRM has no source or campaign column (`crm.source_column = null`). Channel spend is known, channel outcome is not — this single gap makes Matt's budget-allocation question unanswerable.
4. **5.3 / 5.4** Qualified lead, quote value, sale and revenue do not exist as fields anywhere; the frozen goal commissions optimization toward all four.
5. **7.4 / 7.5** The only creative/placement/demographic dashboard fetches from a gitignored path that exists on no branch, and its test passes on a string match — a green check over a dead page.
6. **10.2** The only decision engine is keyword-grain on an account whose Search delivery is a verified zero; all 61 rows return `wait_for_evidence`.
7. **15.6 / 16.2** The parent run reports `ACTIVE` while stopped; a zero-context operator is misled by the first authoritative file they read.
8. **16.5 / `ORCH-CORR-1`** The reviewed branch lacks the entire PPC evidence base the children were commissioned to work on.

**The cheapest path to a working version of Matt's core ask**, in dependency order, derived from the
flows above rather than from the frozen goal:

1. Repair the fixed point so the evidence base is in the working tree (16.5).
2. Render a Meta four-step funnel and a Google two-step funnel from `RPT30/report_data.json` as it
   already stands — impressions → clicks → link clicks → landing-page views → form starts, each with
   its existing `previous` value, y-axis labelled **events, not people** (flow 1.3, 8.2).
3. Repoint the Meta creative dashboard's `SOURCE_ROOT` to a committed snapshot (7.4), which unlocks
   creative, placement, age/gender and geography in one move (9.3).
4. Add `source`, click-ID and `landing_page` columns to the CRM sheet (4.2) — the prerequisite for
   every outcome metric and for offline conversions.
5. Only then retarget the advice engine from keyword grain to creative + targeting grain (10.2), which
   is what `M-11` actually asked for.

Steps 2 and 3 require no new data access, no new credentials and no live mutation.

## Limitations of this lane

- **No browser was opened.** Every interaction claim is read from source. Lane 2 owns live
  verification of `stage-3-ai-advice-review.html`, `fusion-meta-ads-review-2026-08-24/index.html`
  and the two loop-run `index.html` pages.
- **No form was submitted and no live account was read.** Flows 3.3, 13.1 and 15.2 are graded from
  receipts and from the July audit's own proof ladder (`tracking-audit.md:99-108`), not from a fresh
  transaction.
- The `2027.07.29` CRM date anomaly (`date-account-caveats.md:34`) is recorded as a probable
  data-entry error, not corrected, and not counted as a defect of the system.
- Notion pages `3ca9cef9-86ad-81d3-8fb1-de88e4a4ea98` and `3b59cef9-86ad-81be-8085-dc2a0a8fe32f`
  and the Slides deck were not opened; flow 13.1 is receipt-level evidence only.
- `ads-control/reports/stage-3-20260824T111713-2aa95a23.html` (the rendered KPC report) is
  `UNAVAILABLE` in git on every branch — only the template and the bundles exist.
