# Campaigns the why that rides along **🔵 Expert · Lesson 15** CAMPAIGNS - the WHY, carried without anyone remembering to carry it. Expert 14 built a subsystem and left one argument dangling: `research_group_history(group_id, campaign=...)`. This is what goes in that slot, and why the slot is worth having. A campaign is a stamp that crosses lanes. Lanes are WHERE work lives; a campaign is WHICH EFFORT it belonged to, and one effort routinely touches several lanes, several subsystems, and several agents. Nothing else in the record can express that, because everything else is organized by place. THE SURFACE, AND IT SPLITS THE SAME WAY SET (codegen rooms only - these WRITE) research_set_campaign(campaign) research_clear_campaign() READ (capability rooms get this too) research_campaign_view(campaign) THE ONE THING TO ACTUALLY LEARN HERE campaign=None DOES NOT MEAN "no campaign". IT MEANS INHERIT. Pass nothing and the record takes the AMBIENT stamp. To record something with no campaign at all you must CLEAR the ambient one first. That reads like a trap until you see what it buys: campaign membership never depends on an agent remembering to pass an argument on every call, across a session that might be thousands of them. Attribution defaults to true rather than to blank. An EXPLICIT campaign still wins over the ambient one, so the default is a default and not a cage. AND `None` MEANS THE OPPOSITE THING ON THE READ SIDE register_group(campaign=None) -> INHERIT the ambient stamp group_history_view(campaign=None) -> UNFILTERED, every effort Same word, same type, same argument name, opposite meaning - and melder's own docstring calls the difference out rather than leaving you to find it. It is right both times: a write that names no effort belongs to the one in progress, while a read that names no effort wants everything. Attribution defaults to SPECIFIC; queries default to BROAD. Guessing one rule for both is how you end up reading a filtered history and thinking it was the whole story. AND IT RIDES EXACTLY FOUR SEAMS, NOT EVERYTHING The ambient stamp is applied by the ROOT FACADE: `record_world_entry`, `record_promotion`, `register_group`, `recompose_group` - every dynamic bind, staged bind, notch, and composition write. The room's ORGANIZATIONAL verbs (`research_create_lane`, `research_attach`, `research_archive`) delegate straight to the research set and are UNSTAMPED. Moving furniture is not part of an effort's story unless you say so. WRITE/READ AGREEMENT, AND IT IS A LAW (BUG-047) An empty campaign is refused on the WRITE side for one stated reason: `campaign_view` refuses it on the READ side. A public write may never create a record the public query API cannot reach. That is the never-substitute law wearing a different hat - melder would rather refuse your write than let you produce a fact nobody can ever ask about. WHAT THE VIEW ANSWERS {campaign, nodes, transitions, lane_names} TRANSITIONS AND NODES ARE NOT THE SAME SET, deliberately. Every stamped journal entry is a transition; only four acts contribute a NODE (registered, staged, group_registered, group_recomposed). A campaign of pure organizational moves yields transitions and an empty node list - that is correct, not a gap. And the sequence is the JOURNAL's order, not a lane walk. Lane iteration would tie-break same-millisecond ULIDs on their random component, so the story would reorder itself between runs. A history that is not reproducible is not a history. ## Before you run Use the [Expert guide](../../expert/index.md) for prerequisite concepts. Run from a checkout with Melder installed and Python 3.14 free-threading selected. The collection download includes the level's local helper modules. ## Run the saved script ```bash python UX_and_AIX_experiences/04_expert/15_campaigns_the_why_that_rides_along.py ``` ```powershell py -3.14t UX_and_AIX_experiences/04_expert/15_campaigns_the_why_that_rides_along.py ``` {download}`Download this collection <../../downloads/expert-examples.zip>` · [Source on GitHub](https://github.com/Synaptic724/melder/blob/0895ce795453cd87e35d70665957b4e89aad3317/UX_and_AIX_experiences/04_expert/15_campaigns_the_why_that_rides_along.py) ## Public surface research_set_campaign / research_clear_campaign / research_campaign_view, ambient inheritance through research_group_register and research_group_recompose, and research_group_history(campaign=...) ## Code ```{literalinclude} ../../downloads/04_expert/15_campaigns_the_why_that_rides_along.py :language: python :linenos: ``` ## Check the outcome The script contains its own assertions or demonstrated refusal paths. Run it to evaluate those checks against your installed version. The code above is taken directly from the saved file; no run output is invented here. [More expert examples](index.md) · [Level guide](../../expert/index.md) ## Related guides - [Research sets, lanes, residency, and recorded source](../../expert/research.md)