diff --git a/ai/prompt-io/claude/20260610T171226Z_64181219_prompt_io.md b/ai/prompt-io/claude/20260610T171226Z_64181219_prompt_io.md new file mode 100644 index 00000000..55787d86 --- /dev/null +++ b/ai/prompt-io/claude/20260610T171226Z_64181219_prompt_io.md @@ -0,0 +1,47 @@ +--- +model: claude-fable-5[1m] +service: claude +session: 32d15f9a-b2d3-4c26-bdc9-190219141a25 +timestamp: 2026-06-10T17:12:26Z +git_ref: datad_service +diff_cmd: git log -1 -p --follow -- ai/prompt-io/claude/20260610T171226Z_64181219_prompt_io.md +scope: code +substantive: true +raw_file: 20260610T171226Z_64181219_prompt_io.raw.md +--- + +## Prompt + +Same session-initiating `brokerd`-split instruction (see +`20260610T170859Z_75cefe10_prompt_io.md`); this is the +approved plan's "stage 2": decouple the clearing layer +from `feed.portals` BEFORE the feed cutover so live +trading works at every stage boundary. User-decided +constraint applied: post-split `brokerd` is +trading-only and spawned LAZILY only by `emsd`'s +`open_brokerd_dialog()` path. + +## Response summary + +Kill the single coupling forcing feed + trading eps +into one actor (`Router.open_trade_relays()` pulling +its trades portal from `feed.portals[brokermod]`): +`open_brokerd_dialog()` now (maybe) spawns/finds +`brokerd.` itself and ONLY when a live +trades-ep will actually open; paper mode never touches +it. Pre-cutover this is a pure refactor (registry +lookup finds the same feed-spawned daemon). + +## Files changed + +- `piker/clearing/_ems.py` — `open_brokerd_dialog()` + re-sig + inner `acquire_live_portal()`; + `Router.maybe_open_brokerd_dialog()` drops `portal` + param; `open_trade_relays()` drops the + `feed.portals` lookup +- `piker/accounting/cli.py` — keyword-form `portal=` + override kept for the `piker ledger` ad-hoc actor + +## Human edits + +None — committed as generated. diff --git a/ai/prompt-io/claude/20260610T171226Z_64181219_prompt_io.raw.md b/ai/prompt-io/claude/20260610T171226Z_64181219_prompt_io.raw.md new file mode 100644 index 00000000..58c31c37 --- /dev/null +++ b/ai/prompt-io/claude/20260610T171226Z_64181219_prompt_io.raw.md @@ -0,0 +1,43 @@ +--- +model: claude-fable-5[1m] +service: claude +timestamp: 2026-06-10T17:12:26Z +git_ref: datad_service +diff_cmd: git log -1 -p --follow -- ai/prompt-io/claude/20260610T171226Z_64181219_prompt_io.md +--- + +NOTE: diff-ref mode entry (code committed in the same +commit as this log); backfilled from the live dev +session transcript per the `/prompt-io` skill rules. + +> `git log -1 -p --follow -- piker/clearing/_ems.py` + +Generated: new `open_brokerd_dialog()` signature +`(brokermod, exec_mode, fqme=None, portal=None, +loglevel=None)` with an inner `@acm +acquire_live_portal()` that yields the caller-provided +`portal` override (the `piker ledger` path) else +`maybe_spawn_brokerd(brokermod.name)` — designated THE +one place a live, credentialed `brokerd.` gets +booted post-split. The eager +`portal.open_context(trades_endpoint, ...)` +construction moved inside that block so the paper +short-circuit never acquires a live portal. + +> `git log -1 -p --follow -- piker/accounting/cli.py` + +Key analysis (verbatim from session): + +- `feed.portals` had exactly ONE trading consumer: + `piker/clearing/_ems.py:671` (`portal = + feed.portals[brokermod]`) — the single coupling + forcing feed + trading into one actor. +- `piker ledger` (`accounting/cli.py:100-157`) is a + hidden consumer: it calls `broker_init()` directly, + spawns its own ad-hoc actor and passes the portal + positionally — the new signature must keep an + explicit `portal:` override param for this path. +- stage sequencing: clearing decouple lands BEFORE the + feed cutover, otherwise `feed.portals` would hand + emsd a `datad` portal and live `open_trade_dialog` + RPC would fail. diff --git a/piker/accounting/cli.py b/piker/accounting/cli.py index 10306898..229a9d14 100644 --- a/piker/accounting/cli.py +++ b/piker/accounting/cli.py @@ -143,12 +143,15 @@ def sync( # (what the EMS normally does internall) B) open_brokerd_dialog( brokermod, - portal, exec_mode=( 'paper' if account == 'paper' else 'live' ), + # use our own ad-hoc-spawned actor, + # do NOT (spawn and) use the + # `brokerd.` service daemon! + portal=portal, loglevel=loglevel, ) as ( brokerd_stream, diff --git a/piker/clearing/_ems.py b/piker/clearing/_ems.py index c77ffd1b..1d152926 100644 --- a/piker/clearing/_ems.py +++ b/piker/clearing/_ems.py @@ -335,9 +335,14 @@ class TradesRelay(Struct): @acm async def open_brokerd_dialog( brokermod: ModuleType, - portal: tractor.Portal, exec_mode: str, fqme: str|None = None, + + # XXX: explicit (already spawned) trading-actor override, + # currently only used by the `piker ledger` cli which + # boots its own ad-hoc `brokerd`-like actor; normally we + # (lazily) spawn/find the `brokerd.` daemon here. + portal: tractor.Portal|None = None, loglevel: str|None = None, ) -> tuple[ @@ -351,6 +356,10 @@ async def open_brokerd_dialog( paper engine instance depending on live trading support for the broker backend, configuration, or client code usage. + NOTE: this is now the ONE place where a (live, credentialed) + `brokerd.` daemon-actor gets (lazily) booted; pure + data/paper sessions should never spawn one! + ''' get_console_log( level=loglevel, @@ -416,16 +425,29 @@ async def open_brokerd_dialog( ) exec_mode: str = 'paper' - if ( - trades_endpoint is not None - or - exec_mode != 'paper' - ): - # open live brokerd trades endpoint - open_trades_endpoint = portal.open_context( - trades_endpoint, - loglevel=loglevel, + @acm + async def acquire_live_portal(): + ''' + Deliver a portal to the (live, credentialed) trading + actor hosting the backend's `open_trade_dialog()` ep: + either the caller-provided override or the (maybe + lazily spawned) `brokerd.` service daemon. + + ''' + if portal is not None: + yield portal + return + + # XXX: the ONE (normal) place a `brokerd.` + # daemon-actor gets booted in the runtime B) + from piker.brokers._daemon import ( + maybe_spawn_brokerd, ) + async with maybe_spawn_brokerd( + brokermod.name, + loglevel=loglevel, + ) as live_portal: + yield live_portal @acm async def maybe_open_paper_ep(): @@ -437,7 +459,14 @@ async def open_brokerd_dialog( return # open trades-dialog endpoint with backend broker - async with open_trades_endpoint as msg: + async with ( + acquire_live_portal() as live_portal, + + live_portal.open_context( + trades_endpoint, + loglevel=loglevel, + ) as msg, + ): ctx, first = msg # runtime indication that the backend can't support live @@ -581,7 +610,6 @@ class Router(Struct): async def maybe_open_brokerd_dialog( self, brokermod: ModuleType, - portal: tractor.Portal, exec_mode: str, fqme: str, loglevel: str, @@ -606,7 +634,6 @@ class Router(Struct): async with open_brokerd_dialog( brokermod=brokermod, - portal=portal, exec_mode=exec_mode, fqme=fqme, loglevel=loglevel, @@ -668,7 +695,6 @@ class Router(Struct): brokername, _, _, _ = unpack_fqme(fqme) brokermod = feed.mods[brokername] broker = brokermod.name - portal = feed.portals[brokermod] # XXX: this should be initial price quote from target provider flume = feed.flumes[fqme] @@ -682,7 +708,6 @@ class Router(Struct): async with self.maybe_open_brokerd_dialog( brokermod=brokermod, - portal=portal, exec_mode=exec_mode, fqme=fqme, loglevel=loglevel,