Primary finding
Ad sets created under pre-existing campaigns are never appended to .admanage-state/campaigns.json
- scripts/postprocess-admanage-create.sh:148-153
- skills/create-campaign/SKILL.md:84-92
SKILL.md explicitly says: when the parent campaign already exists in state, the skill writes the real campaignId directly and may drop the parentCampaignConfigName field. In postprocess phase 2, when campaign_id_in_payload is a real ID (not the sentinel, not empty), the script skips the resolve block entirely. parent_name is whatever .parentCampaignConfigName // empty returned, which per the skill contract is allowed to be empty. The state-append jq filter then uses `select(.configName == $parent)` — with $parent=='' this matches no campaign and the new adSet is NOT written into the state file even though it succeeded on the API. This permanently desynchronizes .admanage-state/campaigns.json from reality. Since SKILL.md says this is the canonical source for schedule-ads to reference IDs, this is a real data-tracking/data-loss bug.
Recommendation
Match on campaignId rather than configName for state append in phase 2, e.g. `select(.campaignId == $cid)`. Also keep parent_name populated from a reverse lookup so logs/results files are accurate.