On this page

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 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๏ƒ

python UX_and_AIX_experiences/04_expert/15_campaigns_the_why_that_rides_along.py
py -3.14t UX_and_AIX_experiences/04_expert/15_campaigns_the_why_that_rides_along.py

Download this collection ยท Source on GitHub

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๏ƒ

  1"""
  2TIER: expert (15)
  3GOAL: CAMPAIGNS - the WHY, carried without anyone remembering to carry
  4      it. Expert 14 built a subsystem and left one argument dangling:
  5      `research_group_history(group_id, campaign=...)`. This is what goes
  6      in that slot, and why the slot is worth having.
  7
  8      A campaign is a stamp that crosses lanes. Lanes are WHERE work
  9      lives; a campaign is WHICH EFFORT it belonged to, and one effort
 10      routinely touches several lanes, several subsystems, and several
 11      agents. Nothing else in the record can express that, because
 12      everything else is organized by place.
 13
 14      THE SURFACE, AND IT SPLITS THE SAME WAY
 15
 16        SET (codegen rooms only - these WRITE)
 17          research_set_campaign(campaign)
 18          research_clear_campaign()
 19
 20        READ (capability rooms get this too)
 21          research_campaign_view(campaign)
 22
 23      THE ONE THING TO ACTUALLY LEARN HERE
 24
 25        campaign=None DOES NOT MEAN "no campaign". IT MEANS INHERIT.
 26
 27      Pass nothing and the record takes the AMBIENT stamp. To record
 28      something with no campaign at all you must CLEAR the ambient one
 29      first. That reads like a trap until you see what it buys: campaign
 30      membership never depends on an agent remembering to pass an
 31      argument on every call, across a session that might be thousands of
 32      them. Attribution defaults to true rather than to blank.
 33
 34      An EXPLICIT campaign still wins over the ambient one, so the
 35      default is a default and not a cage.
 36
 37      AND `None` MEANS THE OPPOSITE THING ON THE READ SIDE
 38        register_group(campaign=None)      -> INHERIT the ambient stamp
 39        group_history_view(campaign=None)  -> UNFILTERED, every effort
 40      Same word, same type, same argument name, opposite meaning - and
 41      melder's own docstring calls the difference out rather than leaving
 42      you to find it. It is right both times: a write that names no
 43      effort belongs to the one in progress, while a read that names no
 44      effort wants everything. Attribution defaults to SPECIFIC; queries
 45      default to BROAD. Guessing one rule for both is how you end up
 46      reading a filtered history and thinking it was the whole story.
 47
 48      AND IT RIDES EXACTLY FOUR SEAMS, NOT EVERYTHING
 49      The ambient stamp is applied by the ROOT FACADE:
 50      `record_world_entry`, `record_promotion`, `register_group`,
 51      `recompose_group` - every dynamic bind, staged bind, notch, and
 52      composition write. The room's ORGANIZATIONAL verbs
 53      (`research_create_lane`, `research_attach`, `research_archive`)
 54      delegate straight to the research set and are UNSTAMPED. Moving
 55      furniture is not part of an effort's story unless you say so.
 56
 57      WRITE/READ AGREEMENT, AND IT IS A LAW (BUG-047)
 58      An empty campaign is refused on the WRITE side for one stated
 59      reason: `campaign_view` refuses it on the READ side. A public write
 60      may never create a record the public query API cannot reach. That
 61      is the never-substitute law wearing a different hat - melder would
 62      rather refuse your write than let you produce a fact nobody can
 63      ever ask about.
 64
 65      WHAT THE VIEW ANSWERS
 66        {campaign, nodes, transitions, lane_names}
 67      TRANSITIONS AND NODES ARE NOT THE SAME SET, deliberately. Every
 68      stamped journal entry is a transition; only four acts contribute a
 69      NODE (registered, staged, group_registered, group_recomposed). A
 70      campaign of pure organizational moves yields transitions and an
 71      empty node list - that is correct, not a gap.
 72
 73      And the sequence is the JOURNAL's order, not a lane walk. Lane
 74      iteration would tie-break same-millisecond ULIDs on their random
 75      component, so the story would reorder itself between runs. A
 76      history that is not reproducible is not a history.
 77SURFACE EXERCISED: research_set_campaign / research_clear_campaign /
 78                   research_campaign_view, ambient inheritance through
 79                   research_group_register and research_group_recompose,
 80                   and research_group_history(campaign=...)
 81VERIFY: RUN GREEN 2026-08-03 on the owner's 3.14t harness.
 82"""
 83import melder as md
 84
 85
 86def _room(nexus, kind, name):
 87    """Open one rift of the given kind and hand back its command surface."""
 88    configuration = nexus.create_rift_configuration()
 89    configuration.with_space_type(kind)
 90    rift = nexus.create_rift(configuration=configuration, rift_name=name)
 91    rift.mark_active()
 92    return rift.space.command_system
 93
 94
 95def main() -> None:
 96    nexus = md.Nexus()
 97    system_configuration = nexus.create_configuration()
 98    system_configuration.with_rift_creation_enabled(True)
 99    nexus.activate(system_configuration)
100
101    research = md.MutationResearch()
102    research_configuration = research.create_configuration()
103    research_configuration.with_defaults().activate()
104    research.activate(research_configuration)
105
106    codegen = _room(nexus, "codegen", "worker")
107    capability = _room(nexus, "capability", "auditor")
108
109    # THE SPLIT AGAIN. Setting the WHY is a write; reading it is not.
110    assert hasattr(codegen, "research_set_campaign")
111    assert hasattr(codegen, "research_clear_campaign")
112    assert not hasattr(capability, "research_set_campaign")
113    assert not hasattr(capability, "research_clear_campaign")
114    assert hasattr(codegen, "research_campaign_view")
115    assert hasattr(capability, "research_campaign_view")
116    print("set/clear: codegen only   view: both rooms")
117
118    # Some declared versions to compose over.
119    research_set = research.research_set()
120    alpha, beta, gamma = "a" * 64, "b" * 64, "c" * 64
121    for spell_id in (alpha, beta, gamma):
122        research_set.register_spell(spell_id)
123    research_set.create_lane("payments", lane_type="production")
124
125    # 1. NAME THE EFFORT ONCE.
126    codegen.research_set_campaign("q3-payments-split")
127    assert research.active_campaign == "q3-payments-split"
128    print()
129    print("ambient campaign set ->", research.active_campaign)
130
131    # 2. WRITE WITHOUT MENTIONING IT. This is the whole point: no
132    #    `campaign=` argument anywhere, and the record is attributed.
133    first = codegen.research_group_register(
134        [alpha, beta], lane="payments", reason="the payments core",
135    )
136    assert first["campaign"] == "q3-payments-split", (
137        "campaign=None means INHERIT, not none"
138    )
139    print("registered a composition with NO campaign argument ->",
140          first["campaign"])
141
142    second = codegen.research_group_recompose(
143        first["group_id"], add=[gamma], reason="settlement joined",
144    )
145    assert second["campaign"] == "q3-payments-split"
146    print("recompose inherited the same stamp - so the whole arc is one")
147    print("effort, without one call naming it")
148
149    # 3. ORGANIZATIONAL MOVES DO NOT RIDE ALONG. The room's lane verbs go
150    #    straight to the research set, not through the stamping facade.
151    before = len(codegen.research_campaign_view("q3-payments-split")
152                 ["transitions"])
153    codegen.research_create_lane("scratch", lane_type="experiment")
154    after = len(codegen.research_campaign_view("q3-payments-split")
155                ["transitions"])
156    assert after == before, (
157        "an organizational move must not be attributed to an effort "
158        "nobody said it belonged to"
159    )
160    print()
161    print("created a lane under a live campaign; transitions unchanged:",
162          before, "->", after)
163    print("   the stamp rides declarations, not furniture moves")
164
165    # 4. TO RECORD WITH NO CAMPAIGN, CLEAR THE AMBIENT ONE. There is no
166    #    "campaign=None means none" escape hatch, because None is taken.
167    codegen.research_clear_campaign()
168    assert research.active_campaign is None
169    delta = "d" * 64
170    research_set.register_spell(delta)
171    unattributed = codegen.research_group_register(
172        [alpha, delta], lane="payments", reason="side quest",
173    )
174    assert unattributed["campaign"] is None
175    print()
176    print("cleared the ambient stamp -> the next write is unattributed")
177    print("   clearing is the ONLY way to opt out, which is why the")
178    print("   default direction is 'attributed'")
179
180    # 5. AN EXPLICIT CAMPAIGN BEATS THE AMBIENT ONE. A default, not a cage.
181    codegen.research_set_campaign("q3-payments-split")
182    epsilon = "e" * 64
183    research_set.register_spell(epsilon)
184    override = codegen.research_group_register(
185        [beta, epsilon],
186        lane="payments",
187        reason="borrowed for a different effort",
188    )
189    # The room verb takes no campaign argument, so the override is made at
190    # the root facade - the same seam that applies the ambient default.
191    explicit = research.register_group(
192        [gamma, epsilon],
193        lane="payments",
194        campaign="hotfix-2026-08",
195        reason="explicitly someone else's effort",
196    )
197    assert override["campaign"] == "q3-payments-split"
198    assert explicit.campaign == "hotfix-2026-08"
199    print()
200    print("ambient ->", override["campaign"])
201    print("explicit ->", explicit.campaign, " (wins)")
202
203    # 6. THE WRITE/READ AGREEMENT. An empty stamp is refused on BOTH sides
204    #    for one reason: the read side cannot address it.
205    for attempt in (
206            lambda: codegen.research_set_campaign(""),
207            lambda: codegen.research_campaign_view(""),
208    ):
209        try:
210            attempt()
211            raise AssertionError("expected ValueError on an empty campaign")
212        except ValueError:
213            pass
214    print()
215    print("empty campaign refused by the WRITE and by the READ -")
216    print("   a public write may never create a record the public query")
217    print("   API cannot reach")
218
219    # 7. THE VIEW. Transitions are every stamped event; nodes are only the
220    #    four acts that declare something.
221    view = codegen.research_campaign_view("q3-payments-split")
222    for key in ("campaign", "nodes", "transitions", "lane_names"):
223        assert key in view, key
224    assert len(view["nodes"]) <= len(view["transitions"]), (
225        "only registered/staged/group_registered/group_recomposed "
226        "contribute a node - transitions is the wider set"
227    )
228    print()
229    print("campaign view:", len(view["nodes"]), "nodes /",
230          len(view["transitions"]), "transitions across lanes",
231          sorted(view["lane_names"]))
232    print("   sequenced by JOURNAL order, so two runs tell the same story")
233
234    # 8. AND THE JOIN EXPERT 14 LEFT OPEN: one subsystem, one effort.
235    story = capability.research_group_history(
236        second["group_id"], campaign="q3-payments-split",
237    )
238    print()
239    print("group_history(subsystem, campaign) ->", type(story).__name__)
240    print("   WHERE by WHEN - 'how did this subsystem change during that")
241    print("   effort' is a question you can only ask if both were named")
242
243    # ...and here `campaign=None` means UNFILTERED, not "inherit the
244    # ambient one" - the exact inverse of what it meant on every write
245    # above. The ambient stamp is still set right now, so if this read
246    # inherited it the two answers could not differ in principle.
247    unfiltered = capability.research_group_history(second["group_id"])
248    assert research.active_campaign is not None
249    print("group_history(subsystem)          -> unfiltered, not inherited")
250    print("   same argument, same type, opposite default - writes narrow")
251    print("   to the effort in progress, reads widen to all of them")
252    assert isinstance(unfiltered, type(story))
253
254    print()
255    print("a lane says where work lives; a campaign says what it was for")
256    print("None means INHERIT, so attribution defaults to true")
257    print("and nothing is written that nobody could later ask about")
258
259
260if __name__ == "__main__":
261    main()

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 ยท Level guide