recon_gen.common.l2.tg_cache

In-memory TestGeneratorConfig cache for Studio’s data-shaping panel.

X.4.h.2 introduces this cache as the in-memory authority for the trainer’s knob state (plants / scope / end_date / seed). Mirrors the L2InstanceCache shape: constructed once at Studio startup from cfg.test_generator; mutated in-place by the /data/knobs/* PUT routes (h.2-h.5); read by the Studio /deploy route which patches a fresh Config clone with cache.get() before calling run_deploy_pipeline.

No disk persistence here — h.7 layers cfg.yaml save on top via the same atomic-write primitive L2InstanceCache.save uses.

Severability: Studio-only. Dashboards (recon-gen dashboards) does NOT instantiate this cache — it has no knobs to mutate. The absent-cache path is the unit-test surface (make_studio_routes with tg_cache=None); routes that mutate it are mounted only when the cache exists.

Classes

TestGeneratorCache(state[, window, ...])

class recon_gen.common.l2.tg_cache.TestGeneratorCache(state, window=None, *, etl_hook_enabled=True, state_path=None)[source]

Bases: object

Parameters:
classmethod from_cfg_with_state(cfg, cfg_path)[source]

X.4.h.7 — Studio-CLI factory. Load the sidefile if present, merge its overrides on top of cfg.test_generator defaults, wire the cache to write to that sidefile on every mutation.

Sidefile path is <cfg_path.parent>/.studio-state.yaml (sibling of cfg.yaml). Missing sidefile ⇒ pristine cfg defaults + empty Studio state. Malformed sidefile ⇒ same fallback with a warning to stderr (per load_studio_state).

Return type:

TestGeneratorCache

Parameters:
  • cfg (Config)

  • cfg_path (Path | str)

classmethod from_config(cfg)[source]

Snapshot cfg.test_generator + materialize default window.

Window default = [today - (DEFAULT_BASELINE_WINDOW_DAYS - 1), today] — the last 90 days. Trainer-mode UI is not a determinism path, so the wall-clock anchor is honest here.

No sidefile persistence — see from_cfg_with_state for the Studio-CLI flow that loads + saves to disk.

Return type:

TestGeneratorCache

Parameters:

cfg (Config)

get()[source]

Return the current generator state.

TestGeneratorConfig is frozen, so the returned reference is safe to share without defensive-copy concerns.

Return type:

TestGeneratorConfig

get_frame()[source]

BD.5 — return the trainer’s frame as a single AsOfFrame.

The trainer’s three temporal-and-determinism pieces — scrub head (get_up_to()), scenario window (get_window()), and RNG seed (get().seed) — bundle naturally into the post-BD.1 AsOfFrame(as_of, window, seed) shape. Callers that need all three together (plant emit via scenario_to_generators(frame=…) per BD.3, dashboard defaults via DateView(frame=…) per BD.4) take the frame instead of three independent reads.

Derivation: as_of = get_up_to() (the scrub head — what the trainer is “looking at”); window = _window; seed = state.seed (None when the cfg hasn’t pinned one, same as AsOfFrame.live()).

Return type:

AsOfFrame

get_up_to()[source]

Resolve the “up to” / scrub-head date.

tg.end_date is the cached value; when it’s None the cache falls back to self._window.end (the trainer’s intent: “render up through the right edge of my scenario window”).

Return type:

date

get_window()[source]

Return the trainer’s scenario window (closed-closed dates).

Return type:

DateInterval

is_etl_hook_enabled()[source]

Return whether cfg.etl_hook will run on the next Deploy.

True (default) ⇒ patched_config keeps cfg.etl_hook as configured. False ⇒ patched_config clears it to None for that deploy (the cfg’s stored command is unaffected — the operator can flip the toggle back on without re-typing).

Return type:

bool

patched_config(cfg)[source]

Return a clone of cfg with test_generator swapped in.

Trainer “scrub head” model — end_date and cutoff_date play different roles in the patched cfg:

  • end_date becomes window_end (the scenario anchor). Generator anchors at this date so plants land at fixed calendar positions regardless of where the trainer’s scrub head is. Stable scenario.

  • cutoff_date becomes the trainer’s up_to (the scrub head). Deploy’s _build_generator_sql appends DELETE statements after the generator emits to truncate rows past this date. None when up_to == window_end (no truncation).

This decouples “what scenario am I rendering?” (anchor = window_end) from “how far through it am I?” (cutoff = up_to). Click in the timeline → up_to changes, plants stay put, emission cuts off at the new scrub head. Matches the trainer’s mental model end-to-end.

CLI invocations of data apply don’t go through this method — they read cfg.test_generator directly, where end_date keeps its legacy “anchor” meaning and cutoff_date defaults to None (no truncation, current byte-identical-to-locked-seeds behavior).

Return type:

Config

Parameters:

cfg (Config)

replace(new_state)[source]

Swap the cached generator state (window untouched).

Return type:

None

Parameters:

new_state (TestGeneratorConfig)

set_etl_hook_enabled(enabled)[source]

Toggle cfg.etl_hook execution on the next Deploy.

Return type:

None

Parameters:

enabled (bool)

update(*, scope=<object object>, end_date=<object object>, seed=<object object>, plants=<object object>)[source]

Partial update of the generator state — window is separate.

None is a valid value for end_date and seed (it means “use the locked default”), so a _UNSET sentinel is the only way to express “leave this field alone”. Returns the new generator state for the caller to inspect / log.

Return type:

TestGeneratorConfig

Parameters:
  • scope (Literal['full', 'exceptions_only', 'uncovered_rails', 'only_template'] | object)

  • end_date (date | None | object)

  • seed (int | None | object)

  • plants (tuple[Literal['drift', 'overdraft', 'limit_breach', 'stuck_pending', 'stuck_unbundled', 'supersession'], ...] | object)

update_derive_balances(enabled)[source]

X.4.i.3 — toggle the derive_balances post-step-3 flag.

Return type:

None

Parameters:

enabled (bool)

update_only_template(value)[source]

X.4.i.3 — set the only_template name (template-scope target).

None clears the field. Validation against the L2 instance’s actual templates happens at deploy time in _only_template_rails, not here — the UI accepts any string so the trainer can hold an inconsistent state mid-edit.

Return type:

None

Parameters:

value (str | None)

update_window(*, start=<object object>, end=<object object>)[source]

Partial update of the trainer’s scenario window.

Both bounds optional — pass only what changed. After update if start > end, swap them (operator typed in a confusing order; preserve the intent rather than reject). Returns the new DateInterval for caller logging.

Window changes do NOT touch end_date (the up_to scrub head) — it stays where the operator set it. The renderer clamps out-of-window up_to values for display; the next click in the panel will overwrite to a valid date anyway.

Return type:

DateInterval

Parameters:
  • start (date | object)

  • end (date | object)