Swarm runner: standalone run_swarm() lacks stack env scaffolding; YIELDOMEGA_SWARM_REFERRALS=0 verification deferred
Summary
Verification of the local bot swarm path with referrals disabled (YIELDOMEGA_SWARM_REFERRALS=0) was not completed in-session and is tracked here as deferred work.
When attempting to invoke swarm_runner.run_swarm() directly (outside scripts/start-local-anvil-stack.sh), contributors hit swarm runner configuration failures attributable to missing environment scaffolding that the stack wrapper normally establishes implicitly. Until that scaffolding is repeatable for standalone runs and/or surfaced on the canonical wrapper path, the “no swarm referrals” behavior remains undertested in practice.
Background
Intended behavior (YIELDOMEGA_SWARM_REFERRALS)
The TimeCurve bot swarm optionally bootstraps a shared referral code for deterministic local QA aligned with referrer flows and indexer ingestion (GitLab #94).
Implementation:
swarm_referrals_enabled()inbots/timecurve/src/timecurve_bot/referral_bootstrap.pyreturns false whenYIELDOMEGA_SWARM_REFERRALSis set to0(otherwise defaults to enabled).- When enabled,
swarm_runner.pyfunds the registrar HD index (27), runsensure_swarm_referral_registered(...), and setsYIELDOMEGA_REFERRAL_CODEin worker subprocess env so buys usebuy(..., codeHash, ...). - When disabled (
YIELDOMEGA_SWARM_REFERRALS=0), the swarm should skip registrar funding for referrals, skip registration bootstrap, and omitYIELDOMEGA_REFERRAL_CODEfrom workers so buys exercise the non-referral path.
This is documented at a high level in bots/timecurve/README.md and cross-linked from docs/testing/invariants-and-business-logic.md.
Canonical wrapper path
When START_BOT_SWARM=1, start-local-anvil-stack.sh (tail of script):
- Runs
scripts/sync-bot-env-from-frontend.sh, which materializesbots/timecurve/.env.localfromfrontend/.env.local(RPC, chain id, TimeCurve / treasury / NFT addresses,acceptedAssetviacast, etc.). - Selects the venv Python when present, runs dependency preflight.
- From repo root (
cd "${ROOT}"), exportsYIELDOMEGA_ALLOW_ANVIL_FUNDING=1, setsPYTHONPATH, and executes:from timecurve_bot.swarm_runner import run_swarm; run_swarm()
run_swarm() calls load_config(env_file=None, send=True, allow_anvil_funding=False), which loads .env and, when the process cwd is the repo root, bots/timecurve/.env.local (bots/timecurve/src/timecurve_bot/config.py — load_dotenv_files).
Thus the wrapper does two critical things beside setting ALLOW_ANVIL_FUNDING:
- Ensures bot-facing contract/RPC variables exist by syncing frontend → bot
.env.local. - Runs with cwd at repo root so relative dotenv discovery matches repo conventions.
Problem statement (what went wrong)
A contributor attempted to manually reproduce swarm behavior under YIELDOMEGA_SWARM_REFERRALS=0 by calling run_swarm() outside start-local-anvil-stack.sh.
That standalone invocation did not inherit the same effective environment as the wrapper:
sync-bot-env-from-frontend.shmay not have been run (no freshbots/timecurve/.env.local), producingload_config/ValueErrorfailures such as unsetYIELDOMEGA_RPC_URL/RPC_URLor unsetYIELDOMEGA_TIMECURVE_ADDRESS.YIELDOMEGA_ALLOW_ANVIL_FUNDING=1may be unset, causingSystemExit(2)with the swarm’s documented guardrail.- Working directory may not be the repository root;
load_dotenvresolution forbots/timecurve/.env.localis relative to cwd, not the package layout. - Even with a correct shell, forgetting
PYTHONPATHtobots/timecurve/srcbreaks imports when not usingpython -mfrom an installed/env layout.
Net effect: the failure mode observed was a runner configuration / env orchestration gap, not necessarily a defect in swarm_referrals_enabled() logic itself. Because of that gap, YIELDOMEGA_SWARM_REFERRALS=0 was not empirically exercised during the intended session (DEFERRED).
Impact
| Area | Impact |
|---|---|
| QA confidence | “No swarm referrals” path is weaker than “default referrals on” until someone runs swarm with =0 end-to-end. |
| Operator friction | Only the documented wrapper composes prerequisites; ad-hoc Python entrypoints surprise contributors. |
| Regression risk | Future changes to swarm_runner, referral_bootstrap, or worker buy wiring could regress the opt-out path without CI catching it. |
Recommended fixes
Deliver one or more of the following (in order of leverage vs. scope):
A. Wrapper-visible opt-out (minimal, explicit)
Extend START_BOT_SWARM=1 UX on start-local-anvil-stack.sh to forward (or document and default-copy) YIELDOMEGA_SWARM_REFERRALS.
- Example: If an operator exports
YIELDOMEGA_SWARM_REFERRALS=0before starting the stack, the same subprocess that callsrun_swarm()inherits it—no Python code change strictly required, but today the script neither documents nor echoes this knob in the “Bot swarm” banner. - Enhancement: Print a one-line summary when starting the swarm: e.g.
YIELDOMEGA_SWARM_REFERRALS=0 (referral bootstrap disabled)vs default.
This matches the contributor note: “follow up by setting the env on the wrapper script itself in a separate boot”—interpreted as making the wrapper path the place where this flag is first-class for local runs.
B. Document the standalone contract (docs / README)
Add a short “Run swarm without start-local-anvil-stack.sh” checklist to bots/timecurve/README.md (and optionally docs/testing/e2e-anvil.md):
- Deploy / have
frontend/.env.local(or equivalent addresses). bash scripts/sync-bot-env-from-frontend.sh- From repo root:
export YIELDOMEGA_ALLOW_ANVIL_FUNDING=1 - Optional:
export YIELDOMEGA_SWARM_REFERRALS=0 PYTHONPATH=bots/timecurve/src python3 -c "from timecurve_bot.swarm_runner import run_swarm; run_swarm()"
ortimecurve-bot --allow-anvil-funding swarmwith the same env.
C. Friendlier failure messages (optional code)
In run_swarm(), when load_config fails on missing RPC/TimeCurve, catch ValueError once and append a hint pointing to sync-bot-env-from-frontend.sh and repo-root cwd (keep messages short; avoid noisy stack traces for expected misconfig).
D. Automated smoke (stretch)
A small scripted or pytest smoke that mocks/stubs RPC is likely overkill; prefer a documented one-liner plus optional CI job only if we can spin Anvil cheaply. Not required for closing this issue if manual acceptance below is satisfied.
Acceptance criteria
-
Documented path
There is repo-checked documentation (README and/or testing doc) that lists all prerequisites to runrun_swarm()withoutstart-local-anvil-stack.sh, includingsync-bot-env-from-frontend.sh,YIELDOMEGA_ALLOW_ANVIL_FUNDING=1, repo-root cwd, andYIELDOMEGA_SWARM_REFERRALS=0when testing the opt-out. -
Wrapper discoverability
WhenSTART_BOT_SWARM=1, the stack script mentionsYIELDOMEGA_SWARM_REFERRALSin user-facing output (help text, comment block, or banner) so operators know they can set0without reading Python. -
Verified behavior (manual)
On a fresh local Anvil stack (or equivalent):- With
YIELDOMEGA_SWARM_REFERRALS=0, swarm starts successfully via the documented path (wrapper or standalone checklist). - Logs / behavior show no successful
registerCodebootstrap for the swarm shared code (or N/A when registry zero), and worker env does not setYIELDOMEGA_REFERRAL_CODE(or workers buy without referral code—however the bot surfaces that). - With default (referrals on), prior behavior remains unchanged (regression check).
- With
-
Cross-links
Updatedocs/testing/invariants-and-business-logic.mdLocal stack bot swarm row or referrals subsection to reference this issue for theYIELDOMEGA_SWARM_REFERRALS=0verification note (one sentence).
Related references
- Referrals / indexer: GitLab #94
- Bot deps / PEP 668: GitLab #50
- Swarm + Anvil time: GitLab #99
Labels / area
area:bots · type:tech-debt or type:documentation · priority::low-to-medium (suggest; adjust per project convention)