Repository navigation
fix(ads): search_pages_by_name now discovers the full page set, not just one - #158
pipeboard-code-writer[bot] wants to merge 2 commits into
Conversation
…ust one _search_pages_by_name_core called _discover_pages_for_account, a helper built for ad-creation auto-select that returns the FIRST single page it finds (tracking_specs first hit, else client_pages[0], else assigned_pages[0]). The search tool wrapped that one page in a 1-element list, so total_available was always 0 or 1 and name filtering ran against a universe of one — accounts with many pages (e.g. via Business Manager) looked empty or unsearchable, and get_account_pages disagreed with it. Fix: factor the broad 8-approach page-ID discovery out of get_account_pages into shared helpers (_discover_all_page_ids_for_account + _fetch_page_details_for_ids) and use them from BOTH tools, so search_pages_by_name filters the same page universe get_account_pages lists. total_available now reflects the real discovered set, and pages whose details the token cannot read still surface as error entries. Co-Authored-By: Claude <noreply@anthropic.com>
VEREDITO: APPROVE WITH NITS — a causa raiz está correta e o refactor preserva o output de ACHADOS:
PERGUNTAS:
RESPOSTA SUGERIDA: The root-cause analysis is right and sharing the discovery path with |
|
🤖 The Pipeboard code-writer picked up this review and is working on it. I'll push to this branch and reply here with what changed. A human still approves — I never merge on my own review. |
…ail fetches
Address PR review findings on the search_pages_by_name fix:
- Per-item "source" is back: discovery now tracks which approach(es)
surfaced each page and search results carry them (comma-separated), so
callers can tell account-linked pages apart from pages seen only via
me/accounts (the user's own page list, not necessarily usable with the
ad account). The tool description documents this and the fact that
items may lack "name" (inaccessible pages come back as
{"id", "error"} entries).
- Approaches 5+7 (both hit {account_id}/ads with different fields) are
merged into a single request carrying both field sets — one less round
trip per call, same extraction.
- Per-page detail fetches now run with asyncio.gather bounded by a
semaphore (10 concurrent) instead of strictly sequentially, preserving
input order. A Business Manager with dozens of pages no longer costs
N sequential round trips per search.
- Output is deterministically sorted (named pages alphabetically, then
error entries by ID) instead of depending on set iteration order.
- Tests no longer mock a "source" value the real code never produced;
new tests pin the merged single ads request, source accumulation
across approaches, and output ordering.
Co-Authored-By: Claude <noreply@anthropic.com>
|
Thanks — all five findings addressed in the latest push ( 1. Output contract ( 2. 3. Cost per call — both suggested fixes are in:
4. Non-deterministic order — output is now sorted deterministically in both tools: pages with names alphabetically (case-insensitive), then error entries by ID. The discovery helper itself returns an insertion-ordered dict, so nothing downstream depends on set iteration anymore. 5. Tests anchoring a nonexistent Your questions:
Full suite: 546 passed, 9 skipped; package build + import green; E2E re-run against the live path with the new fields visible in output. |
Revisão do push 1. Mudança de contrato no output de 2. Escopo ampliado via 3. Custo por chamada — RESOLVIDO 4. Ordem não determinística — RESOLVIDO 5. Mocks de teste ancorando Novo ponto introduzido por este push:
Perguntas:
|
|
🤖 The Pipeboard code-writer picked up this review and is working on it. I'll push to this branch and reply here with what changed. A human still approves — I never merge on my own review. |
|
Closing as superseded — the diagnosis here was right, but the fix landed on the wrong side.
The fix shipped instead as pipeboard-co/pipeboard.co#2651 (squash Measured before/after on the production path, same account and token: Worth noting for the standalone package: |
Problem
search_pages_by_namedisagreed withget_account_pagesfor the same account: it reportedtotal_availableof 0 or 1 even whenget_account_pageslisted many pages, and name searches against it came back empty. Reported via MCP feedback (Pro user whose Business Manager has 6 pages;search_pages_by_namereturnedtotal_available: 1and could not find any of them by name, including after a fresh reconnect — reconnecting cannot help, the bug is in which endpoints the search path queries).Root cause
_search_pages_by_name_corecalled_discover_pages_for_account(), a helper built for ad-creation auto-select: it tries tracking_specs (first page id only), then client_pages, then assigned_pages, and returns as soon as one source succeeds. The search tool wrapped that single page in a 1-element list and filtered bysearch_termlocally — hencetotal_availablewas always 0 or 1, never the account's real page set.Meanwhile
get_account_pagesaggregates page IDs from 8 approaches (me/accounts, owned_pages, client_pages, adcreatives object_story_spec, ads creative spec, promoted_objects, ALL ads tracking_specs, campaign promoted_object) and fetches details for every unique page found.Fix
Factored the broad discovery out of
get_account_pagesinto two shared helpers:_discover_all_page_ids_for_account()— the exhaustive page-ID discovery, returning each page ID with the discovery source(s) that surfaced it_fetch_page_details_for_ids()— per-page detail fetch (bounded-concurrent), preserving the{"id": ..., "error": "Page details not accessible"}entries for pages the token cannot readBoth
get_account_pages(output shape unchanged) and_search_pages_by_name_corenow use them, so the search tool filters the same page universeget_account_pageslists._discover_pages_for_account(single-page auto-select) is untouched and still used bycreate_ad/create_ad_creativeauto-select.Output contract of
search_pages_by_name(post-review)datacarries the page details plus asourcefield listing which discovery approach(es) surfaced it, comma-separated (e.g."me/accounts,adcreatives,ads_tracking_specs"). Pages discovered only viame/accountscome from the connected user's own page list and may not be usable with this ad account (creating ads as them can fail with a Meta page-permission error) — thesourcemarker lets callers tell them apart, and the tool description says so.name: pages the token cannot read appear as{"id": ..., "error": "Page details not accessible"}(same convention asget_account_pages). That is a Meta-side Page permission denial, not an indexing problem — documented in the tool description.total_availablecounts the full discovered set (including inaccessible pages), matchingget_account_pages'stotal_pages_foundfor the same account.Cost per call (post-review)
{account_id}/adswith different fields are merged into one request carrying both field sets (7 discovery requests total, same as before forget_account_pages).asyncio.gatherbounded by a 10-concurrency semaphore instead of strictly sequentially — a Business Manager with dozens of pages no longer costs N sequential round trips per search.5 bounded rounds (+3.5s) instead of50 sequential round trips (+35s, timeout territory).Tests
uv run python -m pytest: 546 passed, 9 skipped — includes new regression tests assertingtotal_availablereflects the full discovered set, the merged single{account_id}/adsrequest, source accumulation across approaches, deterministic ordering, and that inaccessible pages stay visible as error entries (with theirsource)python -m buildsucceeds; import check passesE2E (pipeboard CLI → Next.js route.ts → this Python backend, E2E token)
Before (old code, stashed):
After:
Sample item after the fix:
The last search case is the reporter's exact scenario: previously
total_available: 1(meaningless); now the caller sees the true size of the page universe with zero name matches.Out of scope (documented in the task)
A page enumerable via the ad account but unreadable by the connected token (Meta-side role denial) still surfaces as an error entry — that needs a Page role grant in Business Suite, not a code change.