Expedify AI Content Engine — build tracker

Plan v1.0 (signed 2026-09-02) · generated 2026-09-03 20:31 · refreshes every 5 min · redeploys when sources change (≤15 min) · status.json

Current phasePhase B engine work complete pending human captures
Steps complete25 / 4654 % of the plan's checklist
Money gate — sourced0 / 187gate ≥ 95 % · 0 % toward target
Lead coverage94 %6 % have no lead yet
Convention disagreements00 self-contradicting pages
Unknown source hosts00 = tiering done

Needs your input

1 open decisions25 files in staged fixes110 Decisions needed700 Source money figures4 Publish to live551 live pages in the hotfix PR

What the engine is waiting on — each chip counts rows that need a human. Open: Astro content-collection schema — requested 2026-09-02; which `m

Phases

Phase A — Plan to v1.0 (no build)

Complete

7/9 steps

Steps
  • John answers v0.4 §8 open items 1–6 — DONE 2026-09-02: files-only; legal read = Expedify (we supply claim list); cap + short post-mortem; Expedify names reviewer; request Astro read access; prototype deleted
  • Fill v0.4 §9 numbers — DONE 2026-09-02 (cap 10/wk + 5 %; floor indexed+90d+200 impr; cohort 80/50/≤30; margin +15 % or −3 pos; review 8 min/25 pp/wk; Opus ≤20/wk; max-age 90/90/180/365; fail-closed fallbacks; 3 deferrals with triggers) — ADR 0005
  • Measurement source — direct GSC API via service account (Ahrefs = reference only); request added to developer requests §1b
  • Obtain Astro content-collection schema; record its field list in findings.md — moved to Phase C prerequisites (requested, pending Expedify)
  • Client PRD aligned to v1.0 (§6 post-mortem, §7 asks incl. GSC service account)
  • ADRs written in expedify docs/adr/: 0001 files-never-deploys, 0002 legal read = Expedify, 0003 cap + post-mortem, 0004 named reviewer (growth-cap numbers ADR waits on §9)
  • Developer requests drafted: docs/Expedify_Developer_Requests_2026-09-02.md (repo access, reviewer, legal sign-off, release safety, footprint, cap/cohort/conversion)
  • Send the requests to Expedify — John to forward docs/Expedify_Handover_Cover_Note_2026-09-03.md (decided 2026-09-03: Claude does not send client-facing mail; note is forward-ready)
  • John signs v1.0 → unfreeze (2026-09-02; file renamed to _v1.0.md)

Phase B — Truth layer (plan P1 → components C1)

In progress

started 2026-09-02 · 12/15 steps

Gate: ≥ 95 % of money figures carry a tier 1/2 source; remainder suppressed. Falsified otherwise → Phase D shrinks to sourced pages only.

Steps
  • Define facts: schema — DONE 2026-09-02 docs/content engine/facts.schema.json (tiers 0–3, statuses incl. not_stated, conditional source/second_source/sign_off requirements): {id, claim_type: fee|processing_time|eligibility|convention|validity, value, unit, source_url, authority_tier: 1 govt|2 embassy|3 aggregator, captured_at, max_age_days, verification_status, second_source_url?}; sections reference facts by {{fact:id}} instead of literal numbers
  • scripts/engine/extract_facts.py — DONE 2026-09-02, read-only, 745 pages → 7,521 candidates in data/facts/candidates/, report reports/facts-extraction-2026-09-02.md; 5 unit tests in tests/test_extract_facts.py. Money gate baseline: 3,316 fee facts = 1,658 first-party + 997 not_stated + 550 with a tier-1/2 candidate on page + 111 with none → 661 third-party figures need real sourcing
  • Independent challenge round 1 (12 findings: 3 blockers — empty convention DB, misleading gate arithmetic, tier heuristic; 7 majors) → INCORPORATED 2026-09-02: extractor v2 + schema v0.2 + data/facts/authority_registry.json (112 hosts) + 11 tests on real data. Gate baseline (v2.1): 187 distinct third-party money figures, 0 % captured, 92 % with a scoped lead, 8 % none. 25 convention disagreements (17 stale hubs + 8 self-contradicting driving guides)
  • Independent challenge round 2 → 4 majors + 4 minors (incl. 2 v2 regressions) → INCORPORATED (v2.1, 16 tests, every page schema-validated)
  • Round 3: all 8 confirmed fixed, verdict COMMIT; 3 follow-ups (money ranges, FAQ-question votes, 'in any N-day' windows, road-authority hosts) folded in → v2.2, 17 tests → COMMITTED + pushed 2026-09-02
  • Tier the unknown source hosts — DONE 2026-09-03: 127 hosts tiered (registry 112 → 224 hosts, ccTLD map 140), 0 unknown left; leads 92 % → 94 %, no-lead 11 figures; idaoffice.org registered as related property, never a source
  • Capture script scripts/engine/capture_facts.py — DONE 2026-09-03: fetches lead URLs (cached, polite, honest UA), matches value near a currency/unit marker, confidence = claim word nearby (weak matches never count). First full run: 21 hits / 546 fetches; money gate 0 → ~2 %; travel.state.gov + mofa.go.jp + immi.homeaffairs.gov.au block bots (403), VFS/moi.gov.ae are JS shells, most ministry leads are sections not fee pages
  • Human source queue scripts/engine/source_queue.pyreports/source-queue-2026-09-03.md (~180 figures grouped by lead host and failure reason: value_not_on_lead_page / blocked_403 / js_or_thin_page / no_lead / weak_match_confirm)
  • Source the candidates: convention facts from data/idp-countries-by-convention.json (audited 2026-08-30, tier 1 via UN treaty lists); fees/times from data/genuine_fees_*.json + VisaHQ matrix (tier 3 → needs tier 1/2 confirmation); everything else → human source queue card
  • scripts/engine/truth_gate.py — DONE 2026-09-03 (R1 provenance, R2 second source, R3 max-age, R4 disagreement; merges captures; per-page verdict PASS/HOLD/FAIL + corpus gate). First run: corpus gate NOT YET, 4/187 money figures sourced. Remaining design gap: the 'every rendered number has a fact id' check needs the facts block retrofit (step 7)
  • scripts/engine/staleness.py — DONE 2026-09-03: ages every dated claim vs §9 max-age, stages 1/2/3 (suppress figure = TODO until the {{fact:id}} retrofit / banner / noindex), stage 2-3 as staged whole-file copies under handoff/demotions-<date>/. Today: 0 stale; 271 fee facts fall due 2026-11, 762 in 2027-02, 1,527 in 2027-08 (the corpus's single last_verified date makes staleness arrive in waves — a reason to capture, not just re-date)
  • scripts/engine/source_watch.py — DONE 2026-09-03: watch list = 56 lead URLs that fetch cleanly (ranked by facts backed), sentence-level diff of numeric lines vs stored snapshot, aos-notify warning on change, report per run; weekly user timer expedify-source-watch.timer (Agentic-OS ops/systemd). Opens no W2 card yet (W2 not built)
  • Legal claim checklist (ADR 0002 deliverable) — DONE 2026-09-03: reports/legal-claim-checklist-2026-09-03.{md,csv}, 2,279 claims / 5 sections with provenance + blank sign-off columns. Expedify's reviewer signs; not yet sent
  • Retrofit run on the corpus; commit facts blocks in tranches (≤ 50 files per PR, playbook order: visa hubs → driving guides → country hubs → tuples)
  • Ledger: SYSTEM.md entry for the timer + scripts

Phase C — Contract + gates (plan P2 → C2, C3 single-brief, C4, C5, C9)

In progress

started 2026-09-03 · 6/10 steps

Gate: dry-run batch builds clean on the site; applied set == manifest.

Steps
  • docs/policies/corpus-admission.md + scripts/engine/admit_brief.py — DONE 2026-09-03 (T1–T7; first brief b-20260903-japan-driving-guide-refresh = HOLD awaiting approver). Original: docs/policies/corpus-admission.md (right-to-exist test) + scripts/engine/admit_brief.py (mechanical parts: sibling payload check, refresh-not-new lookup, query-overlap check vs reports/ SERP data, commercial-value flag from a per-page-type value table)
  • briefs/_template.yaml with facts_plan: — DONE 2026-09-03 (which fact ids the page will carry and their planned sources)
  • C5 exporter scripts/engine/export_site.py — DONE 2026-09-03 for the three routed page types: 191 driving guides + 191 visa hubs + 191 country hubs → site markdown; site parsers: 0 gaps except visa border/refusals (v2 has no such sections — playbook components borderReality/refusalReasons still to author). Driving-guide Astro build passed; full 573-page build running. TODO: idp_nationality (site has only 2 hand-built nationality pages — needs a site route), visa_tuple (site has no route)
  • Create path scripts/engine/create_page.py: brief → registry-composed draft via luna-content (Gemma) → validate_page.pytruth_gate.pydedup_gate.py (siblings + competitor corpus via pipelines/query_competitor_corpus.py) → citation_gate.py (only where the page type warrants) → kanban_request_review (opus-reviewer, evidence-ID contract, ≤ 3 rounds)
  • scripts/engine/approve_batch.py — DONE 2026-09-03 (sample/record, earned batch size, review-time alarm; smoke-tested incl. rejection). Original: Sampled approval scripts/engine/approve_batch.py: picks k files (start k = batch = 5), prints the 3 questions per file, records verdicts + time-on-page to reviews/approvals/<batch_id>.json; batch-size rule (double after 2 clean, halve on any rejection, any rejection rejects the batch); alarm when median review time < threshold
  • scripts/engine/handoff.py + manifest.schema.json v0 — DONE 2026-09-03 (idempotent, cap-recorded; pin to the real site schema later). Original: Handoff scripts/engine/handoff.py: copies approved files to handoff/<batch_id>/, writes manifest.json (file, sha256, action, approver, verdicts, redirect_from), idempotent re-run; pin manifest.schema.json
  • scripts/engine/cap.py — DONE 2026-09-03 (10 new/week, 5 % of 568 modified/week, audit log). Original: Growth-cap + audit log scripts/engine/cap.py: refuses a handoff that would exceed URLs/week or %-modified/week; appends every handoff to reviews/audit-log.jsonl
  • Site-side §6 items 1–6 landed by Expedify's developer (schema read access, build-diff == manifest, atomic release + rollback, kill switch, deploy checks vs indexation watcher, build close-out) — track as one blocked card per item
  • Dry run: 20-page batch through the whole path into handoff/, developer builds it on a preview → measures writer throughput (pages/day) for §9
  • Ledger entries for scripts, board conventions, handoff folder

Phase D — Validation cohort (plan P3 → C6)

Pending

0/5 steps

Gate: ≥ 80 % indexed @ 30 d; ≥ 50 % with ≥ 1 impression-bearing query @ 90 d; no manual action. Fail ⇒ STOP.

Steps
  • Pick 30–60 URLs (page types + hardest tuples) with John/client; record list in findings.md
  • Measurement scripts/engine/measure.py: pull GSC (source per Phase A decision), URL-inspection indexation, conversion events; write data/measure/; per-page cost ledger from Phase C logs
  • Per-page (not sampled) approval for every cohort file
  • Release in tranches over several weeks; robots lifted incrementally by the developer; indexation watcher reporting to aos-notify
  • Hold 8–12 weeks; weekly readout card

Phase E — Improve loop on the cohort (plan P4 → C7, W3, W5)

Pending

0/4 steps

Gate: refreshed beats control at 90 d by the §9 margin; else keep only FIX-FACT/TECHNICAL.

Steps
  • scripts/engine/triage.py: evidence floor check → deterministic typed decision (NO-ACTION default; FIX-FACT / REFRESH / CONSOLIDATE / PRUNE / REWORK-INTENT / TECHNICAL-ONLY) with a stated hypothesis and evidence IDs; cannibalization detection (same query, multiple URLs)
  • Randomized 30–40 % held-out control; weekly site-wide change cap enforced by cap.py
  • Decisions → W2/W5 cards; Opus only on ambiguous cases
  • Optionally rebuild the diagnostic half (SERP top-3 gap analysis) from the deleted prototype's design notes in memory — only after the evidence-ID contract is generalised

Phase F — Scale, research, citation (plan P5–P7 → parameterized C3, C8, W4, W6)

Pending

0/3 steps

Gate: only if D and E passed.

Steps
  • Parameterize create_page.py into build_family.py ("visa|IDP|passport family for [country set]") under cap.py + commercial gate
  • scripts/engine/research.py: source-watch events + SERP-shift detection as primary triggers → candidate briefs → admit_brief.py; keyword sweep secondary, low-frequency; idempotency rule vs in-flight W2 cards
  • Citation experiment: metric + baseline on the cohort, A/B on a subset, rich-result eligibility re-verified first

Waiting on decisions / inputs

  • Delivery model → files-only, confirmed 2026-09-02 (ADR 0001).
  • Measurement source → direct GSC API on Expedify's property; service account requested.
  • Named reviewer → Expedify names a real person (ADR 0004); name itself pending from Expedify.
  • ?Astro content-collection schema — requested 2026-09-02; which meta keys does the site read (incl. last_verified)? PENDING EXPEDIFY
  • Legal read owner → Expedify, we supply the claim checklist (ADR 0002).
  • Disclosure → cap presented with a short non-identifying post-mortem (ADR 0003).
  • §9 numbers → all agreed 2026-09-02 (plan §9 table). Deferred with triggers: writer throughput (dry run), cohort pick (client), manifest schema (Astro schema).
  • v1.0 sign-off → signed 2026-09-02; execution unfrozen.

Staged fixes (not applied to live)

  • fix-fact-2026-09-03 — 19 files staged, awaiting John (e.g. driving-document/for-swiss/index.yaml, driving-document/for-egyptians/index.yaml, driving-document/for-argentines/index.yaml, driving-document/for-spaniards/index.yaml)
  • fix-fact-2026-09-03b — 6 files staged, awaiting John (e.g. united-arab-emirates/index.yaml, bahrain/index.yaml, united-arab-emirates/driving-guide/index.yaml, driving-document/for-emiratis/index.yaml)

Work log

2026-09-03Content-optimization flow + batch 1 (2026-09-03)complete (staged, committed 06d06405) — the flow John asked for: template + research + SERP competitor analysis + verified facts -> an edited version of the page, ready for ChatGPT + human review.
    2026-09-03HANDOVER NOTE (2026-09-03, Fable near its weekly limit — John switching to Opus 5)
      2026-09-03v2 driving-guide artefact clean-up (2026-09-03)complete — 23 guides cleaned (14 dash brand tokens, 69 stray bullet dashes / duplicate self-drive bullets, advice-as-route entries moved to notes, Japan toll sentence recovered from the v1 mirror); extractor 0 disagreements; 573 exports re-run, site parsers 0 gaps. Committed + pushed.
        2026-09-03v2 enrichment: visa border + refusal sections (2026-09-03)complete — 191 visa hubs gained borderReality + refusalReasons (generic scaffolding, staged then applied); registry updated; exporter emits them; site parsers 0 gaps on all 573 exports; extractor 0 disagreements. Commit pushed.
          2026-09-03C5 exporter — visa hubs + country hubs (2026-09-03)complete — 573 pages export, parsers clean (except visa border/refusals = v2 gap), full Astro build exit 0 (195/197/206 pages), clone cleaned. Commit ff7b7b74.
            2026-09-03C5 exporter — driving guides (2026-09-03)complete for driving guides — 191 exported, parser 0 gaps, Astro build exit 0 (195 pages), Japan verified; scratch branch deleted; commit e5aca3eb
              2026-09-03Tracker: URL inventory & agent-view tab (2026-09-03)complete — second tab on the tracker: 869 URLs (live = what the Astro routes serve: 571; v2: 745; both 544; live-only 27; v2-only 201; 97 mirror files with no route), per-URL gate/sourced/fixes/agent/needs/complete, filters. Commit 8f2845e9; deployed.
                2026-09-03Option C — site hotfix + DB correction (2026-09-03)complete (branch pushed, PR via compare link; v2 UAE/Bahrain re-staged + applied, commit a24b976b)
                  2026-09-03Site repo found + two-corpus finding (2026-09-03)complete (finding; decision with John)
                    2026-09-03Phase C (schema-independent pieces) — 2026-09-03in_progress
                      2026-09-03Staged batches applied to live (2026-09-03)complete — John: "do what you recommend". 19 nationality pages + 10 dead-link pages copied from handoff/ over content/; YAML valid; extractor 0 disagreements; committed + pushed. Corrected IDP routes file HELD (checker must render non_party first).
                        2026-09-03Legal checklist + generator v2.1 (2026-09-03)complete
                          2026-09-03Dead source links (2026-09-03)complete (staged, committed)
                            2026-09-03Phase B step 5–6 — staleness + source watch (2026-09-03)complete
                              2026-09-03Phase B step 3b–4 — host tiering, capture, source queue, truth gate (2026-09-03)complete
                              • Tiered all 127 unknown source hosts into the registry (224 hosts, 140 ccTLD mappings; idaoffice.org = related property, never a source). Leads 92 % → 94 %; 11 figures without a lead.
                              • Wrote scripts/engine/capture_facts.py (fetch lead URL, visible text, value-near-marker match, cached, polite). Smoke test 0/12: 403 (immi.homeaffairs.gov.au), JS shells (punetejashtme.gov.al), and a matcher miss on mfa.am. Full run (546 fetches, 120 hosts): 21 raw hits; added confidence scoring (claim word near the value; weak matches never count) → 17 strong captures, 4 of them money figures → gate 0 → 2.1 %. 403 bot-blocks: travel.state.gov (80 attempts), mofa.go.jp, immi.homeaffairs.gov.au; JS shells: visa.vfsglobal.com, moi.gov.ae; 26 of our own source links return 404 (content defect).
                              • scripts/engine/source_queue.pyreports/source-queue-2026-09-03.md: 642 figures (fee+processing+stay) by lead host and reason (value_not_on_lead_page 229, blocked_403 123, no_lead 113, fetch_failed 94, js_or_thin_page 53, lead_url_404 26, weak_match_confirm 4).
                              • scripts/engine/truth_gate.py (R1 provenance, R2 second source, R3 max-age, R4 disagreement): 623 PASS (mostly pages with no third-party money), 122 FAIL on R1; corpus gate NOT YET (4/187).
                              • Committed + pushed.
                              2026-09-03Progress tracker (2026-09-03)complete
                                2026-09-03Phase B step 3a2 — nationality pages + IDP routes (2026-09-03)complete (staged, not applied to live; committed + pushed 2026-09-03 after 2 review rounds)
                                • Found root cause: generator reads data/idp-routes.json, never updated after the 08-30 audit (19 lcc / 20 dest discrepancies; routes = 12 licence × 23 dest subset, 55 routes with a changed destination).
                                • John's rule: fixes to NEW files. Built data/idp-routes.corrected-2026-09-03.json (script clones existing route templates per status pair; authored copy for the new non-party-destination case). Wrapper scripts/gen_idp_nationality_staged.py stages pages from the corrected file without touching the generator or content/.
                                • Staged 19 pages; 19/19 verdicts match the corrected status. Review round 1: 8 findings (3 substantive in the routes recomputation) → all but the generator-internal ones fixed; rebuilt + restaged; round 2 found the Nepal→Indonesia name clobber in sub_names → placeholder fix + Indonesia diagonal + next_review/sources-note polish; rebuilt, restaged, verified (0 clobbers, 19/19 parse) → committed.
                                2026-09-03Phase B step 3a — FIX-FACT convention statementscomplete (committed + pushed 2026-09-03; round-2 diff review clean)
                                • Dumped all 25 flagged pages' convention sentences via x_votes; found Ireland's verdict ("either type") that the voter treated as ambiguous.
                                • Wrote scripts/fixes/fix_convention_statements_2026-09-03.py: exact-match/regex replacements with per-file expected counts; dry-run ABORTED on first pass (hubs had 4 occurrences not 3; Indonesia guide 3 not 2) → located the extra occurrences (families[] block, requirements row note) → counts corrected → applied to 24 files with backups.
                                • Re-ran extractor (reports/facts-extraction-2026-09-03.md): disagreements 0, self-contradictions 0, all YAML valid, 17 tests OK. Residual-wording grep: clean.
                                • Diff review round 1: 1 blocker (Indonesia rows), 2 majors (leftover FAQ sentences; review metadata), minors (voice, script anchors). Restored the 24 backups, extended the script (Indonesia rows via non-party template, 7 FAQ edits, last_reviewed + next_review, anchored+asserted date bumps, three-sentence neither wording), re-applied. Extractor: 0/0 again; 17 tests OK; YAML valid. Round 2 dispatched.
                                2026-09-02Phase B: Truth layerin_progress
                                • Discovery: mapped where facts live (verdict prose, roadRules table, pricingBox, visaTable, requirements rows); recorded in findings.md.
                                • Wrote docs/content engine/facts.schema.json (draft 2020-12; validated with jsonschema).
                                • Wrote scripts/engine/extract_facts.py (read-only) + tests/test_extract_facts.py (5 tests, pass).
                                • Run 1: 745 pages, 7,498 candidates; found 203 gov fees mis-tagged first_party (row text "+ Expedify fee") and 23 hedged guides with no convention fact. Fixed both (first-party = quote box / expedify_fee prop / amount adjacent to "Expedify"; hedged guide → not_stated fact). Run 2: 7,521 candidates, 1 residual mis-tag (canada/visa/for-indians CAD 185).
                                • Disagreements vs data files: 0 (content was generated from the same DBs — expected; the DBs themselves are the thing to source).
                                • Independent adversarial review (subagent, 24 tool calls): 12 findings, 3 blockers. All blockers + majors incorporated: extractor v2, schema v0.2, authority registry (112 hosts), 11 tests incl. real-data loaders + schema validation of real output. Run 3: 6,280 facts; gate 0.0 % captured / 76 % scoped lead / 24 % none; 23 real convention disagreements (13 stale country hubs + India bug etc.). Round-2 re-check dispatched.
                                • Review round 2: blockers 1/2/3/5 confirmed fixed, ids partially; 4 majors (value-bearing ids + 10 schema failures, voter tie-breaks masking inconsistent pages, fx/$0 inflating the denominator, generic-gov + dubai/schengen country scoping) + 4 minors. ALL incorporated (v2.1): value-free ids claim_type.<subject-key>, per-path convention votes with inconsistent pages reported as disagreement, fx inside any "(~ …)" group incl. "/"-separated, $0 by row subject, rolling windows → qualifier, generic gov ^gov.<cc>$ + .gv.at, country aliases (dubai→UAE, schengen→None), own-country advice → tier 1, no invented captured_at, dead code removed, unknown hosts fully listed. Tests 16 (every page validated with FormatChecker; no duplicate ids). Run 4: 187 distinct third-party money figures, 0 % captured, 92 % scoped lead, 8 % none; 25 disagreements of which 8 are self-contradicting driving guides.
                                • Review round 3: all 8 confirmed fixed; verdict commit. Folded in its 3 follow-ups + registry road hosts (lto.gov.ph, nhtsa.gov, …) + europa.eu → v2.2, 17 tests. Final run: 187 distinct third-party figures / 0 % / 92 % lead / 8 % none; 25 disagreements, 7 self-contradicting guides.
                                • Committed + pushed (expedify main) with data/facts/candidates/ gitignored as regenerable output.
                                2026-09-02Phase A: Plan to v1.0complete
                                • Audited project state; found PRD (v0.2-derived, publishes) vs v0.3 (files, no publish) conflict and v0.3 internal contradictions.
                                • John: pause all execution, solidify the plan first. Verified nothing running (no procs/timers/cron; no running/ready/review cards).
                                • Archived t_e87b5d09 (japan improve) + t_6b4a559a, t_872c6ed1, t_7f8140a6, t_54478ab4, t_714daa4d on luna-expedify; commented on parent t_d3e2c562.
                                • Wrote docs/AI_Content_Engine_Project_Plan_v0.4.md (components C1–C9, workflows W1–W6, site-side contract, decisions ledger, §9 blanks). Committed 78764724, pushed to expedify-hk/content-workspace main.
                                • Deleted parked prototype pipelines/page-content-audit/ on John's instruction.
                                • Revised client PRD to v0.4 (handoff not publish; 0–7 phases; open decisions). Unversioned (outbound/ gitignored).
                                • Created these planning files (task_plan.md, findings.md, progress.md) per planning-with-files; added them to the expedify repo .gitignore.
                                • docs/AI_Content_Engine_Project_Plan_v0.4.md (new, committed)
                                • ~/Agentic-OS/outbound/Expedify/Expedify_AI_Content_Engine_PRD.md (revised)
                                • task_plan.md, findings.md, progress.md (new, ignored)
                                • .gitignore (+3 planning files)
                                • docs/adr/0001..0004-*.md, docs/Expedify_Developer_Requests_2026-09-02.md (new)
                                • docs/AI_Content_Engine_Project_Plan_v0.4.md, docs/Expedify_AI_Content_Engine_PRD.md (decisions recorded)
                                • Decision session with John: v0.4 §8 items 1–5 resolved (see task_plan Decisions). Plan §8 + PRD §6/§7 updated; ADRs 0001–0004 written to docs/adr/; developer requests drafted.
                                • §9 session with John: all 13 lines + measurement source agreed; plan §9 rewritten as AGREED table; ADR 0005 (growth cap); dev requests §1b (GSC service account); PRD §7 item 7. Plan marked v1.0 CANDIDATE pending John's explicit sign-off.
                                • John signed v1.0 (file renamed, status line updated, ADR/PRD references bumped). Phase A complete; execution unfrozen; Phase B not started.

                                Recent commits (expedify repo)

                                ec9812022026-09-03tracker: alert tiles, needs-input panel, next-step lede, sortable inventory (v1.2.0)
                                4534ed602026-09-03docs: run optimization writers on Sonnet, not the session default
                                f0d9861b2026-09-03content: batch 2 of the content-optimization flow — 10 more priority visa hubs (staged)
                                22b49f6f2026-09-03content: batch 1 final — china rewritten by its writer after first commit, australia research report added
                                fe023d3b2026-09-03content(ad-lander): enrich reviewStrip to 12 simulated reviews
                                849c28482026-09-03docs: write up the content-optimization flow as the standing process
                                06d064052026-09-03content: batch 1 of the content-optimization flow — 10 priority visa hubs optimized (staged)
                                05da39092026-09-03content(ad-lander): wire author/reviewer to data/reviewer-personas.yaml
                                81d3b4052026-09-03content(ad-lander): visa checker logic + Indian-passport POC
                                6768d3322026-09-03content(v2): clean generator artefacts in 23 driving guides (dash brand tokens, stray bullet dashes, advice-as-route entries, clipped toll sentence); re-export
                                2a027a3d2026-09-03content: add travel-visa-assistance ad-lander (config + outline)
                                5912c6bc2026-09-03content(v2)+engine: borderReality + refusalReasons on all 191 visa hubs; exporter emits them; site parsers now 0 gaps on all 573 pages