From 4485f2b9ce3e9dae812bd576122c1cc669b7bea8 Mon Sep 17 00:00:00 2001 From: goodboy Date: Wed, 10 Jun 2026 13:09:55 -0400 Subject: [PATCH] Fix `pytest` config-dir isolation in subactors The old (commented-out) `get_app_dir()` override gated on `'pytest' in sys.modules` which can NEVER work in spawned subactors (fresh procs, no pytest import); as a result test `paperboi`/daemon actors were writing into the user's REAL `~/.config/piker/accounting/` files.. friggin yikes. Deats, - add `config._maybe_use_test_dir()` which lazily (at conf-path access time, NOT import time) reads the `piker_test_dir` entry from `tractor.runtime._state._runtime_vars['piker_vars']` as pre-loaded by `open_piker_runtime()` from the `tests.conftest._open_test_pikerd()` overrides. - hook it in `get_conf_dir()` and route `get_conf_path()` + `load()`'s mkdir through `get_conf_dir()`. - route `.accounting._ledger` / `._pos` dir derivation through `config.get_conf_dir()` (was reading the `_config_dir` global directly, bypassing the override); also `mkdir(parents=True, exist_ok=True)` for nested tmp-dir creation. (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code Co-Authored-By: Claude Fable 5 Prompt-IO: ai/prompt-io/claude/20260610T170859Z_75cefe10_prompt_io.md --- .../20260610T170859Z_75cefe10_prompt_io.md | 59 +++++++++++++++++++ ...20260610T170859Z_75cefe10_prompt_io.raw.md | 46 +++++++++++++++ ai/prompt-io/claude/README.md | 34 +++++++++++ piker/accounting/_ledger.py | 7 ++- piker/accounting/_pos.py | 11 +++- piker/config.py | 50 +++++++++++++++- 6 files changed, 200 insertions(+), 7 deletions(-) create mode 100644 ai/prompt-io/claude/20260610T170859Z_75cefe10_prompt_io.md create mode 100644 ai/prompt-io/claude/20260610T170859Z_75cefe10_prompt_io.raw.md create mode 100644 ai/prompt-io/claude/README.md diff --git a/ai/prompt-io/claude/20260610T170859Z_75cefe10_prompt_io.md b/ai/prompt-io/claude/20260610T170859Z_75cefe10_prompt_io.md new file mode 100644 index 00000000..c847398d --- /dev/null +++ b/ai/prompt-io/claude/20260610T170859Z_75cefe10_prompt_io.md @@ -0,0 +1,59 @@ +--- +model: claude-fable-5[1m] +service: claude +session: 32d15f9a-b2d3-4c26-bdc9-190219141a25 +timestamp: 2026-06-10T17:08:59Z +git_ref: datad_service +diff_cmd: git log -1 -p --follow -- ai/prompt-io/claude/20260610T170859Z_75cefe10_prompt_io.md +scope: code +substantive: true +raw_file: 20260610T170859Z_75cefe10_prompt_io.raw.md +--- + +## Prompt + +Session-initiating instruction (driving all 7 commits in +this series): + +> ok i want you to become the distributed runtime and +> concurrency expert for this project - namely acquire +> a deep understanding of tractor and how it's used. +> then i want you to attempt to factor our current +> brokerd service daemon into 2 daemons: a brokerd +> which only hosts live/paper trading endpoint tasks +> [..] a new `datad` subdaemon which in a separate +> subactor serves all the data feed service tasks [..] +> give me a mega detailed plan on how to approach this, +> and a staged approach for the implementation. + +Proximate driver for THIS commit: during the approved +plan's stage-0 test gate the agent discovered test +subactors were writing into the user's REAL +`~/.config/piker/accounting/` files (a bogus paper fill +landed in `account.kraken.paper.toml`) because the old +test-dir override in `config.get_app_dir()` was +commented out and could never work in spawned +subactors anyway. Fix applied autonomously under the +approved plan; no explicit per-fix user prompt. + +## Response summary + +Restore `pytest` config-dir isolation by resolving the +per-test tmp dir lazily (at conf-path access time) from +`tractor` runtime-vars, which propagate down the actor +tree; route all conf-path derivation through +`config.get_conf_dir()` so the override is effective in +every (sub)actor. + +## Files changed + +- `piker/config.py` — add `_maybe_use_test_dir()`; + hook in `get_conf_dir()`; route `get_conf_path()` + + `load()` mkdir through it +- `piker/accounting/_ledger.py` — derive ledger dir + via `config.get_conf_dir()` (not the global) +- `piker/accounting/_pos.py` — same for account files + +## Human edits + +None — committed as generated. diff --git a/ai/prompt-io/claude/20260610T170859Z_75cefe10_prompt_io.raw.md b/ai/prompt-io/claude/20260610T170859Z_75cefe10_prompt_io.raw.md new file mode 100644 index 00000000..93026edf --- /dev/null +++ b/ai/prompt-io/claude/20260610T170859Z_75cefe10_prompt_io.raw.md @@ -0,0 +1,46 @@ +--- +model: claude-fable-5[1m] +service: claude +timestamp: 2026-06-10T17:08:59Z +git_ref: datad_service +diff_cmd: git log -1 -p --follow -- ai/prompt-io/claude/20260610T170859Z_75cefe10_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/config.py` + +Generated: `config._maybe_use_test_dir()` — lazily +reads `piker_test_dir` from +`tractor.runtime._state._runtime_vars['piker_vars']` +(pre-loaded by `open_piker_runtime()` from the +`tests.conftest._open_test_pikerd()` overrides) and +calls `_override_config_dir()` when set. Hooked at the +top of `get_conf_dir()`; `get_conf_path()` and +`load()`'s dir-creation rerouted through +`get_conf_dir()`. + +> `git log -1 -p --follow -- piker/accounting/_ledger.py` +> `git log -1 -p --follow -- piker/accounting/_pos.py` + +Generated: ledger/account dir derivation switched from +the `config._config_dir` module global to +`config.get_conf_dir()` + `mkdir(parents=True, +exist_ok=True)` for nested tmp-dir creation. + +Key diagnostic reasoning (verbatim from session): + +The old (commented-out) `get_app_dir()` override gated +on `'pytest' in sys.modules` which can NEVER work in +spawned subactors (fresh procs, no pytest import); as +a result test `paperboi`/daemon actors were writing +into the user's REAL `~/.config/piker/accounting/` +files. Evidence: `account.kraken.paper.toml` gained a +single 0.001 xbtusdt clear stamped 2026-06-09T18:47Z +(a test fill), and `trades_kraken_paper.toml` (252B) +contained only that fill. The resolution must be +checked lazily at config-path access time (NOT import +time) since sub-actors only receive runtime-vars once +their `tractor` runtime has fully booted. diff --git a/ai/prompt-io/claude/README.md b/ai/prompt-io/claude/README.md new file mode 100644 index 00000000..c92b18e9 --- /dev/null +++ b/ai/prompt-io/claude/README.md @@ -0,0 +1,34 @@ +# AI Prompt I/O Log — claude + +This directory tracks prompt inputs and model +outputs for AI-assisted development using +`claude` (claude-code CLI). + +## Policy + +Prompt logging follows the +[NLNet generative AI policy][nlnet-ai]. +All substantive AI contributions are logged +with: +- Model name and version +- Timestamps +- The prompts that produced the output +- Unedited model output (`.raw.md` files) + +[nlnet-ai]: https://nlnet.nl/foundation/policies/generativeAI/ + +## Usage + +Entries are created by the `/prompt-io` skill +or automatically via `/commit-msg` integration. + +Each commit carrying AI-generated changes links +to its provenance entry via a `Prompt-IO:` +commit-msg trailer; entries use "diff-ref mode" +(pointers into `git log -p` instead of verbatim +code copies) to avoid duplicating committed +code. + +Human contributors remain accountable for all +code decisions. AI-generated content is never +presented as human-authored work. diff --git a/piker/accounting/_ledger.py b/piker/accounting/_ledger.py index d2e56bf0..62e16028 100644 --- a/piker/accounting/_ledger.py +++ b/piker/accounting/_ledger.py @@ -324,10 +324,13 @@ def load_ledger( ldir: Path = ( dirpath or - config._config_dir / 'accounting' / 'ledgers' + config.get_conf_dir() / 'accounting' / 'ledgers' ) if not ldir.is_dir(): - ldir.mkdir() + ldir.mkdir( + parents=True, + exist_ok=True, + ) fname = f'trades_{brokername}_{acctid}.toml' fpath: Path = ldir / fname diff --git a/piker/accounting/_pos.py b/piker/accounting/_pos.py index e4577b47..d47c0369 100644 --- a/piker/accounting/_pos.py +++ b/piker/accounting/_pos.py @@ -785,9 +785,16 @@ def load_account( legacy_fn: str = f'pps.{brokername}.{acctid}.toml' fn: str = f'account.{brokername}.{acctid}.toml' - dirpath: Path = dirpath or (config._config_dir / 'accounting') + dirpath: Path = ( + dirpath + or + (config.get_conf_dir() / 'accounting') + ) if not dirpath.is_dir(): - dirpath.mkdir() + dirpath.mkdir( + parents=True, + exist_ok=True, + ) conf, path = config.load( path=dirpath / fn, diff --git a/piker/config.py b/piker/config.py index 4e286bd8..316ff28f 100644 --- a/piker/config.py +++ b/piker/config.py @@ -188,6 +188,48 @@ def _override_config_dir( _config_dir = path +def _maybe_use_test_dir() -> None: + ''' + When running under the `pytest` harness, override + the config dir to the per-test-tmp dir "passed down" + the actor tree via `tractor`'s runtime-vars + inheritance mechanism. + + See the `tractor_runtime_overrides` usage in our + `tests.conftest._open_test_pikerd()` as well as + `.service._actor_runtime.open_piker_runtime()` for + the root-actor's pre-loading of the var state. + + NOTE: this must be checked lazily at config-path + access time (NOT import time) since sub-actors only + receive runtime-vars once their `tractor` runtime + has fully booted. + + ''' + global _config_dir + import tractor + actor = tractor.current_actor( + err_on_no_runtime=False, + ) + if actor is None: + return + + rvs: dict = tractor.runtime._state._runtime_vars + pvars: dict|None = rvs.get('piker_vars') + if ( + pvars + and + (testdir := pvars.get('piker_test_dir')) + ): + testdirpath = Path(testdir) + assert testdirpath.exists(), ( + f'piker test harness might be borked!?\n' + f'testdirpath: {testdirpath!r}\n' + ) + if _config_dir != testdirpath: + _override_config_dir(testdirpath) + + def _conf_fn_w_ext( name: str, ) -> str: @@ -201,6 +243,7 @@ def get_conf_dir() -> Path: on the local filesystem. ''' + _maybe_use_test_dir() return _config_dir @@ -226,7 +269,7 @@ def get_conf_path( assert str(conf_name) in _conf_names fn = _conf_fn_w_ext(conf_name) - return _config_dir / Path(fn) + return get_conf_dir() / Path(fn) def repodir() -> Path: @@ -271,8 +314,9 @@ def load( ''' # create the $HOME/.config/piker dir if dne - if not _config_dir.is_dir(): - _config_dir.mkdir( + conf_dir: Path = get_conf_dir() + if not conf_dir.is_dir(): + conf_dir.mkdir( parents=True, exist_ok=True, )