Compare commits

...

114 Commits

Author SHA1 Message Date
Gud Boi 9176901651 Record chart disconnect E2E handoff in Taken
Add the identified test assignment with independent daemon and
chart-process ownership, alternating departures, reattachment,
search-state variants and deterministic startup-cancellation gates.

Keep service-survival evidence separate from clean chart exit,
including FSP bootstrap/debug-lock failures and native crashes.
Record order-identity and cancellation coverage while retaining
explicit authorization boundaries for live IB and order execution.

Capture current testing/evaluation branch and environment differences
and link the canonical crash and clearing concerns. Their work is
separate; this commit records the E2E assignment, not implementation
or acceptance, and preserves all existing task states.

(this patch was generated in some part by `opencode` using `gpt-6-astra` (`openai`))
2026-10-20 11:46:24 -04:00
Gud Boi 3818b434e4 Record TC draft split and resume context in Taken
Add an identified follow-up to unpack draft e815fd77 after upstream
branch history is settled, preserve its complete changeset, and
prepare reviewed behavioral commits with their checks.

Record the verified Codex dialog and TC checkout in AI_DLOG and
AI_WKT so this work can be resumed. Preserve every existing task
and checkbox state.

(this patch was generated in some part by `codex` using
GPT-6 family models (`openai`); exact model IDs unavailable)
2026-10-06 22:35:08 -04:00
Gud Boi 2acf6c0c32 Add typed clearing migration follow-ups to Taken
Record the committed schema/reducer foundation and its offline
qualification alongside the remaining paper, live adapter, EMS/UI,
and paired payload-enforcement work. Link the implementation plan
and retain explicit race, startup and bounded teardown checks.

Add one identified task with 23 unchecked items. Existing task
states remain human-owned.

(this patch was generated in some part by `codex` using
`gpt-6` (`openai`))
2026-10-06 22:19:09 -04:00
Gud Boi 5a57051973 Add Trend Calculus and Jev follow-ups to Taken
Append six identified tasks and 24 unchecked items for prototype
review, live Jev qualification, provider-service wiring, Pine
parity, chart qualification and native subsecond sampling.

Preserve existing task states, properties, prose and ordering. Link
the existing FSP synchronization task and retain annotation IPC
work with its current tracker. Record f148fbcc on moar_ai_skillz as
a workflow dependency rather than duplicating it.

Allow Taken's required final subtree separator only for .taken Org
files through a narrow whitespace attribute. Keep the guarded save
and prompting provenance alongside this data-only change.

Prompt-IO: ai/prompt-io/codex/20261005T230013Z_9555e20c_trend_followups_d48a77_prompt_io.md

(this patch was generated in some part by `codex` using
GPT-6 family models (`openai`); exact model IDs unavailable)
2026-10-06 15:09:45 -04:00
Gud Boi 9555e20cb3 Add a federated project store from LNS
Move the project scope from the current LNS working corpus into a
registered branch-local store. Preserve task states, checkbox
markers, carry metadata and contextual cross-repository references.
Expose the shared taken skill through the local discovery tree.
Include the existing live-store snapshot under a contextual
heading; retain every stable ID and state. Record cutover
requirements before retargeting existing agents. Validate Org
structure and manifest identity; keep application test suites
outside this data-only scope.

Prompt-IO: ai/prompt-io/20261003T001837Z_9115d24_project_stores.md

(this patch was generated in some part by `codex` using `gpt-6`
(`openai`))
2026-10-04 15:32:41 -04:00
Gud Boi 9115d24da0 Fix string errors in `.ib.broker` relay
Normalize structured and raw IB errors without indexing strings as
message dictionaries.

Deats,
- match each error form before request-id handling;
- parse `[code N]` and `[Errno N]` prefixes without crashing;
- cover raw errors through `deliver_trade_events()`.

Prompt-IO: ai/prompt-io/opencode/20260828T063126Z_1606122b_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-28 02:52:12 -04:00
Gud Boi 1606122b45 Add `piker-clearing-expert` skill
Capture EMS/order-control ownership, identifier boundaries, ledger
and position invariants, backend variation, and current known hazards.

Store the canonical bundle under `.agents/skills/` and bridge Claude
discovery through a portable relative link.

Also,
- add architecture, debugging, and test-map references;
- document the provider-neutral source convention;
- ignore the canonical `wkts/` runtime root.

Prompt-IO: ai/prompt-io/opencode/20260828T021411Z_6ca6ab2a_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-27 23:24:54 -04:00
Gud Boi 7b51927f6b Fix `SymbologyCache.search()` key matching
RapidFuzz treated `mktmaps` values as fuzzy choices and called
`len()` on `MktPair` structs during paper-ledger persistence.

Search string keys explicitly and resolve each match back to its
struct so native market-ID aliases survive legacy FQME
qualification.

Also,
- cover alias preservation and the paper-ledger TOML round trip.

Prompt-IO: ai/prompt-io/opencode/20260828T013057Z_6ca6ab2a_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-27 23:11:43 -04:00
Gud Boi 6ca6ab2a10 Fix Binance USD-M websocket routing
Binance retired its legacy futures websocket and split quote
traffic across `/public` and `/market`. Route `bookTicker` and
`aggTrade` through dedicated URLs, merge both sockets, and accept
the new `st` stream-type field.

Bound connection and first-quote startup so failed or silent
streams raise `DataUnavailable` instead of hanging `datad` and
chart consumers.

Also,
- derive feed mode from resolved `FutesPair` metadata
- keep direct URL subscriptions stable across reconnects
- cover USD-M routes, payloads, and startup timeouts

Prompt-IO: ai/prompt-io/opencode/20260826T222600Z_5f631e19_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-26 19:24:37 -04:00
Gud Boi 5f631e1996 Plan repository-wide branch landing order
Audit every retained worktree, branch family, rebased patch, stash,
and unreachable commit against `main`.

Deats,
- factor useful histories into dependency-ordered PR lanes;
- reduce the DPI branch swarm to one five-commit patch set;
- separate runtime, datad, backfill and chart ownership work;
- record deferred patches and post-landing stale-ref cleanup;
- attach Prompt-IO provenance for the generated audit plan.

Prompt-IO: ai/prompt-io/opencode/20260818T064340Z_09ddcf50_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-18 12:47:30 -04:00
Gud Boi 09ddcf5055 Plan `pytest-qt` chart UI audit layers
Distill the gap-overlay E2E findings into a staged audit plan for
the full PyQtGraph iface. Keep Qt and graphics real while replacing
feeds, services and provider search with deterministic seams.

Deats,
- define the headless PyQt6 env and pytest-qt fixture ownership;
- layer primitive, input, SHM, full-window and actor checks;
- guard window SIGINT, provider search and overlapping symbol boot;
- require signal/postcondition waits and lifecycle leak assertions;
- split reviewable PRs before distilling a repo-local UI skill.

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-17 19:55:26 -04:00
Gud Boi 335190a4e3 Test gap dialogs through a real `tractor` actor
Schema checks do not exercise ctx discovery, directional decoding,
stream setup, request correlation or endpoint-finally cleanup.

Boot an isolated child actor around `remote_gap_overlays()` and
drive it through `AnnotClient`. Cancel one published request, leave
its reply queued and prove the next request discards that stale
msg.

Also,
- verify generated request IDs and remote-owner cleanup;
- document automated coverage and broker-backed manual boundaries.

Prompt-IO: ai/prompt-io/opencode/20260817T222036Z_f65df6a1_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-17 19:11:03 -04:00
Gud Boi b738c9f4f7 Fix focused gap routing in `ChartView`
The keyboard loop treated every display not owning the focused
history chart as a realtime target. One `Ctrl+G` could therefore
toggle unrelated cached symbols.

Match the focused chart explicitly before selecting its timeframe.
Factor startup registration and toggle routing into testable
helpers, and use scoped PyQt6 enums throughout the input path.

Deats,
- preserve independent 60s startup layers per display;
- toggle only the focused 1s or 60s chart;
- drive the production event relay with a real `QKeyEvent`.

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-17 19:09:46 -04:00
Gud Boi 711666f9c8 Fix Qt indexing in `GapAnnotations.reposition()`
`prepareGeometryChange()` ran after the gap rects and arrow specs
were mutated. Qt could retain the item's old scene index even
though `boundingRect()` returned its new prepended-history
position.

Invalidate cached bounds and notify Qt before changing any
geometry. Cover both scene lookup positions with a real offscreen
item.

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-17 19:08:28 -04:00
Gud Boi 78982d8280 Fix `PlotItem` cleanup in `GapOverlayMngr`
`PlotItem.addItem()` records overlays outside the Qt scene. Direct
scene removal leaves stale plot refs and range bookkeeping behind.

Track each annotation's owning plot and remove it through the same
API used for insertion. Exclude pixel-sized arrows from data
bounds.

Deats,
- centralize replacement, hiding and owner teardown;
- retain scene removal as a defensive fallback;
- cover real Qt registries and duplicate-FQME chart identity.

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-17 19:08:02 -04:00
Gud Boi f65df6a138 Own gap overlays in chart-local `GapOverlayMngr`
Detect positive timestamp gaps from chart OHLCV SHM with one
vectorized pass. Route local UI and remote callers through one
`GapOverlayMngr` renderer, and key each layer by owner, chart,
FQME and timeframe so remote ctx teardown cannot remove local
graphics.

Deats,
- add tagged `SetGapOverlay` requests and typed
  `GapOverlay` responses;
- keep `remote_annotate()` intact and serve its typed dialog from
  a dedicated `remote_gap_overlays()` Tractor ctx;
- admit `SetGapOverlay`, `GapOverlay`, `list[str]` and `None` in
  the Tractor payload spec, then strictly narrow requests to
  `SetGapOverlay` and replies to `GapOverlay`;
- serialize shared-stream dialogs and correlate request IDs so
  stale replies cannot skew later exchanges;
- rename `AnnotCtl` / `open_annot_ctl()` to `AnnotClient` /
  `open_annot_client()` across storage and annotation callers;
- enable 60s history overlays at startup and toggle the focused
  1s or 60s chart directly with `Ctrl+G`;
- guard redraws by chart, FQME and timeframe; tolerate empty
  arrays and out-of-range lookups; update rect and arrow geometry;
- cover detector geometry, msgpack roundtrips, schema rejection,
  typed stale-FQME errors and renamed storage seams;
- document ownership, refresh, stack landing and live
  qualification.

Prompt-IO: ai/prompt-io/opencode/20260814T032716Z_2aa66c2c_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-14 19:03:11 -04:00
Gud Boi 2aa66c2cf6 Fix IB `MethodProxy` response routing
`open_client_proxy()` broadcast its one-slot linked channel to an
always-running status relay and an idle method receiver. Status
traffic advanced only the relay, so the first lazy contract lookup
for an uncached order raised `Lagged` before receiving its
response.

Give `relay_client_proxy_messages()` sole receive ownership. Route
tagged responses from concurrent caller tasks into their per-`mid`
slots while each proxy context retains its own channel and relay.

Deats,
- log stale responses with their original method name;
- warn when unresolved-request metadata exceeds its bounded limit;
- drop late responses after caller cancellation;
- preserve the reconnect design note and message-case rationale;
- accept clean channel EOF during proxy teardown;
- cover idle status, concurrency, cancellation and EOF paths.

Prompt-IO: ai/prompt-io/opencode/20260811T205826Z_06d5ea5d_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-11 18:46:23 -04:00
Gud Boi 06d5ea5dc0 Fix sparse IB history prepends in `ShmArray`
IB returns trading samples, not one row per wall-clock period.
Converting elapsed time to a physical prepend offset left 42,720
zero rows in NVDA's 60s buffer even though both valid margins
shared the same timestamp.

Use the actual earliest provider sample as the NativeDB boundary.
After reverse retrieval, prepend older stored rows directly beside
it.

Deats,
- start reverse insertion at the current first index;
- update `ShmArray._first` after every provider prepend;
- trim provider/NativeDB overlap by its timestamp;
- publish the final storage prepend to chart and FSP consumers;
- derive the source `time` field from `ohlc_key_map`;
- cover the full lifecycle with a real multi-frame `ShmArray`.

Prompt-IO: ai/prompt-io/opencode/20260805T195001Z_f0f3c806_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-05 19:40:07 -04:00
Gud Boi f0f3c80657 Simplify `store` series and SHM discovery
Make `store ls` show every NativeDB FQME and its available sample
periods by default. Keep `store series` as the detailed view of
each exact sub-series, including its period, size and path. Share
the same optional FQME filter and numerically ordered payload.

Use `store shm` as the canonical volatile-buffer ep and make its
default operation a read-only report. Enter the prior persistence
workflow only through `--write-parquet`, require that flag before
`--reload-parquet-to-shm`. Reject read-only formatting options in
repair mode instead of silently ignoring them.

Also,
- update the qualification docs, debug helper and harness refs;
- cover no-flag listings, command help and SHM mode validation;
- retain exact generation and object-name selection for stale SHM.

Prompt-IO: ai/prompt-io/opencode/20260801T015321Z_74ea2152_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-01 00:34:55 -04:00
Gud Boi 74ea2152f9 Expose storage state through `piker store`
Make bare storage groups and eps render help, so the CLI is
discoverable without memorizing `Typer` option conventions. Add
read-only `series` and `shm` eps for durable `NativeDB` series and
live history buffers, with `Rich`-table or `JSON` output.

Deats,
- preserve exact `(fqme, period)` identities for `NativeDB`
  series;
- parse exact `(generation, fqme, kind)` names for SHM buffers;
- reconstruct provider dtypes when attaching to live buffers;
- tolerate buffers disappearing between discovery and attach.

Also,
- report expected and observed cadence separately, plus invalid
  rows, ordering defects and ranked gaps;
- add exact `ldshm --shm-name` selection and keep disabled write
  and reload paths from reading undefined state or entering `pdb`;
- document the triage layers and cover help, identity parsing,
  read-only diagnostics, dtype selection and `ldshm` flow.

Prompt-IO: ai/prompt-io/opencode/20260731T165215Z_0846cbd4_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-31 17:26:11 -04:00
Gud Boi 0846cbd4a4 Repair IB startup gaps in `ShmArray`
Keep reverse history and stored SHM layout aligned so sparse IB
frames do not expose permanent zero-time reservations.

Deats,
- omit inclusive query endpoints from SHM but retain full storage
- serialize and bound farm resets, blanks, and cancellations
- run the final null sweep after reverse retrieval completes
- require valid timestamp boundaries for synthetic gap rows
- cover the live QQQ 653-row failure and reset interleavings

Prompt-IO: ai/prompt-io/opencode/20260729T204046Z_ad6c560f_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-29 16:46:52 -04:00
Gud Boi ad6c560f6b Guard `ldshm` from invalid SHM snapshots
Do not pass unpublished or corrupt timestamp rows into dedupe,
`NativeDB` replacement writes, or live SHM reload.

Deats,
- infer cadence from finite positive observed steps
- choose the smaller observed step when frequency ties
- skip invalid or degenerate snapshots as a whole
- cover the QQQ zero-slot failure with mutation-failing spies

Prompt-IO: ai/prompt-io/opencode/20260729T185406Z_d359b1cb_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-29 15:07:50 -04:00
Gud Boi d359b1cbfb Repair first-slot OHLC sentinels in `NativeDB`
Filter known zero-initialized epoch slots before persisted history
hydrates SHM or merges with a fresh provider frame.

Deats,
- reindex the repaired read view for same-session chart publication
- treat sentinel-only files as empty history for fresh backfill
- reject new first-slot sentinels but preserve modern zero-price bars
- classify raw sentinel evidence separately in `store audit`

Prompt-IO: ai/prompt-io/opencode/20260729T041723Z_ce33deb6_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-29 13:30:51 -04:00
Gud Boi ce33deb6b9 Use IB UTC syntax for history `endDateTime`
Serialize aware reverse-fill boundaries as `YYYYMMDD-HH:MM:SS`
before handing them to `ib_async`.

This avoids Gateway error 10314 from its trailing-`UTC` format and
drops the unfinished EST comparison breakpoint from `Client.bars()`.

Prompt-IO: ai/prompt-io/opencode/20260729T034106Z_922a5df8_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-28 23:44:15 -04:00
Gud Boi 922a5df88d Add `xonsh` backfill qualification workflow
Drive known-bad, fresh, restart and append checks from a private
disposable NativeDB root.

Deats,
- bind child config and imports to the marked worktree root
- preserve checksum-matched reports, snapshots and case metadata
- compare timestamp coverage, seam values and persisted structure
- document manual chart runs, gap triage and the expansion matrix

Prompt-IO: ai/prompt-io/opencode/20260728T003247Z_ed85721c_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-28 19:41:55 -04:00
Gud Boi d7d537d531 Add raw OHLCV audit to `NativeDB`
Inspect Parquet bytes directly without opening storage or normalizing
malformed evidence.

Deats,
- report canonical schema, timestamp, index and finite-value state
- retain exact cadence aggregates with bounded endpoint details
- snapshot bytes before parsing and clean up partial copy failures
- reject unsafe FQMEs, links, collisions and non-regular sources

Prompt-IO: ai/prompt-io/opencode/20260728T005034Z_ed85721c_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-28 19:38:50 -04:00
Gud Boi 1f30917047 Repair legacy timestamps in `NativeDB` merges
Keep incoming provider frames strict while dropping non-positive rows
from persisted history before merge validation.

This lets known-bad MNQ history repair itself on the next valid IB
frame without losing valid old bars or publishing noncanonical indexes.

Prompt-IO: ai/prompt-io/opencode/20260728T232718Z_ed85721c_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-28 19:37:15 -04:00
Gud Boi ed85721cfd Clean test-owned SHM leaks in `pytest`
Track successful `create=True` calls through Tractor's
current-process factory instead of scanning `/dev/shm` or trusting
the attachment cache.

- cooperate with normal actor unlink and restore token-cache state
- verify POSIX object identity before fallback cleanup
- fail after cleaning exact surviving segments
- reproduce the pre-`current_actor()` allocation leak

Prompt-IO: ai/prompt-io/opencode/20260727T215906Z_689df816_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-27 18:09:03 -04:00
Gud Boi 19c6d98e6d Normalize provider OHLCV for `NativeDB`
IB history frames omit the derived `index` and append `count`.
Normalize provider data to durable names, order, and dtypes before
merge, and reject fractional epoch truncation.

Also,
- map canonical storage fields into provider-specific SHM on reload
- select Polars columns by name instead of position
- exercise first-start and restart through actor, SHM, and Parquet

Prompt-IO: ai/prompt-io/opencode/20260727T212454Z_689df816_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-27 18:08:33 -04:00
Gud Boi 689df816d6 Drop redundant `NativeDB` writer locks
`datad`'s persistent feed task already owns each series writer.
The actor-local and file locks duplicated this SC contract and left
`.parquet.lock` sidecars that upstream indexed as data.

- preserve validated temp writes, `fsync()`, and atomic replacement
- keep exact-suffix indexing for legacy lock and temp sidecars
- prove full and incremental writes create no lock artifacts

Prompt-IO: ai/prompt-io/opencode/20260727T204711Z_84a6d47b_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-27 16:58:00 -04:00
Gud Boi 84a6d47b44 Plan known-FQME backfill qualification
- preserve dirty main-tree work while transferring branch ownership
- hold provider, FQME, timeframe, and symptom fixed while debugging
- capture read-only baselines before disposable live runs
- turn each reproduced failure into deterministic coverage
- qualify interruption and restart behavior after the normal lifecycle

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-24 18:33:06 -04:00
Gud Boi b000db94e0 Make `NativeDB` backfills atomic and non-destructive
- merge provider deltas without dropping unaffected history
- validate reopened temp parquet before atomic replacement
- serialize writers with actor-local and cancellable file locks
- publish cache only after disk replacement
- persist full startup/backfill frames before SHM publication
- bound sampler notifications and avoid MarketStore duplicates

Prompt-IO: ai/prompt-io/opencode/20260722T024134Z_3a4f6737_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-24 18:33:06 -04:00
Gud Boi c60a5a45f3 .tsp: report non-positive timestamp steps
- keep positive missing-history gaps distinct from ordering faults
- return duplicate and reversed timestamp rows as structured data
- warn before normalization removes the original ordering evidence

Prompt-IO: ai/prompt-io/opencode/20260722T024133Z_3a4f6737_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-24 18:33:06 -04:00
Gud Boi 6913e80d1a Preserve gap-annotation profiling handoff
Track the untracked Claude session note with its benchmark, client
bottleneck, and follow-up optimization sketch intact.

(this commit msg was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-24 18:33:06 -04:00
Gud Boi 931f22d235 `.tsp._history`: drop orphaned backfill events
- let structured nursery joins own child-task completion
- stop empty provider frames before timestamp endpoint indexing
- cover provider exhaustion and empty frames with bounded tests

Prompt-IO: ai/prompt-io/opencode/20260722T001358Z_16f3cd6f_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-24 18:33:06 -04:00
Gud Boi 976db6c33c .tsp: normalize gaps after sorted dedupe
- recompute datetime and delta cols after final timestamp ordering
- detect only positive sample-step gaps
- document inclusive null grouping with an updated visual map
- check all small null layouts against an independent test oracle
- honor selected null cols and reject discontinuous frame indexes
- share exact null grouping across NumPy and Polars

Prompt-IO: ai/prompt-io/opencode/20260722T001357Z_16f3cd6f_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-24 18:33:06 -04:00
Gud Boi 99fb9d2eab Plan gap-checker and backfiller repairs
Lay out a correctness-first path from deterministic regressions
through normalized gap detection, structured backfill completion,
and crash-safe `NativeDB` persistence.

Deats,
- separate checker, storage, provider, and annotation gates
- record confirmed current defects and data invariants
- define commit boundaries and timeout-wrapped verification

Prompt-IO: ai/prompt-io/opencode/20260721T215055Z_c6ec3d41_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-24 18:33:06 -04:00
Gud Boi e766c158de Add Piker `/run-tests` harness
Extract current test env, scope, fixture, and runtime guidance into a
repo-owned override for the canonical Run base.

Deats,
- require `xonsh` plugin disable and approved Nix provisioning
- separate deterministic checks from live, GUI, and container cases
- map source areas and document actor timeout retry behavior

Prompt-IO: ai/prompt-io/opencode/20260724T220156Z_43d7a3ed_prompt_io.md

(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
2026-07-24 18:22:12 -04:00
Gud Boi 43d7a3edad Use canonical `ai.skillz` provider links
Replace the local `commit-msg` base and OpenCode shim with
canonical ignored links while preserving Piker's tracked style guide.

Also,
- expose the full skill catalog to Claude and OpenCode
- add canonical slash commands, including `run-tests`
- keep hybrid runtime state local

(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
2026-07-24 17:50:12 -04:00
Gud Boi c6ec3d4144 Share piker skills across coding harnesses
Use portable Agent Skills metadata and one repo-local source for
both Claude Code and OpenCode.

Deats,
- replace harness-only frontmatter and dynamic context
- make `commit-msg` runtime-aware and review-first
- add a thin OpenCode command and integration docs
- guard `np.searchsorted()` guidance before OOB indexing
- record OpenCode prompt provenance

Prompt-IO: ai/prompt-io/opencode/20260721T205228Z_58ffe487_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-21 17:09:07 -04:00
Gud Boi 58ffe48775 Bump `flake.lock` after dev-shell fixes
Advance `nixpkgs` to `241313f4`; the refreshed closure passes Qt,
`pyqtgraph` and wksmod-path import checks.

(this commit msg was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-21 16:44:58 -04:00
Gud Boi ceb806c136 Rebuild stale `uv` envs after flake updates
`uv sync` accepts an existing same-minor Python even when its Nix
store path belongs to an older closure. That can leave a glibc 2.40
interpreter loading Qt built against glibc 2.42.

- derive the Python package once from `cpython`
- clear `py313` when its interpreter target differs from the flake
- ignore inherited `VIRTUAL_ENV` before syncing

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-21 16:37:59 -04:00
Gud Boi 3da8900f68 Fix `flake.nix` dev-shell install
The refreshed Nix closure no longer supplies `libz.so.1` to the
`uv` NumPy wheel, and leaves the synced `py313` env off `PATH`.

- add Nix `zlib` to `LD_LIBRARY_PATH` for native Python deps
- activate `py313` after `uv sync` so `pyqtgraph`, `piker` and
  `xonsh` resolve from the locked env

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-07-21 15:13:35 -04:00
Gud Boi f6dc218a8e Bump `flake.lock` to avoid nix warnings..
However this breaks `pyqtgraph` due to missing `PyQtX` libs??
2026-07-15 16:29:18 -04:00
Gud Boi b73300c820 Add `piker-conc-expert` claude-code skill
Distilled distributed-runtime + structured-concurrency
expertise for the `tractor` actor-tree, auto-applied (not
user-invocable) when working on daemon/service arch, RPC
eps, `to_asyncio` integration, cancellation semantics or
hang/wedge/skew debugging.

Deats,
- `SKILL.md`: the core mental model incl. the post-split
  actor-tree taxonomy (`datad`/`brokerd`/`emsd`/etc.),
  daemon lifecycle conventions, actor-local-state hazards
  and `tractor` primitive usage as deployed here.
- `gotchas.md`: symptom -> cause -> fix entries distilled
  from this branch's (datad|brokerd)-split debugging (eg.
  un-warmed contract caches, stale IPC resps, double/bare
  log records, ib client-id collisions).
- `debug-recipes.md`: actor-system forensics incl. wedged
  actor triage, hang-proof test gating and regression vs
  pre-existing attribution.

(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 <noreply@anthropic.com>
2026-06-10 20:21:48 -04:00
Gud Boi 0404e4230e .ib.api: req-id correlate `MethodProxy` calls
The (single, shared) relay chan has NO ordering guarantee
when a caller task is cancelled (eg. by a search-req
timeout) after sending its request but before consuming the
response: the "orphaned" response gets mis-delivered to the
next caller, off-by-one skewing every result thereafter!

Deats,
- tag each request with a `mid` from an `itertools.count()`
  on the proxy and echo it back in both the result and
  exception resps from `open_aio_client_method_relay()`.
- drop (w/ a warning) any stale resp whose `mid` doesn't
  match the current caller's and keep waiting for ours.
- rewrite the resp-wait loop as a `match:` (resolving the
  old "py3.10 syntax" TODO) incl. the prior inline
  `('error', ...)`/`('event', ...)` relay cases.

(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 <noreply@anthropic.com>
2026-06-10 20:19:41 -04:00
Gud Boi ba32f286b9 datad: console-log the backend's mod subtree
Pre the (datad|brokerd)-split this was done by `brokerd`'s
daemon fixture, so ALSO `get_console_log()` the provider
backend's mod subtree (eg. `piker.brokers.ib.*`) in
`_setup_persistent_datad()`; without it all backend records
emitted in the `datad` actor fall through to the stdlib's
bare (non-colorized) `logging.lastResort` handler.

(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 <noreply@anthropic.com>
2026-06-10 20:13:08 -04:00
Gud Boi b0766764f0 .ib.broker: eagerly pre-qualify pp/order mkts
Follow-up to the lazy order-req qualify: pre-qualify (and
cache) contracts for ALL already-open pps and orders during
`open_trade_dialog()` startup so live submissions NEVER pay
a first-request qualification delay; the warmup runs before
the order handler task starts so early reqs just buffer in
the ems IPC stream. Any brand-new mkt still lazily
qualifies on its first submission.

Deats,
- factor the `Client` lookup-table writes out of
  `.symbols.get_mkt_info()` into a new `cache_contract()`
  helper which now ALSO keys `_contracts` by `mkt.fqme`
  (read by the fill-time pp-update path in
  `emit_pp_update()`) alongside `mkt.bs_fqme` (read by
  `Client.submit_limit()`); resolves the old "post-split
  mktmap lookup" TODO.
- explicitly `cache_contract()` in
  `handle_order_requests()` since the lifo-cache may
  deliver a hit (body skipped) when another acct/client
  already qualified the fqme.
- filter `None` entries (ambiguous contracts) from
  `qualifyContractsAsync()` results in
  `Client.find_contracts()` before any attr access + raise
  a better "use a (more) venue-qualified fqme" error msg.
- relay ALL (non-cancel) errors from the aio method-relay
  task to the `trio`-side caller instead of crashing the
  whole proxy/dialog; critical post-`datad`-split where eg.
  qualification failures are expected to be caught
  per-request by order/warmup code.
- handle inline `('event', ...)` api-farm status msgs in
  `MethodProxy._run_method()` at info-level instead of the
  "UNKNOWN IB MSG" warning.
- only `log.setLevel()` in `open_trade_dialog()`;
  attaching a handler via `get_console_log()` double-prints
  every record since the daemon fixture already enables the
  console handler on the parent subsys logger.

(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 <noreply@anthropic.com>
2026-06-10 20:09:27 -04:00
Gud Boi 456c6a5567 .ib.broker: lazily qualify contracts on order req
Post (datad|brokerd)-split the trading actor's
`Client._contracts` cache is never warmed by in-proc
feed setup (that now happens in the `datad.ib` sibling)
so ALL live submissions failed with "no live feed?" at
`Client.submit_limit()`; `brokerd` must be able to
submit orders without any feed registered in its own
subactor.

Deats,
- thread the acct `proxies` table into
  `handle_order_requests()` and, on a `_contracts`
  cache-miss for the req's fqme, lazily run the same
  `get_mkt_info(fqme, proxy=...)` symbology ep the
  feed-side uses; it writes the `mkt.bs_fqme` key
  `submit_limit()` looks up (and warms `_cons2mkts`
  which the position-audit path also needs) on exactly
  the same aio `Client` instance.
- guard `submit_limit()` w/ a try/except ->
  `BrokerdError` relay so a single bad submission
  degrades to an EMS error msg instead of crashing the
  dialog (and causing the `TrioTaskExited` teardown
  storm seen in testing).
- fix the (non-f-string..) raise msg in
  `Client.submit_limit()` and doc the new lazy-qualify
  contract; the bug was foretold by the TODO in
  `.symbols.get_mkt_info()` B)

(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 <noreply@anthropic.com>
Prompt-IO: ai/prompt-io/claude/20260610T213549Z_f084e899_prompt_io.md
2026-06-10 17:36:51 -04:00
Gud Boi f084e89991 Add `datad`-split design plan + provenance docs
Commit the AI-authored design doc driving this branch's
7-commit `brokerd` -> (`datad` + `brokerd`) split; all
prior commits' `Prompt-IO:` entries reference its
stages so this makes those refs resolvable in-repo for
PR review.

Also,
- add the doc's own prompt-io entry pair (scope: docs)
  incl. a 4-item implementation-deviation log vs. the
  plan as written.

(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 <noreply@anthropic.com>
Prompt-IO: ai/prompt-io/claude/20260610T173309Z_f15f8178_prompt_io.md
2026-06-10 13:38:50 -04:00
Gud Boi f15f8178a3 brokerd: slim RPC caps + `ib` client-id offset
Caps-sec tightening now that `brokerd` is trading-only: NO
`piker.data.*` (feed) mods are RPC-enabled in the (live,
credentialed) trading actor anymore.

Deats,
- drop `_data_mods` for a minimal `_brokerd_service_mods`
  (just `piker.brokers._daemon`); dedup-compose with the
  backend's set in `spawn_brokerd()`.
- `broker_init()` reads the backend's `_brokerd_mods`
  (fallback: `__enable_modules__` for flat backends).
- fail fast in `spawn_brokerd()` via `validate.get_eps()`
  when a backend offers NO live order-ctl eps (eg.
  `kucoin`, `deribit`) -> tells the caller to use
  paper-mode instead of booting a dead actor; analogous
  warning in `datad_init()` for datad-ep-less backends.
- offset `ib`'s default `client_id` per daemon-kind in
  `load_aio_clients()`: post-split BOTH `datad.ib` and
  `brokerd.ib` connect to the same gw/tws endpoint and the
  shared default (6116 + linear retry incrs) would collide
  and burn the full conn-timeout retry cycle; datad gets
  +16, ad-hoc (test/cli) actors +32.
- drop the import-cleanup leftovers (`exceptiongroup`,
  `_FeedsBus` type-only import) and the now-resolved
  "expose datad" TODO in `.cli`.

(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 <noreply@anthropic.com>

Prompt-IO: ai/prompt-io/claude/20260610T171344Z_eee19de0_prompt_io.md
2026-06-10 13:14:24 -04:00
Gud Boi eee19de090 .data: cut feed layer over to `datad` actors
The topology flip: all data-feed consumers now route to the
new `datad.<broker>` sibling daemon; `brokerd` becomes
trading-only and is ONLY ever booted lazily by `emsd`'s
`open_brokerd_dialog()` (see prior commit). Chart-only and
paper sessions run with zero (live, credentialed) `brokerd`
procs B)

Deats,
- `open_feed()` -> `maybe_spawn_datad()` (NB: imported
  relative-direct from `._daemon` to dodge a partial-init
  cycle via `piker.service`); flip the `open_feed_bus()`
  actor-name assert to `'datad'`; comment sweep.
- slim `_setup_persistent_brokerd()` to a trading-only
  fixture: console logging + pinned-open ctx; the feed-bus
  alloc moves to `_setup_persistent_datad()` and backend
  `open_trade_dialog()` ctxs own their own task trees.
  (the `piker ledger` ad-hoc actor enters this same slimmed
  fixture - exactly what it needs.)
- repoint data-flavoured spawn sites to `maybe_spawn_datad`:
  `.ui._app` symbol-search (+ rename
  `install_brokerd_search` -> `install_datad_search`),
  `.brokers.core.symbol_search()`, `.brokers.cli`
  `brokercheck`/`record`, legacy kivy `.ui.cli` +
  `option_chain`'s `wait_for_actor()`.
- invert `tests.test_services` expectations: feed/EMS-paper
  flows must spawn `datad.kraken` and `paperboi.kraken`
  with an explicit negative assert that NO `brokerd.kraken`
  service task exists.

(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 <noreply@anthropic.com>

Prompt-IO: ai/prompt-io/claude/20260610T171259Z_59d5d9a6_prompt_io.md
2026-06-10 13:13:44 -04:00
Gud Boi 59d5d9a66d .clearing: lazily spawn `brokerd` from `emsd`
Drop the ONE coupling that forces feed + trading eps into
the same actor: `Router.open_trade_relays()` pulling its
trades-dialog portal from `feed.portals[brokermod]`.
Instead `open_brokerd_dialog()` now (maybe) spawns/finds
`brokerd.<broker>` itself via `maybe_spawn_brokerd()` and
ONLY when a live trades-ep will actually be opened; the
paper-mode short-circuit never touches it, so post
feed-cutover paper sessions will run with zero `brokerd`
procs.

Pre-cutover this is a pure refactor: the registry lookup
just finds the same feed-spawned daemon.

Deats,
- new `open_brokerd_dialog()` sig: portal acquisition moves
  inside via an `acquire_live_portal()` helper; keep an
  explicit `portal: Portal|None` override for the
  `piker ledger` cli which boots its own ad-hoc actor.
- `Router.maybe_open_brokerd_dialog()` drops its `portal`
  param; `open_trade_relays()` drops the `feed.portals`
  lookup entirely.
- `.accounting.cli`: pass `portal=` by keyword.

(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 <noreply@anthropic.com>

Prompt-IO: ai/prompt-io/claude/20260610T171226Z_64181219_prompt_io.md
2026-06-10 13:12:59 -04:00
Gud Boi 6418121923 Add `datad` daemon machinery to `.data`
First half of the `brokerd` split: a new per-provider
data-feed-only daemon-actor `datad.<broker>` to (soon) host
all `validate._eps['datad']` eps (live quotes, history
loading, symbology search) leaving `brokerd` for live order
ctl only. Purely additive; nothing routes through it yet.

Deats,
- new `piker.data._daemon` mod mirroring the
  `.brokers._daemon` conventions (and the `samplerd`
  sub-daemon precedent):
  - `_setup_persistent_datad()` lifetime fixture owning the
    actor-global `_FeedsBus` alloc.
  - `datad_init()` building `enable_modules` from the
    backend's `_datad_mods` (falling back to
    `__enable_modules__` for not-yet-split backends) and
    copying `_spawn_kwargs` (critical for `ib`'s
    `infect_asyncio`).
  - `spawn_datad()`/`maybe_spawn_datad()` wrapping
    `Services` + `maybe_spawn_daemon()`.
- add `piker.data._daemon` to `_root_modules` so `pikerd`
  can run `spawn_datad()` requests.
- re-export the spawn eps from `piker.service`.
- add `test_datad_spawn` verifying actor boot + service
  registration via `ensure_service('datad.kraken')`.

Note the `Services`-based impl style deliberately mirrors
`spawn_brokerd()` so the eventual `tractor.hilevel`
`ServiceMngr` port (see the `service_mng_to_tractor`
branch's d8c21d44 prep work) lands symmetrically on both.

(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 <noreply@anthropic.com>

Prompt-IO: ai/prompt-io/claude/20260610T171142Z_119d2c04_prompt_io.md
2026-06-10 13:12:26 -04:00
Gud Boi 119d2c0495 Declare per-daemon-kind backend mod groups
Prep for the `brokerd` -> (`datad` + `brokerd`) actor split
by having each (split-style) backend declare which of its
submods host which daemon-kind's eps, exactly per the
`piker.data.validate._eps` groupings; `ib` already had
`_brokerd_mods`/`_datad_mods` so extend the convention to
`kraken`, `binance` and `deribit` (and add `'api'` to ib's
datad set since both kinds need the `Client` layer).

`__enable_modules__` stays as the (deduped) union so this
is a ZERO behavior change; flat backends (`kucoin` etc.)
just don't declare the split yet.

Also,
- add `validate.get_eps()` returning a backend's defined
  eps per daemon-kind for spawn-time introspection.
- import `NoBsWs`/`open_autorecon_ws` from
  `piker.data._web_bs` directly in `.kraken.broker` (they
  were only re-exported via `.kraken.feed`) so the trading
  mod doesn't depend on the feed mod for ws primitives.

(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 <noreply@anthropic.com>

Prompt-IO: ai/prompt-io/claude/20260610T171105Z_bc6e18d7_prompt_io.md
2026-06-10 13:11:42 -04:00
Gud Boi bc6e18d7b4 Port service+tests to latest `tractor` APIs
Continue the `repair_tests`-branch mission (already merged
in this stack's ancestry, see f4c4f1e2 which ported
`conftest.py`) by fixing the remaining drift breakage vs.
`tractor` git `main`; without these NOTHING boots since the
`tractor.Address` port in 604e5fcf.

Deats,
- normalize reg addrs via `wrap_address()` in
  `open_pikerd()` before `.unwrap()`-ing; entries may be
  raw `tuple`s when passed in from (test) client code.
- port `check_for_service()` to `query_actor(regaddr=)`
  (was `arbiter_sockaddr=`) incl. its 2-tuple yield and
  the now-required `open_registry(addrs=)` arg.
- `wait_for_actor(registry_addr=)` + `.chan.raddr.unwrap()`
  raw-tuple compares in `test_runtime_boot` and
  `ensure_service()`.
- update `run_test_w_cancel_method()` for modern `tractor`
  cancel semantics: self-requested sub-service cancels are
  absorbed (no `ContextCancelled` raised to the opener) and
  single-exc groups collapse to a bare KBI.
- `RemoteActorError.boxed_type` (was `.type`) and
  `Position.cumsize` (was `.size`) renames in tests.
- bump the paper-EMS startup budget 9 -> 19s; it includes
  a live (kraken) symbology fetch so needs net headroom.
- woops, add the missing comma in `.deribit.api`'s
  `tractor.trionics` import tuple..

(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 <noreply@anthropic.com>

Prompt-IO: ai/prompt-io/claude/20260610T171022Z_4485f2b9_prompt_io.md
2026-06-10 13:11:05 -04:00
Gud Boi 4485f2b9ce 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 <noreply@anthropic.com>

Prompt-IO: ai/prompt-io/claude/20260610T170859Z_75cefe10_prompt_io.md
2026-06-10 13:10:09 -04:00
Gud Boi 75cefe10f1 ib: attempt to REPL closed connection events 2026-06-09 14:03:41 -04:00
Gud Boi b547a33da4 ib: attempt to REPL closed connection events 2026-06-09 14:03:34 -04:00
Gud Boi 0df3943f3c Pin `tractor` to git `main`, bump `xonsh` deps
Switch the `tractor` dep from the local editable checkout
(`../tractor`) back to the upstream git `main` branch so a
fresh clone can `uv sync` without a sibling repo.

Also,
- add `xonsh>=0.23.8` as a core dep
- bump `repl` group pins: `xonsh>=0.23.0` and
  `prompt-toolkit>=3.0.50` (was `==3.0.40`)
- relock `uv.lock` accordingly

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-06-09 14:00:20 -04:00
Gud Boi 5466acb764 Add `tpt_bind_addrs` and separate registry eps
Thread a `tpt_bind_addrs` param through
`open_piker_runtime()` and `open_pikerd()` so
pikerd's bind addrs can differ from the registry
endpoint (support a dedicated `regd` service).
Simplify `load_trans_eps()` to delegate entirely
to `tractor.discovery.parse_endpoints()`.

Deats,
- `conf.toml`: fix maddr prefixes to proper `/ip4/` and `/unix/`, add
  `chart` endpoints section, add commented `regd` example.
- `cli/__init__.py`: replace `parse_maddr` with `parse_endpoints`,
  rename `--maddr` -> `--maddrs`, parse `regd` key from eps falling back
  to `pikerd` addrs.
- `_actor_runtime.py`: thread `tpt_bind_addrs` through runtime open fns
  to `open_root_actor`.
- `ui/cli.py`: activate `network` config parsing for chart CLI, extract
  `chart` eps for bind and `regd`/`pikerd` for registry, pass
  `tpt_bind_addrs` through runtime config.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-04-16 13:32:04 -04:00
Gud Boi 604e5fcf9c Use `tractor.Address` for endpoint resolution
Replace manual `layers['ipv4']['addr']` / `layers['tcp']['port']` tuple
extraction with direct `tractor.Address` objects returned from
`load_trans_eps()`. In `open_pikerd()` call `addr.unwrap()` for the
raw-tuple comparison against `root_actor.accept_addrs`.

Deats,
- `conf.toml`: update maddr prefix `/ipv4/` ->
  `/ipv/`, add commented UDS socket path example.
- `cli/__init__.py`: wrap endpoint loading in
  `maybe_open_crash_handler`, append `addr`
  objects directly to `regaddrs`.
- `ui/cli.py`: restructure `chart()` body into
  `maybe_open_crash_handler` scope, switch to
  `registry_addrs` from config, comment out the
  `network`-based `load_trans_eps` path (WIP
  `multiaddr` transition).
- `_actor_runtime.py`: use `addr.unwrap()` for
  accept-addr membership check.
- `uv.lock`: add `multiaddr >= 0.2.0` and its
  transitive deps.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-04-16 13:32:04 -04:00
Gud Boi 887e1ea6b7 Use walrus `getattr()` over `hasattr()` in `_window`
Replace all nested `hasattr()` + re-access chains
with `:= getattr(..., None)` walrus assigns
throughout the zoom UI methods; flattens deeply
nested `if hasattr` / `if hasattr` / `if hasattr`
pyramids into single chained `and` conditions.

Also,
- apply multiline code style per `py-codestyle`
  (list literals, fn sigs, `except` clauses,
  comments, docstrings)
- replace bare `pass` in `except` handlers with
  `log.exception()` calls
- fix `_qt_win` annotation to `QMainWindow|None`

(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-04-16 13:31:09 -04:00
Gud Boi 54da297304 Tighten logging and annotations in `_web_bs`
Split multi-value log msgs onto separate f-str
lines, add `!r` to URL and error format refs,
and fix `response_type` annotation from bare
`type` to `Type[Struct]`.

Also,
- Use `X|Y` union style (no spaces).
- Add `-> None` return hint to `proxy_msgs()`.
- Single backticks in `fixture` docstring ref.
- Expand `Callable` return type across lines.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-04-16 13:31:09 -04:00
Gud Boi 9f7c38a37b Use `ppfmt()` in EMS and guard `brokerd_msg` set
Replace all `pformat()` calls with `ppfmt()` from
`tractor.devx.pformat` and drop the `pprint`
import. Guard `status_msg.brokerd_msg = msg` with
an `if not` check to avoid clobbering a value
already set by earlier processing.

Also,
- Add `!r` to `broker` in a couple log msgs.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-04-16 13:31:09 -04:00
Gud Boi 8299c65818 Clean up `TooFastEdit` remnants and ws-token flow
Drop all commented-out `TooFastEdit` class,
`reg_err_types`, and `isinstance()` references.
Replace the hard ws-token `assert` in
`subscribe()` with a soft mismatch log that
updates the local `token` ref; cache the result
as `latest_token` for use in sub msgs.

Also,
- Comment out the `reg_err_types` import.
- Switch `pformat` -> `ppfmt` in `openOrders` update log.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-04-16 13:30:00 -04:00
Gud Boi 170c95da28 Fall back to `con.exchange` in IB ledger fill loop
Use `con.primaryExchange or con.exchange` so
`pexch` is populated even when `primaryExchange`
is empty (e.g. for certain combo/forex fills).

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-04-16 13:30:00 -04:00
Gud Boi e1cd3fd955 Replace `TooFastEdit` sentinel with `set` tracker
Drop the pattern of storing a `TooFastEdit` exc
instance in `reqids2txids` as a sentinel value;
instead track affected reqids in a dedicated
`toofastedit: set[int]` and check membership
via `reqid in toofastedit`.

Deats,
- Comment out `TooFastEdit` class and its `reg_err_types()` call.
- Add `toofastedit` param to both `handle_order_requests()` and
  `handle_order_updates()`, threaded from `open_trade_dialog()`.

Also,
- Use `partial()` with kwargs for the `tn.start_soon()` call to the
  order handler.
- Add `await tractor.pause()` on the too-fast edit path for runtime
  debugging; will remove once confident this all works.
- Expand comments explaining the cancel/edit race condition.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-04-16 13:29:55 -04:00
Gud Boi 8cefc1bdf8 Guard `brokerd_msg` set in order-mode dialog loop
Use `msg.setdefault('brokerd_msg', msg)` instead of blind assignment and
log a warning when the field was already populated.

Specifically, this avoids a self-reference field recursion which causes
crashes when using `tractor.devx.pformat.ppfmt()`..

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-04-15 18:54:33 -04:00
Gud Boi 709269fcf7 Cache ws-token on `Client` and auto-refresh
Add `_ws_token` cache attr to `Client` with a
`force_renewal` flag on `get_ws_token()`. Drop
the `token` param threading through
`handle_order_requests()` and
`handle_order_updates()` — all call sites now
use `await client.get_ws_token()` instead.

Deats,
- `api.py`: add `_ws_token: str|None = None`,
  return cached token unless `force_renewal`,
  comment out `InvalidKey`/`InvalidSession`
  classes and `reg_err_types()` call (WIP move).
- `broker.py`: drop `token` param from
  `handle_order_requests()`,
  `handle_order_updates()`, and call sites;
  replace all `token` refs with
  `await client.get_ws_token()`.
- `subscribe()`: rework `InvalidSession` handling
  to match on `(etype_str, ev_msg)` tuple, call
  `get_ws_token(force_renewal=True)` and
  `continue` the sub-ack loop; extract `fmt_msg`
  var to avoid repeated `ppfmt()` calls.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-04-14 14:29:26 -04:00
Gud Boi 5387538ba9 Add `InvalidSession` exc and ws-token refresh
Introduce `InvalidSession` for stale ws auth sessions (err-msg
'ESession:Invalid session') and factor the token-fetch into a new
`Client.get_ws_token()`. In `subscribe()`, dynamically resolve the exc
type from kraken's error-type str via `getattr()` on the `api` mod and
begin handling `InvalidSession` with a token refresh attempt.

Deats,
- `.kraken.api`: add `InvalidSession(RuntimeError)` with `subscription`
  attr, register it alongside `InvalidKey` in `reg_err_types()`, add
  `get_ws_token()` method.
- `.broker`: import `api` mod instead of individual names (`Client`,
  `BrokerError`), rework ws sub error handling to parse the kraken
  error-type prefix and resolve the matching exc class, add catch-all
  `case _:` for unknown ws events, pass `client` to `subscribe()`
  fixture, replace inline token fetch with `client.get_ws_token()`.

Also,
- Rename `nurse` -> `tn` for "task nursery" convention.
- Use `ppfmt()` for ws msg formatting.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-04-01 16:45:01 -04:00
Gud Boi d4dc8854e0 Use `tn` for nursery vars in UI modules
Rename `root_n` -> `tn` in `_app.py` and
`ln` -> `tn` in `_display.py` to match the `trio` nursery naming
convention used elsewhere. Drop a couple stray blank lines.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-04-01 14:05:08 -04:00
Gud Boi a0586f8219 Always re-raise in `maybe_spawn_daemon()` handler
Move bare `raise` outside the `if lock.owner` guard so the error
propagates regardless of whether the stale-lock branch runs. Also add
a blank-line separator in the crash log msg.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-31 19:43:27 -04:00
Gud Boi 923b4de296 Improve `load_accounts()` logging and defaults
Move `'paper'` default entry into the initial `bidict` instead of
appending post-loop. Add per-provider branch logging: an `info`-level
msg accumulating each loaded `account_alias` and a `debug`-level msg
(using `ppfmt()`) when a provider is skipped bc it wasn't requested.

Also,
- Early-`continue` when `accounts_section is None` instead of nesting
  inside an `else`.
- Import `ppfmt` from `tractor.devx.pformat`.
- Tighten union-type annotations to `X|Y` style.
- De-structure loop vars for readability.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-31 14:16:40 -04:00
Gud Boi ebc5bbd42b Fix kraken account-alias config mismatch
Rename `Client._name` -> `Client._key_descr` so the attr actually
describes what it holds (the `key_descr` field from `brokers.toml`). In
`open_trade_dialog()` look up the account-alias via `conf['accounts']`
and raise a `ConfigurationError` with a config-file example when no
matching entry exists.

Deats,
- `api.py`: rename `name` param/attr to `key_descr`, add docstring to
  `get_client()`, pull `conf['key_descr']` into a named local.
- `broker.py`: replace `acc_name` with `fqan` (fully-qualified account
  name), add accounts dict validation with actionable error msg.
- `brokers.toml`: add `src_fiat`, `accounts.spot` entry, and comments
  explaining the required field relationships.

(this commit-msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-31 14:02:32 -04:00
Tyler Goodlet 4cfe0a9dac Drop `.cancel_actor()` from `maybe_spawn_daemon()`
Since `tractor`'s new and improved inter-actor cancellation semantics
are much more pedantic, AND bc we use the `ServiceMngr` for spawning
service actors on-demand, the caller of `maybe_spawn_daemon()` should
NEVER conduct a so called "out of band" `Actor`-runtime cancel request
since this is precisely the job of our `ServiceMngr` XD

Add a super in depth note explaining the underlying issue and adding
a todo list of how we should prolly augment `tractor` to make such cases
easier to grok and fix in the future!
2026-03-30 20:06:43 -04:00
Gud Boi f210a478c6 Move `reg_err_types` imports to module-level
Hoist inline `from tractor._exceptions import reg_err_types` calls up to
the module-level import block across 5 files so they follow normal
import ordering.

Other,
- `kraken/broker.py`: same; also add `ConfigurationError` import and
  raise on missing `src_fiat` config field instead of `KeyError`.
- `storage/__init__.py`: same; also switch from relative to absolute
  `piker.*` imports and reorder the import block.
- comment out stray `await tractor.pause()` in binance `feed.py`.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-30 13:25:41 -04:00
Gud Boi 79be47635f Harden `.ib.venues` against unknown exchange cals
Deats,
- catch `InvalidCalendarName` in `has_holiday()` so
  venues without an `exchange_calendars` entry (eg.
  `IDEALPRO` for forex, `PAXOS` for crypto) gracefully
  return `False` instead of raising.
- add `log` via `get_logger()` to emit a warning when
  skipping the holiday check for an unmapped venue.
- fix `std_exch` type annot from `dict` -> `str`.
- guard `is_expired()` against empty
  `.realExpirationDate` strings.
- fill in `is_expired()` docstring.

(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-24 20:21:29 -04:00
Gud Boi 26aaec2e0c Add `ls` caps for claudy 2026-03-24 20:21:29 -04:00
Gud Boi 4bfe8d99b3 Swap `open_channel_from()` yield-pair order
Port deribit and IB `asyncio` bridge callables to the new
`to_asyncio.open_channel_from()` signature where the `LinkedTaskChannel`
is the first param and `started_nowait()` replaces the old
`to_trio.send_nowait()` sync handshake.

Deats,
- deribit `api.py`: update `aio_price_feed_relay()` and
  `aio_order_feed_relay()` signatures to take `chan: LinkedTaskChannel`
  as first arg; drop `from_trio`/`to_trio` params; replace
  `to_trio.send_nowait()` with `chan.send_nowait()` and
  `chan.started_nowait()`.
- drop `functools.partial()` wrapping in both `open_price_feed()` and
  `open_order_feed()`; pass `fh=`/`instrument=` as kwargs directly.
- IB `broker.py`: same `chan` + `started_nowait()` port for
  `recv_trade_updates()`.

Other styling,
- rewrap `recv_trade_updates()` docstring to 67 chars.

(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-24 20:21:29 -04:00
Gud Boi 611597ee18 Update `tractor` private-API refs across codebase
Port internal `tractor._<mod>` references to their
new public or reorganized paths after `tractor`
refactored its subpkg layout.

Deats,
- `tractor._portal.Portal` -> `tractor.Portal`.
- `tractor._supervise.ActorNursery` -> `tractor.ActorNursery`.
- `tractor._multiaddr` -> `tractor.discovery._multiaddr`.
- `tractor._addr` -> `tractor.discovery._addr`.
- `tractor._state._runtime_vars` -> `tractor.runtime._state._runtime_vars`.
- `tractor._state.is_debug_mode()` -> `tractor.runtime._state.is_debug_mode()`.

Files touched: `brokers/data.py`, `cli/__init__.py`, `data/feed.py`,
`service/_actor_runtime.py`, `service/_mngr.py`, `storage/cli.py`,
`tsp/_annotate.py`, `ui/kivy/monitor.py`, `ui/kivy/option_chain.py`.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-24 20:21:29 -04:00
Gud Boi 94ac2ee82a Use pre-set reg addrs in `maybe_spawn_daemon()`
Pull actor-runtime `registry_addrs` from (the new)
`tractor.get_runtime_vars()` (over the previous hardcoding of
`('127.0.0.1', 6116)`..)) so that underlying `find_service()` and
`maybe_open_pikerd()` calls use the local actor's assigned registrar
endpoints.

Note, this is particularly necessary to get the `pytest` harness workin
again alongside any local running `pikerd` instance(s).

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-24 20:21:29 -04:00
Gud Boi bf43036fe1 Add `MarketNotFound` exc and improve binance fqme error
Add a `MarketNotFound(SymbolNotFound)` subclass for
mkt-pair-specific lookup failures; use it in binance
`get_mkt_info()` with a detailed expected-form hint.

Deats,
- add `MarketNotFound` in `brokers/_util.py`.
- re-export from `brokers/__init__.py`.
- binance `feed.py`: swap `SymbolNotFound` import
  for `MarketNotFound`; build `expected` string
  showing the `<pair>.<venue>.<broker>` format
  and suggest `".spot."` if venue is missing.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-24 20:21:29 -04:00
Gud Boi 8424c368f6 Update feed test fqmes to use `.<venue>` format
Switch all `test_feeds.py` parametrized fqmes from
legacy `<pair>.<broker>` to the current
`<pair>.<venue>.<broker>` schema (e.g.
`btcusdt.spot.binance`).

Deats,
- update binance fqmes: `btcusdt.binance` ->
  `btcusdt.spot.binance`, same for `ethusdt`.
- update kraken fqmes: `ethusdt.kraken` ->
  `ethusdt.spot.kraken`, `xbtusd.kraken` ->
  `xbtusd.spot.kraken`.
- update cross-broker set similarly.
- comment out old fqmes with `!TODO` to later
  validate raising on bad/legacy formats.

Also,
- reformat `if ci_env and not run_in_ci` to
  multiline style.
- reformat `pytest.skip()` msg to multiline.
- add `?TODO` for symbology helper fn.
- drop stray `await tractor.pause()` in
  `conftest.py`.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-24 20:21:29 -04:00
Gud Boi 84c742a885 Register all custom excs with `tractor` IPC
Call `reg_err_types()` for every piker-defined
exception so they can be marshalled and re-raised
across actor boundaries.

Deats,
- `brokers/_util.py`: auto-register `BrokerError` +
  all `__subclasses__()` (6 types).
- `config.py`: `ConfigurationError` +
  `__subclasses__()` (`NoSignature`).
- `data/validate.py`: `FeedInitializationError`.
- `service/_ahab.py`: `DockerNotStarted`,
  `ApplicationLogError`.
- `service/marketstore.py`: `MarketStoreError`.
- `storage/__init__.py`: `TimeseriesNotFound`,
  `StorageConnectionError`.
- `brokers/kraken/api.py`: `InvalidKey`.
- `brokers/kraken/broker.py`: `TooFastEdit`.
- `brokers/questrade.py`: `QuestradeError`.

Also,
- uncomment `execution_venue` field on kraken `Pair`.

(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-24 20:21:29 -04:00
Gud Boi 7f8a198c98 Factor `Pair` schema-mismatch handling to `_util`
Add `get_or_raise_on_pair_schema_mismatch()` helper
and `SchemaMismatch` error type in `brokers._util`
to standardize the "provider changed their API" error
reporting across backends.

Deats,
- add `SchemaMismatch(BrokerError)` exc type.
- `get_or_raise_on_pair_schema_mismatch()`: catch
  `TypeError` on `Pair` ctor, build `ppfmt()`-ed
  report with provider name, fall back to
  `pair_type._api_url` if no explicit URL passed,
  then raise `SchemaMismatch`.
- binance `api.py`: replace inline `try/except` +
  `e.add_note()` with the new helper.
- kraken `api.py`: replace bare `Pair(...)` ctor
  with the new helper inside crash handler.

Also,
- add `_api_url: ClassVar[str]` to binance
  `FutesPair` and kraken `Pair` structs.
- binance `feed.py`: warn on missing `.<provider>`
  in fqme; raise `SymbolNotFound` on empty venue.
- reformat `start_dt`/`end_dt` unions to
  `datetime|None` style in binance `Client`.
- wrap binance `_pairs` lookup in
  `maybe_open_crash_handler()`.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-24 20:21:29 -04:00
Gud Boi de82df727d Add `is_expired()` and harden `.ib.venues` helpers
Add an expiry-date predicate and guard venue session
lookups against expired contracts and empty session
lists in `.ib.venues`; use in `api.py` to skip gap
detection for expired tracts.

Deats,
- add `is_expired()` predicate using
  `pendulum.parse()` on `realExpirationDate`.
- `sesh_times()`: raise `ValueError` if contract is
  expired or has no session intervals (instead of
  `StopIteration` from `next(iter(...))`).
- `is_venue_closure()`: handle `None` return from
  `sesh_times()` with guard + `breakpoint()`.

Also in `api.py`,
- import and call `is_expired()` from `.venues`.
- gate gap-detection on `not _is_expired`.
- default `timeZoneId` to `'EST'` when IB returns
  empty/`None`.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-24 20:21:29 -04:00
Gud Boi 5df511f7d2 Handle `[Errno ...]` str-errors in `.ib.broker`
Extend the str-type error code parser to also match
`[Errno <N>]` prefixed msgs (not just `[code <N>]`)
by iterating a list of prefix patterns and
`int()`-casting the extracted code on first match.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-24 20:21:29 -04:00
Gud Boi f4c4f1e2d5 Update `conftest.py` for `tractor` runtime API changes
Port test fixtures to match `tractor`'s updated
registry and channel APIs.

Deats,
- use `registry_addrs=[reg_addr]` (list) instead of
  `registry_addr=reg_addr` in `maybe_open_pikerd()`.
- `wait_for_actor()` now takes `registry_addr=`
  kwarg instead of `arbiter_sockaddr=`.
- access `portal.chan` (not `.channel`) and unwrap
  remote addr via `raddr.unwrap()`.
- yield `raddr._host`/`raddr._port` instead of
  tuple-indexing.
- drop random port generation; accept `reg_addr`
  fixture from `tractor`'s builtin pytest plugin.

Also,
- add `reg_addr: tuple` param to `open_test_pikerd()`
  fixture (sourced from `tractor._testing.pytest`).
- type-narrow `reg_addr` to `tuple[str, int|str]`.
- drop unused `import random`.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-24 20:21:29 -04:00
Gud Boi 24f2712d58 Gate `size_to_values()` on macOS in `_axes.py`
NOTE, this reversion was discovered as needed by @goodboy after
extensively manually testing the new zoom-by-font-size feats introduced
alongside macOS support.

Use class-body `if _friggin_macos:` branching to
conditionally define `size_to_values()` for both
`PriceAxis` and `DynamicDateAxis` — macOS gets the
new `_updateWidth()`/`_updateHeight()` + geometry
recalc path, other platforms fall back to the
original `setWidth()`/`setHeight()` calls.

Deats,
- add `platform` import and module-level
  `_friggin_macos: bool` flag.
- `PriceAxis.size_to_values()`: macOS branch calls
  `_updateWidth()` + `updateGeometry()`; else branch
  uses `self.setWidth(self.typical_br.width())`.
- `DynamicDateAxis.size_to_values()`: macOS branch
  calls `_updateHeight()` + `updateGeometry()`; else
  uses `self.setHeight(self.typical_br.height() + 1)`.
- reorder imports: `platform` before `typing`.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-24 20:21:29 -04:00
Gud Boi f9979956a0 EDITABLE `tractor` 2026-03-18 15:06:37 -04:00
Gud Boi 31859e9d03 Exclude crypto futes from `without_src` sym key
Extend the `col_sym_key` asset-type check in `start_backfill()`
to also exclude crypto-denominated futures (where `src` is
`'crypto_currency'` and `dst` is `'future'`) from the
`without_src=True` fqme path.

Also in `.brokers.binance` backend (it being the guilty culprit in the
discovery of this bug; and why i touched styling this code),

- reformat `make_sub()` fn sig to multiline style in
  `.binance.feed`.
- add backtick around `dict` in `make_sub()` docstring.
- reformat `or` conditionals to multiline style in
  `.binance.feed.get_mkt_info()`.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-17 21:15:31 -04:00
Gud Boi e30957b62f Drop `Flume.feed`, it's unused yet causes import cycles.. 2026-03-17 21:15:31 -04:00
Gud Boi b87710e999 Just warn on single-bar nulls instead of bping
Replace the debug breakpoint with a warning-log when a single-bar
null-segment is detected in `get_null_segs()`. This lets the gap
analysis continue while still alerting about the anomaly.

Deats,
- extract the 3-bar window (before, null, after) and calculate
  a `gap: pendulum.Interval` for the warning msg.
- comment-out the old breakpoint block for optional debugging as needed.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-17 21:15:31 -04:00
Gud Boi 392ea6162a Lul, drop long unused poetry lock file 2026-03-17 21:15:31 -04:00
Gud Boi d98c1706d6 Pin `pg` at latest official `0.14.0` release
Keep in masked GH sources lines for easy hackin against upstream
`master` branch when needed as well!
2026-03-17 21:15:31 -04:00
Gud Boi 88d309a613 .ui._editors: log multiline styling and re-leveling 2026-03-17 21:15:31 -04:00
Gud Boi 87707e4239 .ui._lines: drop unused graphics-item import 2026-03-17 21:15:31 -04:00
Gud Boi 073176a4c2 Add batch-submit API for gap annotations
Introduce `AnnotCtl.add_batch()` and `serve_rc_annots()` batch
handler to submit 1000s of gaps in single IPC msg instead of
per-annot round-trips. Server builds `GapAnnotations` from specs
and handles vectorized timestamp-to-index lookups.

Deats,
- add `'cmd': 'batch'` handler in `serve_rc_annots()`
- vectorized timestamp lookup via `np.searchsorted()` + masking
- build `gap_specs: list[dict]` from rect+arrow specs client-side
- create single `GapAnnotations` item for all gaps server-side
- handle `GapAnnotations.reposition()` in redraw handler
- add profiling to batch path for perf measurement
- support optional individual arrows for A/B comparison

Also,
- refactor `markup_gaps()` to collect specs + single batch call
- add `no_qt_updates()` context mgr for batch render ops
- add profiling to annotation teardown path
- add `GapAnnotations` case to `rm_annot()` match block

(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-17 21:15:31 -04:00
Gud Boi 8c08ddd38c Add a `GapAnnotations` path-renderer
For a ~1000x perf gain says ol' claudy, our boi who wrote this entire
patch! Bo

Introduce `GapAnnotations` in `.ui._annotate` for batch-rendering gap
rects/arrows instead of individual `QGraphicsItem` instances. Uses
upstream's `pyqtgraph.Qt.internals.PrimitiveArray` for rects and
a `QPainterPath` for arrows. This API-replicates our prior annotator's
in view shape-graphics but now using (what we're dubbing)
"single-array-multiple-graphics" tech much like our `.ui._curve`
extensions to `pg` B)

Impl deats,
- batch draw ~1000 gaps in single paint call vs 1000 items
- arrows render in scene coords to maintain pixel size on zoom
- add vectorized timestamp-to-index lookup for repositioning
- cache bounding rect, rebuild on `reposition()` calls
- match `SelectRect` + `ArrowItem` visual style/colors
- skip reposition when timeframe doesn't match gap's period

Other,
- fix typo in `LevelMarker` docstring: "graphich" -> "graphic"
- reflow docstring in `qgo_draw_markers()` to 67 char limit

(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-17 21:15:31 -04:00
Gud Boi b4c905b592 Add info log for shm processing in `ldshm` CLI cmd
Log shm file name and detected period before null segment
processing to aid debugging.

(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-17 21:15:31 -04:00
Gud Boi 494bc4ce85 Bump to latest official `pyqtgraph` release 2026-03-17 21:15:31 -04:00
Gud Boi ce8ad59f7c Improve styling and logging for UI font-size zoom
Refine zoom methods in `MainWindow` and font helpers
in `_style` to return `px_size` up the call chain and
log detailed zoom state on each change.

Deats,
- make `_set_qfont_px_size()` return `self.px_size`.
- make `configure_to_dpi()` and `_config_fonts_to_screen()`
  return the new `px_size` up through the call chain.
- add `font_size` to `log.info()` in `zoom_in()`,
  `zoom_out()`, and `reset_zoom()` alongside
  `zoom_step` and `zoom_level(%)`.
- reformat `has_ctrl`/`_has_shift` bitwise checks and
  key-match tuples to multiline style.
- comment out `Shift` modifier requirement for zoom
  hotkeys (now `Ctrl`-only).
- comment out unused `mn_dpi` and `dpi` locals.

Also,
- convert all single-line docstrings to `'''` multiline
  style across zoom and font methods.
- rewrap `configure_to_dpi()` docstring to 67 chars.
- move `from . import _style` to module-level import
  in `_window.py`.
- drop unused `screen` binding in `boundingRect()`.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-17 21:15:19 -04:00
di1ara 2e42d7e1c4 improve ui zoom defaults 2026-03-17 21:15:19 -04:00
Gud Boi 17e7232d12 Fix chart axis scaling on UI zoom level change
Again a patch (vibed) from our very own @dnks
(just a commit msg reworking using his new `/commit-msg` skill added by
@goodboy B)

Deats,
- add `Axis.update_fonts()` to recalculate tick font, text offset,
  bounding rect and `pyqtgraph`'s internal text-width/height tracking
  after a zoom change; store `_typical_max_str` at init for later reuse.
- rework `PriceAxis.size_to_values()` and
  `DynamicDateAxis.size_to_values()` to use pyqtgraph's
  `_updateWidth()`/`_updateHeight()` with `updateGeometry()` instead of
  raw `setWidth()`/ `setHeight()` so auto-expand constraints are
  respected.
- fix `GlobalZoomEventFilter` to mask out `KeypadModifier` and
  explicitly require both Ctrl+Shift, letting plain Ctrl+Plus/Minus pass
  through to chart zoom.
- add `_update_chart_axes()` to walk all plot-item axes during
  `_apply_zoom()` and call `splits.resize_sidepanes()` to sync subplot
  widths.

(this commit msg, and likely patch, was generated in some part by
[`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-17 21:15:19 -04:00
Gud Boi 88f66baeed Add global UI font-size zoom scaling (from @dnks)
Add `Ctrl+Shift+Plus/Minus/0` shortcuts for zooming all
UI widget font sizes via a `GlobalZoomEventFilter`
installed at the `QApplication` level.

Deats,
- `.ui._window`: add `GlobalZoomEventFilter` event
  filter class and `MainWindow.zoom_in/out/reset_zoom()`
  methods that reconfigure `DpiAwareFont` with a
  `zoom_level` multiplier then propagate to all child
  widgets.
- `.ui._style`: extend `DpiAwareFont.configure_to_dpi()`
  and `_config_fonts_to_screen()` to accept a
  `zoom_level` float multiplier; cast `px_size` to `int`.
- `.ui._forms`: add `update_fonts()` to `Edit`,
  `Selection`, `FieldsForm` and `FillStatusBar` for
  stylesheet regen.
- `.ui._label`: add `FormatLabel.update_font()` method.
- `.ui._position`: add `SettingsPane.update_fonts()`.
- `.ui._search`: add `update_fonts()` to `CompleterView`
  and `SearchWidget`.
- `.ui._exec`: install the zoom filter on window show.
- `.ui.qt`: import `QObject` from `PyQt6`.

(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-17 21:15:19 -04:00
Tyler Goodlet 3f77180b63 Add `.xsh` script mentioned in gitea #50
Note since it's actually `xonsh` code run with either,
- most pedantically: `xonsh ./snippets/calc_ppi.xsh`
- or relying on how shebang: `./snippets/calc_ppi.xsh`
  * an sheboom.
2026-03-17 21:15:19 -04:00
Tyler Goodlet ccc5a745de Reorder imports in `qt_screen_info.py` ??
For wtv reason on nixos importing `pyqtgraph` first is causing `numpy`
to fail import?? No idea, but likely something to do with recent
`flake.nix`'s ld-lib-linking with `<nixpkgs>` marlarky?
2026-03-17 21:15:19 -04:00
Tyler Goodlet 5b1c80a8a5 Add some Qt DPI extras to `qt_screen_info.py`
- set `QT_USE_PHYSICAL_DPI='1'` env var for Qt6 high-DPI
  * we likely want to do this in `piker.ui` as well!
- move `pxr` calc from widget to per-screen in loop.
- add `unscaled_size` calc using `pxr * size`.
- switch from `.availableGeometry()` to `.geometry()` for full
  bounds.
- shorten output labels, add `!r` repr formatting
- add Qt6 DPI rounding policy TODO with doc links

(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2026-03-17 21:15:19 -04:00
Tyler Goodlet e600d61eef Re-fmt and `.info()` the `.configure_to_dpi()` DPI calcs for now 2026-03-17 21:15:19 -04:00
di1ara 5c7f00a3b8 fixed spacing 2026-03-17 21:15:19 -04:00
di1ara 3b0c27ec44 fixed pytest test for dpi font auto calculation 2026-03-17 21:15:19 -04:00
di1ara 574fb80d79 added pytest, moved dependencies 2026-03-17 21:15:19 -04:00
di1ara cdb0e8411a fix DpiAwareFont default size calculation 2026-03-17 21:15:19 -04:00
225 changed files with 23129 additions and 2972 deletions

View File

@ -0,0 +1,174 @@
---
name: piker-clearing-expert
description: >
Piker clearing, EMS/order-control, accounting, position, and
trade-ledger expertise. Apply when changing or debugging Order,
Status, Brokerd* protocols, live or paper fills, dark orders,
order IDs, TransactionLedger persistence, Account/Position/PPU,
FQME/bs_mktid identity, SymbologyCache lookups, or broker trade
normalization.
compatibility: >
Requires a piker checkout and familiarity with Python, trio,
tractor, msgspec Structs, and backend-specific trading APIs.
metadata:
author: goodboy
version: "1.0"
---
# Piker Clearing Expert
Use this mental model before touching order control or accounting:
```text
OrderClient / UI
-> emsd routes intent and owns order-dialog state
-> live brokerd OR paperboi executes and reports events
-> executing backend owns fill-to-accounting integration
-> emsd translates and broadcasts broker events
-> UI consumes order state and position summaries
```
The EMS is not the trade ledger. It correlates IDs, runs dark
predicates, and relays lifecycle events. Paper and accounting-aware
live backends may normalize fills into transactions, update local
ledger/account state, and emit `BrokerdPosition` summaries. Inspect
each live backend; this integration is not uniform.
## Runtime ownership
- `OrderClient` owns client intent and local sent-order tracking.
- `emsd` owns `Router`, `DarkBook`, active `Status` dialogs,
subscribers, and `oid <-> reqid` correlation.
- `brokerd.<broker>` owns credentialed live order control.
- `paperboi.<broker>` owns simulated execution and paper accounting.
- `TransactionLedger` owns trade-record persistence, not order state.
- `Account` owns positions keyed by backend-system market identity.
- UI `Position.update_from_msg()` is a summary reset, not durable
transaction accounting.
For daemon placement, context streams, cancellation, and actor-local
state, also load `piker-conc-expert`.
## Event lifecycle
1. A client sends `Order` or `Cancel` through `OrderClient`.
2. `process_client_order_cmds()` creates or updates EMS `Status`.
3. EMS routes a `BrokerdOrder` or stores a dark trigger predicate.
4. The executing backend acknowledges with `BrokerdOrderAck`.
5. EMS records the current `oid <-> reqid` relation.
6. Accepted, ack-correlated status/fill events become client updates.
7. The backend integrates fills into accounting when supported.
8. A resulting `BrokerdPosition` is relayed through EMS to clients.
Cancellation may arrive before acknowledgement. Preserve
`Status.cancel_called`; once the ack supplies `reqid`, EMS can issue
the deferred `BrokerdCancel`.
## Identifier namespaces
Never substitute one namespace merely because two values happen to
match in a backend:
| ID | Owner and invariant |
|---|---|
| `oid` | Stable client/EMS order-dialog ID across submit, modify, cancel |
| `reqid` | Backend order-control ID established by an ack; it may change |
| venue order ID | Native API identity; may differ from both IDs above |
| `tid` | Stable clear ID used to deduplicate position events and TOML keys |
| `fqme` | Piker-normalized market address and symcache lookup key |
| `bs_fqme` | FQME without its final broker suffix |
| `bs_mktid` | Backend-system market key and `Account.pps` key |
Normalize every durably persisted `tid` to a stable string. Although
`Transaction.tid` permits integers, TOML mapping keys do not.
Paper transactions commonly set `Transaction.bs_mktid` to the full
FQME. The embedded `Position.mkt.bs_mktid` can still retain the
provider-native market ID. Check the concrete object and table owner
before asserting key equality.
## Accounting invariants
- `Transaction.size` is signed; preserve side semantics during
broker normalization.
- `Transaction` is the normalized in-memory interchange. Live ledger
records may remain backend-native and normalize through
`mod.norm_trade()` when read.
- `Position._events` is keyed by `tid`; `add_clear()` is the
idempotence boundary.
- `Position.ppu` and `Position.cumsize` are derived from transaction
events, not independent mutable truth.
- Normalized accounting transactions need a stable, non-null
`bs_mktid`; `Account.pps` uses it as the position key.
- `Account.update_from_ledger()` resolves market metadata from
`SymbologyCache.mktmaps` or its explicit fallback table.
- UI position messages are projections. Do not feed their synthetic
reset event back into durable broker accounting.
## Persistence contracts
Trade ledgers live under the accounting ledger directory as
`trades_<broker>_<account>.toml`. Top-level keys are transaction IDs;
record schemas are backend-specific unless the paper engine wrote a
normalized transaction dictionary.
`open_trade_ledger()` writes on context exit when its data comparison
detects a change or `rewrite=True`. Its snapshot is shallow, so nested
in-place record mutation can evade dirty detection. Explicit writes can
also fail inside fill handlers. Treat all persistence points as part of
the live execution path.
Accounts persist active positions. `dump_active()` separates open and
net-zero positions. Do not assume `minimized_clears()` safely preserves
size/PPU: current sign-transition and longer net-zero slicing lacks
round-trip coverage and can retain the wrong event subset.
## Market identity and symcache
`SymbologyCache` has three distinct key-addressed mappings:
- `assets`: provider asset ID to `Asset`;
- `pairs`: native `bs_mktid` to backend pair `Struct`;
- `mktmaps`: searchable FQME or explicit alias to normalized
`MktPair`.
Search mapping keys, then resolve values. RapidFuzz treats mapping
values as choices; passing `mktmaps` directly makes it operate on
`MktPair` objects. Preserve in-memory aliases because IB can store both
native IDs and FQMEs for one `MktPair`. Do not persist such aliases
without fixing reload: `from_dict()` currently requires every key to
equal `mkt.fqme`.
## Change workflow
Before editing:
1. Identify the owning layer: client, EMS, executor, accounting, or UI.
2. Write down every ID/key transition across that boundary.
3. Trace both normal and cancel-before-ack event ordering.
4. Determine whether records are native, normalized, or summaries.
5. Check actor-local caches and context-exit writes.
6. Add a deterministic regression at the narrowest broken contract.
7. Run live/backend tests only when their credentials and side effects
are explicitly authorized.
Do not fix an accounting defect by adding state to EMS, or fix an EMS
correlation defect by rewriting durable transaction history.
## Canonical source map
- `piker/clearing/_messages.py`: client and brokerd protocol types.
- `piker/clearing/_client.py`: `OrderClient` and `open_ems()`.
- `piker/clearing/_ems.py`: router, dark book, ID mapping, translation.
- `piker/clearing/_paper_engine.py`: simulated execution/accounting.
- `piker/accounting/_ledger.py`: transactions and ledger persistence.
- `piker/accounting/_pos.py`: positions, accounts, PPU event state.
- `piker/accounting/_mktinfo.py`: `Asset`, `MktPair`, FQME schema.
- `piker/data/_symcache.py`: provider symbology mappings and search.
- `piker/accounting/calc.py`: transaction ordering and PPU/PnL math.
- `piker/brokers/*/broker.py`: backend order-control integration.
- `piker/brokers/*/ledger.py`: backend trade normalization.
See [architecture.md](architecture.md) for complete flows,
[gotchas.md](gotchas.md) for symptom-driven diagnosis, and
[test-map.md](test-map.md) for verification choices.

View File

@ -0,0 +1,210 @@
# Clearing And Accounting Architecture
## Protocol direction
Client-facing messages and broker-facing messages intentionally differ.
```text
client -> EMS
Order, Cancel
EMS -> executor
BrokerdOrder, BrokerdCancel
executor -> EMS
BrokerdOrderAck, BrokerdStatus, BrokerdFill,
BrokerdError, BrokerdPosition
EMS -> subscribed clients
Status, BrokerdPosition
```
`Order` carries the client `oid`, account, market, price, size, action,
and execution mode. `BrokerdOrderAck` establishes the executor's
`reqid`; subsequent broker messages should carry that ID so EMS can
resolve the client dialog.
`Status` is mutable current dialog state, not a history envelope. Exact
client states include `pending`, `open`, `dark_open`, `triggered`,
`fill`, `closed`, `canceled`, and `error`. Audit existing translation
before adding a state; backend `BrokerdStatus` vocabulary is narrower
and current producers are not fully conformant.
## EMS routing
`_emsd_main()` serves each client/market context. The actor-local
`Router` owns books, subscribers, order dialogs, and cached live trades
relays. Paper mode opens per-symbol relay contexts under the same broker
key, a current overwrite/pop hazard for concurrent paper symbols.
`open_brokerd_dialog()` is the session live-versus-paper selection
point:
- live execution talks to `brokerd.<broker>`;
- paper execution talks to `paperboi.<broker>`;
- chart-only and paper sessions should not boot credentialed brokerd.
Do not infer paper execution from each order's `exec_mode`. Session
mode selects the executor; per-order mode primarily distinguishes dark
from immediately routed orders, despite incomplete literal annotations.
`process_client_order_cmds()` handles submit, modify, cancel, dark,
and alert intent. It updates EMS dialog state before routing broker
commands. `translate_and_relay_brokerd_events()` applies acks, statuses,
fills, errors, and positions to dialogs and subscribers.
The `DarkBook` stores local predicates and watches quote ticks. When a
predicate triggers, `clear_dark_triggers()` emits the live broker order
and updates the existing client dialog rather than inventing a second
order identity.
## Ack and cancel ordering
Required actionable ordering is submit, ack, open, zero or more fills,
then terminal status. EMS handles cancel-before-ack, but it does not
buffer arbitrary status-before-ack events; those currently fall through
as unhandled. Backends must ack before actionable statuses.
The important race is:
```text
client Cancel(oid)
-> EMS has no reqid yet
-> mark Status.cancel_called
BrokerdOrderAck(oid, reqid)
-> install mapping
-> send deferred BrokerdCancel(reqid)
```
Do not drop a pre-ack cancel and do not send a broker cancel with an
`oid` unless the backend explicitly uses that value as its `reqid`.
## Paper execution
`PaperBoi` maintains simulated orders, opens the market feed, and fills
orders when L1 ask/bid quotes cross their prices. `trade`/`last` ticks
zip buy and sell iterators, so a lone resting side currently may not
fill from those tick types. `fake_fill()` is the core fill-to-account
path:
1. emit `BrokerdFill`;
2. emit terminal `BrokerdStatus` for the simulated order;
3. construct a signed normalized `Transaction`;
4. `TransactionLedger.update_from_t()`;
5. `Account.update_from_ledger()`;
6. persist the ledger and account;
7. emit `BrokerdPosition` with current size and PPU.
The protocol notifications and persistence occur in one task but are
not a database transaction. If persistence fails after fill/status
publication, restart/reconciliation behavior matters. Do not reorder
the sequence casually.
Partial fills currently reuse `oid` as `tid`. A later partial therefore
overwrites the paper ledger record and is rejected by position-event
dedupe. Fix unique fill IDs before relying on paper partial-fill math.
`open_trade_dialog()` opens symcache, paper ledger, account, and feed
resources under structured concurrency. An exception from any nested
resource, including ledger context exit, cancels the dialog and its feed
stream.
## Live backend variation
There is no universal live-broker accounting implementation:
- IB normalizes execution/commission events, updates ledger/account
state, and emits position updates.
- Kraken has backend trade normalization and position reconstruction,
but its live path writes account state without inserting every new
trade into `TransactionLedger`.
- Binance currently relays venue order/position events and does not
share every local `TransactionLedger`/`Account` path.
Inspect the backend's `broker.py`, `ledger.py`, and `norm_trade()` before
moving generic logic. A paper behavior is not automatically a live
backend contract.
## Ledger normalization
`TransactionLedger.data` is the durable record mapping. For paper it
contains transaction-like dictionaries. For live brokers it can contain
raw provider records.
`iter_txns()` sorts records and chooses normalization:
- paper uses `_paper_engine.norm_trade()`;
- live accounts use `brokermod.norm_trade()`.
Normalizer output should carry datetime, signed size, price, cost,
FQME, backend market ID, and transaction ID. Some normalizers return
`None` for unusable records; `iter_txns()` does not filter it, while
`to_txns()` does. Audit direct iterator consumers.
`write_config()` sorts records for stable persistence. Paper rewrite
logic migrates old symbol fields and qualifies legacy FQMEs through the
symcache. Search can return no matches; callers which select the first
result need an explicit fallback or a descriptive failure policy.
## Position event model
`Position` holds a market and a transaction-event mapping. New clears
are deduplicated by `tid`. Current size and PPU are recomputed from the
event sequence.
`Account.update_from_ledger()`:
1. receives normalized transactions;
2. resolves each market through `mktmaps` or a fallback table;
3. looks up or creates the position by `Transaction.bs_mktid`;
4. adds unseen clears;
5. returns visited positions, including duplicates whose clear was
rejected by `add_clear()`.
`minimized_clears()` intends to discard irrelevant history after
net-zero or sign transitions, but current slicing is not proven safe
and can reconstruct the wrong active size/PPU. Treat it as a known bug
surface until sign-transition round trips are covered.
`Position.update_from_msg()` serves UI summaries by replacing local
event state with a synthetic clear representing reported size and PPU.
That projection is intentionally lossy.
## Symbology contracts
`MktPair.fqme` is a normalized endpoint address assembled from pair,
venue, expiry/contract information, and broker. `MktPair.bs_fqme`
drops the broker suffix. `MktPair.bs_mktid` is the provider-native
market identity.
`SymbologyCache.load()` attempts to obtain provider assets and pair
structs, calls `get_mkt_info()`, and populates conventional mappings.
Assets are optional; missing/empty pair support returns early.
```text
assets[provider_asset_id] -> Asset
pairs[pair.bs_mktid] -> backend Pair Struct
mktmaps[mkt.fqme] -> MktPair
```
Some backends add aliases such as `mktmaps[bs_mktid]`. Search must
return the matched mapping key rather than rebuilding a key from the
value, otherwise aliases silently collapse. Current serialization then
writes aliases which reload rejects because `from_dict()` asserts each
key equals `mkt.fqme`.
## Persistence boundaries
`open_trade_ledger()` loads account TOML and compares a shallow snapshot
in `finally`. Top-level additions/replacements trigger writes; nested
in-place mutation may not. `open_account()` similarly mediates account
state. These contexts run inside actor tasks, so serialization errors
are runtime failures, not merely offline maintenance failures.
When changing schemas:
- preserve backend-native records unless migration is explicit;
- keep old paper field migration deterministic;
- avoid fuzzy qualification when an exact `bs_mktid`/FQME exists;
- test context exit, not only in-memory updates;
- inspect emitted positions after disk round trips.

View File

@ -0,0 +1,146 @@
# Clearing And Ledger Gotchas
## Trade dialog dies immediately after a fill
Likely cause: an explicit ledger/account write in the fill handler, or
a later context-exit write, raised after event publication.
Inspect, in order:
1. the first non-cancellation exception in the actor traceback;
2. `PaperBoi.fake_fill()` or the live backend fill handler;
3. explicit and context-exit `write_config()` calls;
4. FQME/`bs_mktid` qualification and TOML-compatible values;
5. the persisted ledger/account files for a partial update.
Tractor cancellation logs are often the consequence. Root-cause the
inner serialization, lookup, or normalization exception first.
## `MktPair` has no `len()` in RapidFuzz
Cause: a mapping of string keys to `MktPair` values was passed directly
to `rapidfuzz.process.extract()`. Mapping values become fuzzy choices.
Fix: search a list/view of string keys and resolve matched keys back
through the original mapping. Do not add sequence methods to `MktPair`
and do not use `processor=str` when aliases must be preserved.
## Fuzzy qualification raises `IndexError`
Cause: the caller selected the first match from an empty result after
the score cutoff rejected every key.
Decide explicitly whether to retain the input identity, reject the
record with a descriptive exception, or obtain missing market metadata.
Do not lower the cutoff blindly; the wrong market corrupts accounting.
## Cancel is ignored or sent with the wrong ID
Cause: confusion between client `oid` and backend `reqid`, commonly
during cancel-before-ack ordering.
Verify the ack installed both mapping directions and that
`Status.cancel_called` survives until `reqid` exists. Log both IDs and
the actor/backend account at every transition.
## Fills appear but positions do not move
Check these boundaries:
- the backend emitted `BrokerdFill` but never normalized a transaction;
- transaction size lost its buy/sell sign;
- `tid` collided with an existing position event;
- `Transaction.bs_mktid` selected a different `Account.pps` key;
- `mktmaps` lacked the exact transaction identity;
- position publication happened before accounting update;
- the backend intentionally reports venue positions instead of local
ledger-derived positions.
## Duplicate or inflated position size
Do not deduplicate by price/time alone. Confirm stable transaction IDs,
chronological sorting, and `Position.add_clear()` idempotence. Broker
execution IDs and order IDs are not interchangeable.
For partial fills, several `tid`s may correctly share one order
`reqid`. Collapsing them loses clears; replaying one `tid` twice
inflates nothing only if the dedupe boundary is preserved.
Current paper fills use `tid=oid`, so later partials overwrite the
ledger record and are deduplicated out of the position. Treat paper
partial fills as broken until each clear receives a stable unique ID.
## Paper and live state disagree
Paper uses normalized local transactions and can use the full FQME as
its account position key. A live backend may use native IDs, raw ledger
records, or venue-reported positions.
Compare normalized `Transaction`s and emitted `BrokerdPosition`s, not
raw TOML dictionaries across backends.
## Account appears under the wrong name
Separate UI/EMS account aliases from backend account IDs and ledger file
names. Backends may prefix account names for routing while persistence
uses an unprefixed native account key.
Trace account identity through `open_ems()`, broker dialog startup,
ledger opening, and `BrokerdPosition.account` before changing naming.
## A module-global cache is unexpectedly empty
Symcache, clients, contracts, and order tables are actor-local. Data
loaded in `datad` is not automatically available in `brokerd` or
`paperboi`. Also, `_symcache._caches` is currently read but never
populated, so do not assume repeated `open_symcache()` calls hit it.
Load `piker-conc-expert` and audit actor-local globals, async caches,
and dialog startup warming. Never rely on an import side effect from a
sibling actor.
## Teardown emits a cancellation storm
Find the first application exception. Structured cancellation then
closes feed streams, broker streams, and nested contexts. Avoid masking
the originating exception with broad cancellation handling.
For stream ownership and context cancellation semantics, use
`piker-conc-expert`.
## Broker status is logged as unhandled
EMS currently accepts only ack-correlated `open`, `closed`, and
`canceled` broker statuses. Declared `pending` and backend-produced
`fill`/`filled` variants fall through, as do statuses arriving before
ack. Verify producer vocabulary and ack ordering before adding another
consumer branch.
## Another client sees order or position events
Current translation broadcasts order updates by FQME and positions to
all attached clients; `Router.dialogs` is not used as an event privacy
filter. Treat subscriber isolation as incomplete, not guaranteed.
## Debug breadcrumb
Capture one row per event with:
```text
actor, backend, account, fqme, bs_mktid,
oid, reqid, venue_order_id, tid,
message_type, status, action, signed_size, price, timestamp
```
Then compare:
1. client command order;
2. EMS status history;
3. broker event order;
4. normalized transactions;
5. position event IDs and computed size/PPU;
6. ledger/account disk state;
7. emitted position summaries.
This separates protocol loss, identity skew, accounting math, and
persistence failure without guessing from UI state.

View File

@ -0,0 +1,84 @@
# Clearing And Accounting Test Map
## Deterministic first-pass tests
Use the narrowest test that owns the broken contract:
| Area | First target |
|---|---|
| Ledger/account persistence | targeted node in `tests/test_accounting.py` |
| EMS bad-backend handling | `tests/test_ems.py::test_ems_err_on_bad_broker` |
| IB request/response routing | `tests/test_ib_method_proxy.py` |
| IB history normalization | `tests/test_ib_history.py` |
| Service/actor startup | targeted node in `tests/test_services.py` |
Known missing high-value regressions include paper partial-fill IDs,
status-before-ack handling, `minimized_clears()` sign-transition round
trips, nested ledger dirty detection, and symcache alias reload.
For `SymbologyCache.search()` and paper ledger qualification, cover:
- real `Asset` and `MktPair` values;
- a native-ID alias and canonical FQME key;
- the actual `TransactionLedger.write_config()` path;
- persisted TOML fields after the round trip;
- no use of live APIs or user configuration.
## Side-effectful tests
Do not run these without explicit authorization and the repository test
harness guidance:
- paper EMS tests which open live market/symbology feeds;
- live Binance/Kraken feed suites;
- IB account tests using configured accounts;
- tests which write tracked ledger/account fixtures;
- broker dialogs requiring credentials or venue connectivity.
After any accounting test using `tests/_inputs`, inspect its diff. Some
context managers can rewrite fixture ledgers on exit.
## Regression design
Prefer a direct contract regression over a full actor test when the
failure is synchronous and deterministic. Use an actor-level test when
the bug depends on:
- ack/cancel or fill/status interleaving;
- context cancellation or stream ownership;
- actor-local state/cache separation;
- daemon selection or startup;
- publication ordering across EMS and broker streams.
Every regression should document the original failure, triggering
state, violated invariant, arrangement, and proof of the fix.
## Verification layers
1. `git diff --cached --check` for the exact staged boundary.
2. Ruff with the repository's intended rule profile.
3. A deterministic unit/filesystem regression.
4. Adjacent accounting or EMS tests which do not require live state.
5. Explicitly authorized backend/actor integration tests.
Do not interpret a passing ellipsis-body or hard-skipped test as
behavioral coverage. Read the selected node before using it as a gate.
## Manual reconciliation check
For production-only failures, compare one fill end to end:
```text
BrokerdFill
-> normalized Transaction
-> ledger record
-> Position._events[tid]
-> Position.cumsize / ppu
-> account TOML
-> BrokerdPosition
-> UI summary
```
All identities, sign, price, and account values should remain explainable
at each arrow. A mismatch identifies the owning boundary for the next
focused test.

View File

@ -0,0 +1 @@
../../../../../../taken/wkts/federated_project_stores/.agents/skills/taken/SKILL.md

View File

@ -0,0 +1 @@
../../../../../../../taken/wkts/federated_project_stores/.agents/skills/taken/agents/openai.yaml

View File

@ -0,0 +1 @@
../../../../../../../taken/wkts/federated_project_stores/.agents/skills/taken/references/agents.md

View File

@ -0,0 +1 @@
../../../../../../../taken/wkts/federated_project_stores/.agents/skills/taken/references/writes.md

View File

@ -0,0 +1 @@
{"version": 1, "backend": "legacy"}

View File

@ -3,7 +3,8 @@
"allow": [ "allow": [
"Bash(chmod:*)", "Bash(chmod:*)",
"Bash(/tmp/piker_commits.txt)", "Bash(/tmp/piker_commits.txt)",
"Bash(python:*)" "Bash(python:*)",
"Bash(ls:*)"
], ],
"deny": [], "deny": [],
"ask": [] "ask": []

View File

@ -1,84 +0,0 @@
---
name: commit-msg
description: >
Generate piker-style git commit messages from
staged changes or prompt input, following the
style guide learned from 500 repo commits.
argument-hint: "[optional-scope-or-description]"
disable-model-invocation: true
allowed-tools: Bash(git *), Read, Grep, Glob, Write
---
## Current staged changes
!`git diff --staged --stat`
## Recent commit style reference
!`git log --oneline -10`
# Piker Git Commit Message Generator
Generate a commit message from the staged diff above
following the piker project's conventions (learned from
analyzing 500 repo commits).
If `$ARGUMENTS` is provided, use it as scope or
description context for the commit message.
For the full style guide with verb frequencies,
section markers, abbreviations, piker-specific terms,
and examples, see
[style-guide-reference.md](./style-guide-reference.md).
## Quick Reference
- **Subject**: ~50 chars, present tense verb, use
backticks for code refs
- **Body**: only for complex/multi-file changes,
67 char line max
- **Section markers**: Also, / Deats, / Other,
- **Bullets**: use `-` style
- **Tone**: technical but casual (piker style)
## Claude-code Footer
When the written **patch** was assisted by
claude-code, include:
```
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
```
When only the **commit msg** was written by
claude-code (human wrote the patch), use:
```
(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
```
## Output Instructions
When generating a commit message:
1. Analyze the staged diff (injected above via
dynamic context) to understand all changes.
2. If `$ARGUMENTS` provides a scope (e.g.,
`.ib.feed`) or description, incorporate it into
the subject line.
3. Write the subject line following verb + backtick
conventions from the
[style guide](./style-guide-reference.md).
4. Add body only for multi-file or complex changes.
5. Write the message to a file in the repo's
`.claude/` subdir with filename format:
`<timestamp>_<first-7-chars-of-last-commit-hash>_commit_msg.md`
where `<timestamp>` is from `date --iso-8601=seconds`.
Also write a copy to
`.claude/git_commit_msg_LATEST.md`
(overwrite if exists).
---
**Analysis date:** 2026-01-27
**Commits analyzed:** 500 from piker repository
**Maintained by:** Tyler Goodlet

View File

@ -150,21 +150,18 @@ Common in piker commits (33.0% use colons):
- File:line references not used (0 occurrences) - File:line references not used (0 occurrences)
- No WIP commits in analyzed set - No WIP commits in analyzed set
### Claude-code Footer ### Coding-harness Footer
When the written **patch** was assisted by claude-code, When a coding harness assisted with the written patch,
include: identify the active harness, model, and provider:
``` ```
(this patch was generated in some part by [`claude-code`][claude-code-gh]) (this patch was generated in some part by `<harness>` using `<model>` (`<provider>`))
[claude-code-gh]: https://github.com/anthropics/claude-code
``` ```
When only the **commit msg** was written by claude-code When it generated only the commit message, use:
(human wrote the patch), use:
``` ```
(this commit msg was generated in some part by [`claude-code`][claude-code-gh]) (this commit msg was generated in some part by `<harness>` using `<model>` (`<provider>`))
[claude-code-gh]: https://github.com/anthropics/claude-code
``` ```
## Piker-Specific Terms ## Piker-Specific Terms

View File

@ -0,0 +1 @@
../../.agents/skills/piker-clearing-expert

View File

@ -0,0 +1,155 @@
---
name: piker-conc-expert
description: >
Distributed-runtime and structured-concurrency
expertise for piker's `tractor` actor-tree. Apply
when working on daemon/service architecture, actor
spawning/discovery, cross-actor RPC (ctx/stream
eps), `to_asyncio` integration, cancellation
semantics, or debugging hangs/wedges/skews in the
actor system.
compatibility: >
Requires a piker checkout and familiarity with Python,
trio, and tractor.
metadata:
author: goodboy
version: "1.0"
---
# Piker Concurrency & Runtime Expertise
The distilled mental model for piker's distributed
runtime: a `trio`-structured actor tree supervised by
`tractor` (pinned to git main) where every long-lived
subsystem is a named daemon-actor talking over
ctx/stream IPC.
## Actor tree & daemon taxonomy
```
pikerd root supervisor + registry
├── datad.<broker> feed bus, shm writers, tsp
│ history, symbol search
├── brokerd.<broker> live order-ctl ONLY; lazily
│ spawned by emsd, credentialed
├── emsd dark-clearing + order routing
│ └── paperboi.<broker> sim-clearing (paper mode)
└── samplerd singleton OHLC clock/increment
```
Key invariants:
- `datad` hosts all `piker.data.validate._eps['datad']`
eps; `brokerd` only the `['brokerd']` (order-ctl)
ones. The `_eps` table in `piker/data/validate.py`
is the authoritative contract; `get_eps(mod, kind)`
introspects a backend's support.
- `brokerd.<broker>` is booted in EXACTLY one place:
`open_brokerd_dialog()` in `piker/clearing/_ems.py`
(with a `portal:` override for the `piker ledger`
ad-hoc actor). Chart-only + paper sessions run with
ZERO brokerd procs. Never add a data-path spawn!
- backends declare per-daemon-kind submods via
`_datad_mods`/`_brokerd_mods` in their
`__init__.py` (fallback: `__enable_modules__`).
## Daemon lifecycle conventions
Every daemon-kind follows the same trio of fns (see
`piker/brokers/_daemon.py` + `piker/data/_daemon.py`
as the canonical pair):
- `_setup_persistent_<kind>()`: a `@tractor.context`
"lifetime fixture" run via
`Services.start_service_task()`; does console-log
setup ONCE for the actor, allocs any actor-global
state (eg. datad's `_FeedsBus`), then
`await ctx.started()` + `trio.sleep_forever()`.
- `<kind>_init()`: builds `enable_modules` + actor
name `f'<kind>.{brokername}'` and copies backend
`_spawn_kwargs` (CRITICAL: `ib` needs
`infect_asyncio=True` in EVERY daemon-kind).
- `spawn_<kind>()` + `maybe_spawn_<kind>()`: thin
wrappers over `Services.actor_n.start_actor()` and
`piker.service.maybe_spawn_daemon()` (registry
find-or-spawn w/ per-service-name locking).
Caps-sec model: `enable_modules` gates RPC entry ONLY
— python imports are unrestricted in-proc. Keep each
daemon's enable set minimal; the (credentialed)
`brokerd` must never RPC-enable `piker.data.*` feed
mods.
## Actor-local state: the #1 split hazard
Module-globals and instance caches are PER-ACTOR.
Anything that "just worked" because two subsystems
shared a process will break when they're split into
sibling actors. Canonical example: `ib`'s
`Client._contracts` was warmed by feed-side
`get_mkt_info()` in-proc; post datad/brokerd-split
the trading actor must warm it itself (eagerly at
`open_trade_dialog()` startup for open pps/orders +
lazily per order request via
`symbols.cache_contract()`).
When moving code across actor boundaries ALWAYS audit:
- module-global registries (`feed._bus`,
`_accounts2clients`, `_client_cache`, ..)
- `@async_lifo_cache`/`maybe_open_context` caches
(NOTE: `async_lifo_cache` keys on POSITIONAL args
only; a cache-hit SKIPS the fn body and thus any
side-effect writes!)
- logging handler placement (see gotchas.md)
## tractor primitives as used here
- `@tractor.context` eps: `await ctx.started(val)`
unblocks the caller w/ `val`; long-lived eps then
`ctx.open_stream()` or `sleep_forever()`.
- discovery: `tractor.find_actor()` via
`piker.service.find_service()`;
`wait_for_actor(name, registry_addr=...)`;
`query_actor(name, regaddr=...)` yields
`(sockaddr, portal)`. Addrs are wrapped
`tractor.discovery._addr.Address` types — use
`wrap_address()` to normalize raw tuples and
`.unwrap()` for comparisons.
- runtime-vars: `_runtime_vars['piker_vars']` is
inherited down the spawn tree; used eg. for
`piker_test_dir` config isolation — read LAZILY at
use-time, never at import time (subactors only get
vars post runtime-boot).
- cancellation semantics (modern tractor): a
`ContextCancelled` whose `.canceller` is your own
actor is ABSORBED (clean exit, nothing raised);
single-exc groups collapse (`collapse_eg`) so eg.
a KBI propagates bare. Exc attrs:
`RemoteActorError.boxed_type` (not `.type`).
## `to_asyncio` (infect-asyncio) integration
For `ib` (and `deribit`) the backend client runs on
an embedded `asyncio` loop via
`tractor.to_asyncio.open_channel_from()` +
`LinkedTaskChannel`.
Rules learned the hard way:
- a shared req/resp channel MUST correlate responses
to requests (see `MethodProxy._run_method()`'s
`mid` protocol in `piker/brokers/ib/api.py`):
caller cancellation (eg. `move_on_after` timeouts)
otherwise orphans a response and silently skews
every later result off-by-one.
- the aio-side relay must catch + ship back ALL
(non-cancel) exceptions as `{'exception': err}`
resps; an escaping error kills the relay task ->
channel -> proxy nursery -> the whole dialog,
bypassing every caller-side guard.
- `TrioTaskExited` ("child asyncio task is still
running?") on teardown is a known wart family;
prefer upstream `tractor` fixes over piker-side
bandaids.
See [gotchas.md](gotchas.md) for the symptom->cause
registry and [debug-recipes.md](debug-recipes.md) for
forensics techniques.

View File

@ -0,0 +1,100 @@
# Debug recipes: actor-system forensics
Field-tested techniques for diagnosing hangs, wedges
and cross-actor state bugs WITHOUT a debugger attached
(or when `py-spy` ain't installed).
## Wedged actor triage (no REPL)
1. find the tree:
`ps -eo pid,etime,args | grep -E 'pytest|tractor._child'`
— long-`etime` `tractor._child` procs w/ a stuck
parent = wedge.
2. kernel state:
`cat /proc/<pid>/wchan` + `status | grep -E
'State|Threads'` — `do_epoll_wait` + sleeping =
idle event loop, NOT cpu-spin.
3. **the money read** — socket queues:
`ss -tnp | grep <pid>`
- `Recv-Q > 0` on the parent-IPC conn = the actor
STOPPED CONSUMING its msg loop (runtime bug),
parent is waiting on it.
- zero external (api/ws) conns = wedged before/
without provider IO; don't blame the network.
- `CLOSE-WAIT` lingerers = unclean peer teardown.
4. cleanup: `pkill -f tractor._child` (NB: in
compound shell cmds `pkill`'s exit code poisons
`&&` chains — run it standalone).
## Hang-proof test gating
- per-suite, never combined (cross-suite session
state interacts w/ the 2nd-boot wedge):
`timeout -k 5 300 python -m pytest tests/<one>.py -q`
- rc 124/143 = hang-kill -> retry ONCE before
investigating.
- isolate a flaky test w/ a 3x loop; ~50% hit-rate
signatures match the known 2nd-boot wedge (see
gotchas.md).
## Regression vs pre-existing attribution
When a failure appears mid-refactor:
1. `git stash -u` (or checkout the file subset) and
re-run the EXACT failing case at baseline.
2. if baseline can't even run, selectively revert
ONLY the suspect layer:
`git diff <files> > /tmp/x.patch;
git checkout <files>` -> test ->
`git apply /tmp/x.patch`.
3. flake-rate compare (3x runs) beats single-shot
conclusions.
## Off-by-one / stale IPC resp detection
Mismatched query->result content in logs (resp
payload obviously for a prior request) = shared
req/resp channel w/o correlation + a cancelled
caller. Grep the ep for `move_on_after`/`fail_after`
around proxied calls. Fix = req-id (`mid`) tagging,
never "just a lock" (cancellation still orphans).
## Logging-chain audits
When records double-print or go bare (see gotchas.md):
```python
import logging
l = logging.getLogger('piker.brokers.ib.broker')
while l:
print(l.name, l.level, l.handlers, l.propagate)
l = l.parent
```
Exactly ONE stderr handler should exist in the chain,
attached by the actor's daemon fixture.
## Live actor-tree smoke (headless)
Boot against an ALT registry port so a user's running
stack is untouched; script in a REAL file (tractor
children re-exec `__main__` from path — stdin scripts
crash w/ `FileNotFoundError: .../<stdin>`):
```python
async with maybe_open_pikerd(
registry_addrs=[('127.0.0.1', 6979)],
):
async with open_feed(['xbtusdt.kraken']) as feed:
assert await check_for_service('datad.kraken')
assert not await check_for_service(
'brokerd.kraken'
)
```
## In-proc fail-fast unit checks
Spawn-path guards that raise BEFORE touching the
runtime can be tested w/ a bare `trio.run()` (eg
`spawn_brokerd('kucoin')` raising the datad-only
error) — no pikerd needed.

View File

@ -0,0 +1,116 @@
# Known gotchas: symptom -> cause -> fix
A registry of distributed-runtime failure modes hit
(and diagnosed) in the field; check here FIRST when a
log/traceback matches.
## "Can not order ..., no qualified contract cached"
- **Symptom**: `RuntimeError` from
`ib.api.Client.submit_limit()` w/ empty
`Client._contracts` in `brokerd.ib`.
- **Cause**: per-actor cache never warmed; feed-side
qualification now lives in `datad.ib`.
- **Fix(ed)**: eager warmup at `open_trade_dialog()`
start + lazy per-order `get_mkt_info()` +
`cache_contract()` (writes BOTH `mkt.bs_fqme` and
`mkt.fqme` keys; different consumers read each!).
## Search returns results for the WRONG pattern
- **Symptom**: fqme search for 'gld' returns nvda
results; next query returns the prior query's set.
- **Cause**: `MethodProxy` channel off-by-one — a
caller cancelled (search `move_on_after` timeout)
after sending its request orphans the response;
every later caller consumes the previous resp.
- **Fix(ed)**: `mid` req-id correlation in
`_run_method()` + relay; stale resps are dropped w/
a "Dropping stale method-resp" warning. If that
warning spams, some caller is being cancelled
mid-call habitually — find + fix its timeout.
## One bad request crashes a whole dialog/actor
- **Symptom**: `TrioTaskExited` storm + nursery
teardown after a single method error (eg ambiguous
contract `AttributeError`).
- **Cause**: exception escaped the aio-side relay
loop (`open_aio_client_method_relay()`) killing
channel + proxy nursery; caller-side `try/except`
CANNOT catch it.
- **Fix(ed)**: relay catches `Exception` -> ships
`{'exception': err, 'mid': ...}` resp; order
handler converts to EMS `BrokerdError` msgs.
## Ambiguous ib contracts -> `NoneType` attr errors
- **Symptom**: `'NoneType' object has no attribute
'primaryExchange'` in `find_contracts()`.
- **Cause**: `qualifyContractsAsync()` returns `None`
entries for ambiguous (eg venue-less stonk fqme
matching multiple listings: 'gld' -> ARCA/USD +
VENTURE/CAD).
- **Fix(ed)**: filter `None`s + raise descriptive
`ValueError` ("use 'gld.arca.ib'").
## Double-printed log records (same task id, 2x)
- **Symptom**: every record from some subsys printed
twice w/ identical task ids.
- **Cause**: stderr handlers attached at TWO levels
of one logger-propagation chain (eg daemon fixture
on `piker.brokers.ib` + an ep calling
`get_console_log(name=__name__)` on the child).
tractor's handler-dedup only checks the SAME
logger, not ancestors.
- **Rule**: console handlers are attached ONCE per
actor in the `_setup_persistent_*()` fixture; eps
needing a different level use `log.setLevel()`
ONLY, never `get_console_log()`.
## Bare/non-colorized log lines
- **Symptom**: records w/ no timestamp/actor prefix.
- **Cause**: NO handler anywhere in the emitting
logger's chain -> stdlib `logging.lastResort`. Post
actor-splits, a daemon fixture may only cover its
own subsys subtree (eg datad's `piker.data.*` but
not the backend's `piker.brokers.<broker>.*`).
- **Fix(ed)**: `_setup_persistent_datad()` enables
BOTH `piker.data.<broker>` and
`piker.brokers.<broker>` subtrees.
## 2nd in-proc runtime boot wedges (~50%)
- **Symptom**: test hangs when one test proc boots a
2nd `pikerd` (eg `test_multi_fill_positions`'s
persistence re-check); a zombie `*.{broker}` child
lingers w/ unread bytes in its parent-IPC Recv-Q.
- **Cause**: pre-existing `tractor`-main runtime
teardown bug (confirmed independent of piker-layer
changes via revert-testing 2026-06).
- **Mitigation**: run suites per-file wrapped in
`timeout -k 5 300 ...`; retry once on rc 124/143.
Do NOT chase as a regression of unrelated changes.
## ib client-id collisions post-split
- **Symptom**: 2nd ib daemon burns the full
conn-timeout retry cycle connecting to gw/tws.
- **Cause**: `datad.ib` + `brokerd.ib` both default
`client_id=6116` w/ linear `+i` retries.
- **Fix(ed)**: role-based offsets in
`load_aio_clients()`: datad +16, ad-hoc (test/cli)
actors +32.
## `async_lifo_cache` skipped side-effects
- **Symptom**: a fn's cache-write side effect
(eg `get_mkt_info()` -> `_contracts`) missing for
a 2nd client/proxy.
- **Cause**: cache keys on POSITIONAL args only; a
hit skips the body entirely.
- **Rule**: never rely on cached-fn side effects;
perform required writes explicitly at the call
site (eg `cache_contract()` after `get_mkt_info`).

View File

@ -5,7 +5,11 @@ description: >
across distributed actor systems. Apply when across distributed actor systems. Apply when
adding profiling, debugging perf regressions, or adding profiling, debugging perf regressions, or
optimizing hot paths in piker code. optimizing hot paths in piker code.
user-invocable: false compatibility: >
Requires a piker checkout and Python profiling work.
metadata:
author: goodboy
version: "1.0"
--- ---
# Piker Profiling Subsystem # Piker Profiling Subsystem

View File

@ -5,7 +5,11 @@ description: >
ethos. Apply when communicating with piker devs, ethos. Apply when communicating with piker devs,
writing commit messages, code review comments, or writing commit messages, code review comments, or
any collaborative interaction. any collaborative interaction.
user-invocable: false compatibility: >
Harness-independent communication guidance.
metadata:
author: goodboy
version: "1.0"
--- ---
# Piker Slang & Communication Style # Piker Slang & Communication Style

View File

@ -47,7 +47,7 @@ that's why it's like 1000x faster ya know?"
- Wrap all code symbols: `function()`, - Wrap all code symbols: `function()`,
`ClassName`, `field_name` `ClassName`, `field_name`
- File paths: `piker/ui/_remote_ctl.py` - File paths: `piker/ui/_remote_ctl.py`
- Commands: `git status`, `piker store ldshm` - Commands: `git status`, `piker store shm`
**Explain like you're pair programming:** **Explain like you're pair programming:**
``` ```

View File

@ -5,7 +5,12 @@ description: >
for piker's UI. Apply when optimizing graphics for piker's UI. Apply when optimizing graphics
performance, adding new chart annotations, or performance, adding new chart annotations, or
working with `QGraphicsItem` subclasses. working with `QGraphicsItem` subclasses.
user-invocable: false compatibility: >
Requires a piker checkout with PyQtGraph and Qt source
available for inspection.
metadata:
author: goodboy
version: "1.0"
--- ---
# PyQtGraph Rendering Optimization # PyQtGraph Rendering Optimization

View File

@ -0,0 +1,291 @@
# Test Harness Reference: piker
This repository-local file supplements the canonical `/run-tests` skill.
Keep shared execution, worktree, failure-inspection, and cleanup policy in the
canonical `SKILL.md`; keep Piker commands, paths, fixtures, and known outcomes
here.
## Project And Environment
- Project/import: `piker`
- Test root: `tests/`
- Supported Python: `>=3.12,<3.14`
- Preferred complete environment: worktree-local `py313` inside the current
`nix develop` shell
- The flake shell pins CPython 3.13 and sets
`UV_PROJECT_ENVIRONMENT=py313`.
- Verify the interpreter, package resolution, and dependency import before
running tests. A bare `py313` may lack the Qt binding supplied by Nix.
Use an already-provisioned `py313` only when `import piker` succeeds:
```text
py313/bin/python -m pytest -p no:xonsh
```
If direct environment paths are unavailable, an existing uv environment can
be used without changing it, subject to the same import check:
```text
UV_PROJECT_ENVIRONMENT=py313 uv run --frozen --no-sync python -m pytest -p no:xonsh
```
Ask before running provisioning commands such as:
```text
UV_PROJECT_ENVIRONMENT=py313 uv sync --dev --all-extras --no-group lint
nix develop
nix-shell default.nix
```
`nix develop` is the current Wayland/Qt 6 shell. `default.nix` is the current
X11 shell. Do not use `develop.nix` for current testing; it retains the old
Python 3.11, Poetry, and Qt 5 stack.
The current root-checkout `py313` resolves `piker` locally but fails
`import piker` outside Nix because PyQtGraph cannot import PyQt or PySide. Do
not treat that environment as test-ready and do not enter `nix develop`
without approval: its shell hook may recreate and sync `py313`.
Plain `uv sync` does not include the testing group. The `dbs` dependency group
is also absent from normal dev-shell provisioning.
## Commands
Base command in the preferred environment:
```text
py313/bin/python -m pytest -p no:xonsh
```
The explicit `-p no:xonsh` is required. The tracked comments-only
`pytest.ini` takes precedence over `pyproject.toml`, so the intended
`addopts = "-p no:xonsh"` and `testpaths = ["tests"]` are inactive.
Always pass a test path or node ID explicitly.
Package-resolution check that does not import Piker's dependencies:
```text
py313/bin/python -c 'import importlib.util, pathlib, sys; root = pathlib.Path.cwd().resolve(); spec = importlib.util.find_spec("piker"); mod = pathlib.Path(spec.origin).resolve(); print(sys.executable); print(mod); assert mod.is_relative_to(root)'
```
Dependency import check, required before collection or execution:
```text
py313/bin/python -c 'import pathlib, piker, sys; root = pathlib.Path.cwd().resolve(); mod = pathlib.Path(piker.__file__).resolve(); print(sys.executable); print(mod); assert mod.is_relative_to(root)'
```
Safe core collection check:
```text
py313/bin/python -m pytest -p no:xonsh -q --collect-only tests/test_watchlists.py tests/test_accounting.py tests/test_services.py tests/test_ems.py tests/test_feeds.py tests/test_cli.py
```
Default first-pass flags are `-q -x --tb=short --no-header` unless the user
requests otherwise. For actor-heavy tests, use one file or node per process
with an outer timeout:
```text
timeout -k 5 300 py313/bin/python -m pytest -p no:xonsh -q <one-file-or-node>
```
If an actor-heavy command exits `124` or `143`, retry that exact command once
and report both attempts. Never convert a retry pass into an unconditional
pass, and do not retry assertion, import, collection, or configuration
failures.
## Scope And Path Resolution
Resolve bare test filenames beneath `tests/`. Preserve complete node IDs and
apply `-k` only within explicitly selected paths. There is no marker-based
offline/full-suite split, so never use `pytest tests` as a deterministic
default.
Deterministic or local first-pass targets:
- `tests/test_watchlists.py`
- `tests/test_storage_audit.py`
- `tests/test_store_cli.py`
- `tests/test_backfill_audit_snippet.py`
- `tests/test_ib_history.py`
- `tests/test_ib_method_proxy.py`
- `tests/test_history_backfill.py`
- `tests/test_ldshm.py`
- `tests/test_accounting.py::test_account_file_default_empty`
- `tests/test_services.py::test_runtime_boot`
- `tests/test_services.py::test_datad_spawn`
- `tests/test_ems.py::test_ems_err_on_bad_broker`
Require explicit authorization before running:
- `tests/test_feeds.py` - live Binance/Kraken feeds;
- `tests/test_services.py::test_ensure_datafeed_actors` - live Kraken feed;
- `tests/test_services.py::test_ensure_ems_in_paper_actors` - paper EMS with
live Kraken symbology/feed access;
- `tests/test_ems.py::test_multi_fill_positions` - live backend setup and
persisted state;
- `tests/test_accounting.py::test_paper_ledger_position_calcs` - tracked
fixtures plus possible live symcache generation;
- `tests/test_accounting.py::test_ib_account_with_duplicated_mktids` - active
broker/account configuration and state writes;
- `tests/test_dpi_font.py` - Qt/UI import and user-config side effects;
- `tests/test_docker_services.py` - optional dependencies and containers;
- `tests/test_questrade.py` - obsolete credentialed imports.
`tests/test_cli.py` is currently hard-skipped. It is not an active CLI
regression gate.
Never execute `piker store anal` or `piker store shm --write-parquet`
as tests. They are mutating or interactive operational commands.
## Project-Specific Flags And Backend Matrix
| Flag | Purpose |
|---|---|
| `--ll LEVEL` | Piker log level |
| `--confdir PATH` | Override `piker.config._config_dir` |
| `--spawn-backend trio|mp_spawn|mp_forkserver` | Tractor process backend |
| `--tpt-proto PROTO` | Tractor transport; one protocol per session |
| `--tpdb` / `--debug-mode` | Tractor plugin debug mode |
| `--pdb` | Standard pytest debugger |
| `-s` | No capture; required with `--pdb` and `open_test_pikerd` |
Do not invent `network`, `offline`, `docker`, `gui`, or `broker` markers; none
currently exists. `CI=1` is not an offline selector: feed tests still leave a
live Kraken case enabled. Current Piker tests exercise TCP; do not assume the
whole suite supports UDS merely because the Tractor plugin exposes it.
## Fixture Invariants
- The session `confdir` fixture does nothing unless `--confdir` is passed.
Its claimed `tests/data` fallback is not implemented and that directory is
absent.
- Function-scoped `tmpconfdir` changes process-global config state and does
not restore the previous path. Use separate pytest processes when
diagnosing state leakage.
- `open_test_pikerd` passes the temporary config path to child actors through
`tractor_runtime_overrides`.
- `tests/_inputs/trades_binance_paper.toml` and
`tests/_inputs/account.binance.paper.toml` are used in place. Accounting
contexts can write them on exit. Inspect `git diff -- tests/_inputs` after
any selected accounting case.
- Importing `tests/test_dpi_font.py` constructs module-global font objects
before fixtures can isolate config. If explicitly requested, isolate
`XDG_CONFIG_HOME` before Python starts and use the proper Qt/Nix shell.
- Piker and the installed Tractor pytest plugin do not provide a
repository-local process or socket reaper. Never apply historical broad
`pkill -f tractor._child` guidance automatically.
- The function-scoped autouse `shm_leak_tracker` fixture wraps Tractor's
current-process `SharedMemory` factory. It tracks only successful
`create=True` calls, restores the pre-test `_known_tokens` cache, and
unlinks exact surviving names before failing the leaking test. It never
scans `/dev/shm` or unlinks attachments created by another process.
## Test Layout
```text
tests/
conftest.py Piker options, config fixtures, Tractor plugin
_inputs/ tracked ledger/account fixtures
test_accounting.py config, ledgers, accounts, and position math
test_cli.py legacy CLI suite; hard-skipped
test_docker_services.py container integrations; optional deps, skipped
test_dpi_font.py Qt DPI/font behavior
test_ems.py actor, EMS, and paper-position behavior
test_feeds.py live Binance/Kraken feeds and shared memory
test_ib_history.py deterministic IB history request formatting
test_ib_method_proxy.py deterministic IB asyncio proxy routing
test_history_backfill.py deterministic history/SHM orchestration
test_ldshm.py SHM unpublished-slot guard
test_questrade.py obsolete credentialed tests; skipped
test_services.py pikerd/datad/feed/EMS actor lifecycle
test_store_cli.py storage command help and diagnostics UX
test_storage_audit.py read-only NativeDB audit and JSON CLI
test_backfill_audit_snippet.py
disposable xonsh qualification helpers
test_watchlists.py deterministic watchlist JSON operations
```
## Change-To-Test Mapping
| Changed area | Run first | Caveat |
|---|---|---|
| `piker/watchlists/` | `tests/test_watchlists.py` | CLI suite is skipped |
| `piker/storage/_audit.py`, `piker/storage/cli.py` | `tests/test_storage_audit.py` | direct Typer app, no actor |
| `piker/storage/cli.py` command UX | `tests/test_store_cli.py` | fake SHM/runtime, no mutation |
| `snippets/nativedb_backfill_audit.xsh` | `tests/test_backfill_audit_snippet.py` | disposable paths only |
| `piker/brokers/ib/api.py`, `feed.py` history | `tests/test_ib_history.py` | fake client, no network |
| `piker/brokers/ib/api.py` method proxy | `tests/test_ib_method_proxy.py` | fake channel, no network |
| `piker/tsp/_history.py` | `tests/test_history_backfill.py` | fake provider/storage/SHM |
| `piker/storage/cli.py` SHM null-slot guard | `tests/test_ldshm.py` | synthetic timestamps, no SHM mutation |
| `piker/config.py` | `test_account_file_default_empty` | root-network test has a known mismatch |
| `piker/accounting/` | targeted accounting node | some cases use live/configured state |
| `piker/ui/_style.py`, `piker/ui/qt.py` | `tests/test_dpi_font.py` | GUI/config-isolated opt-in |
| `piker/service/_actor_runtime.py`, `_registry.py`, `_mngr.py` | `test_runtime_boot` | then `test_datad_spawn` |
| `piker/service/`, `piker/data/_daemon.py` | `test_datad_spawn` | feed lifecycle cases are live |
| `piker/data/feed.py`, `flows.py`, `_sharedmem.py`, `_sampling.py` | collect first | feed execution needs live permission |
| `piker/clearing/` | `test_ems_err_on_bad_broker` | multi-fill case is live/persisted |
| `piker/brokers/binance/`, `kraken/` | selected feed/accounting node | live network |
| `piker/brokers/ib/` | duplicated-market-ID node | controlled account config required |
| Docker/service adapters | `tests/test_docker_services.py` | optional deps and containers |
| project, lock, or Nix files | import check and safe collection | full collection is not safe by default |
Prefer deterministic filesystem/config tests, then local actor-runtime nodes,
then explicitly approved live broker, GUI, or container coverage.
## Quick Checks
```text
py313/bin/python -c 'import importlib.util, pathlib, sys; root = pathlib.Path.cwd().resolve(); spec = importlib.util.find_spec("piker"); mod = pathlib.Path(spec.origin).resolve(); print(sys.executable); print(mod); assert mod.is_relative_to(root)'
py313/bin/python -c 'import pathlib, piker, sys; root = pathlib.Path.cwd().resolve(); mod = pathlib.Path(piker.__file__).resolve(); print(sys.executable); print(mod); assert mod.is_relative_to(root)'
py313/bin/python -m pytest -p no:xonsh -q tests/test_watchlists.py
py313/bin/python -m pytest -p no:xonsh -q tests/test_accounting.py::test_account_file_default_empty
timeout -k 5 300 py313/bin/python -m pytest -p no:xonsh -q tests/test_services.py::test_runtime_boot
timeout -k 5 300 py313/bin/python -m pytest -p no:xonsh -q tests/test_services.py::test_datad_spawn
timeout -k 5 300 py313/bin/python -m pytest -p no:xonsh -q tests/test_ems.py::test_ems_err_on_bad_broker
```
## Known Outcomes
- The current root-checkout `py313` fails `import piker` outside the Nix shell
with `ImportError: PyQtGraph requires one of PyQt5, PyQt6, PySide2 or
PySide6`. This is an incomplete environment, not an application regression.
- `tests/test_accounting.py::test_root_conf_networking_section` currently
expects `network.tsdb`, which is absent from the tracked config template.
Match the current `KeyError: 'tsdb'` before classifying it as the known
repository mismatch.
- `tests/test_docker_services.py` is marked skipped but imports
`elasticsearch` first. Without the `dbs` group it fails collection rather
than skipping.
- `tests/test_questrade.py` is marked skipped but imports undeclared `asks`
through the legacy broker module before marks apply.
- `test_open_orders_reloaded` and `test_dark_order_clearing` in
`tests/test_ems.py` contain only ellipsis bodies. A pass does not verify the
named behavior.
- Treat `.pytest_cache` feed IDs using old FQME forms as stale historical
cache, not current known failures.
Do not classify every `TooSlowError`, timeout, or child survivor as the known
Tractor teardown wedge. Match the selected node and the documented
second-runtime/lingering-child signature.
## Tractor Runtime Notes
The suite loads `tractor._testing.pytest`. Defaults are the `trio` spawn
backend and TCP transport. Registry addresses are normally session-unique,
except `tests/test_services.py::test_runtime_boot`, which binds
`127.0.0.1:6666`. Check that fixed port only for that node; ordinary tests do
not require Piker's production `127.0.0.1:6116` registry address.
Current repository concurrency notes document an intermittent second
in-process `pikerd` boot wedge with a lingering broker child and unread parent
IPC bytes. Use the outer timeout for actor-heavy nodes. Retry an exact command
once only after status `124` or `143`, and report both attempts.
Ordinary actor tests support normal capture. Diagnose a capture-dependent hang
by retrying only the exact node with `-s`. Standard `--pdb` with
`open_test_pikerd` requires `-s`; Tractor's `--tpdb` is a separate option.
After abnormal exit, inspect only descendants, sockets, and shared-memory
objects attributable to the exact pytest session. Ask before signaling or
unlinking anything.

View File

@ -6,7 +6,12 @@ description: >
with OHLCV arrays, timestamp lookups, gap with OHLCV arrays, timestamp lookups, gap
detection, or any array/dataframe operations in detection, or any array/dataframe operations in
piker. piker.
user-invocable: false compatibility: >
Requires a piker checkout with NumPy and optionally
Polars.
metadata:
author: goodboy
version: "1.0"
--- ---
# Timeseries Optimization: NumPy & Polars # Timeseries Optimization: NumPy & Polars
@ -61,11 +66,14 @@ ts_array = np.array(timestamps)
# binary search for all timestamps at once # binary search for all timestamps at once
indices = np.searchsorted(time_arr, ts_array) indices = np.searchsorted(time_arr, ts_array)
# bounds check and exact match verification # bounds check before indexing: searchsorted may return
valid_mask = ( # len(time_arr) for values above the final timestamp
(indices < len(array)) in_bounds = indices < len(time_arr)
& valid_mask = np.zeros(indices.shape, dtype=bool)
(time_arr[indices] == ts_array) valid_mask[in_bounds] = (
time_arr[indices[in_bounds]]
==
ts_array[in_bounds]
) )
valid_indices = indices[valid_mask] valid_indices = indices[valid_mask]

2
.gitattributes vendored 100644
View File

@ -0,0 +1,2 @@
# Taken requires a blank line after every Org subtree, including EOF.
.taken/*.org whitespace=-blank-at-eof

192
.gitignore vendored
View File

@ -110,6 +110,12 @@ ENV/
.claude/*_commit_*.md .claude/*_commit_*.md
.claude/*_commit*.toml .claude/*_commit*.toml
# ai.skillz/commit-msg
.claude/skills/commit-msg/msgs/
.claude/skills/commit-msg/conf.toml
.claude/git_commit_msg_LATEST.md
.opencode/skills/commit-msg/SKILL.md
# nix develop --profile .nixdev # nix develop --profile .nixdev
.nixdev* .nixdev*
@ -130,3 +136,189 @@ gitea/
# LLM conversations that should remain private # LLM conversations that should remain private
docs/conversations/ docs/conversations/
# BEGIN ai.skillz: direct:symlink:claude:run-tests
/.claude/skills/run-tests/SKILL.md
# END ai.skillz: direct:symlink:claude:run-tests
# BEGIN ai.skillz: direct:symlink:opencode:run-tests
/.opencode/skills/run-tests/SKILL.md
# END ai.skillz: direct:symlink:opencode:run-tests
# BEGIN ai.skillz: direct:symlink:opencode:command:run-tests
/.opencode/commands/run-tests.md
# END ai.skillz: direct:symlink:opencode:command:run-tests
# BEGIN ai.skillz: direct:symlink:claude:close-wkt
/.claude/skills/close-wkt
# END ai.skillz: direct:symlink:claude:close-wkt
# BEGIN ai.skillz: direct:symlink:opencode:close-wkt
/.opencode/skills/close-wkt
# END ai.skillz: direct:symlink:opencode:close-wkt
# BEGIN ai.skillz: runtime:code-review-changes
.claude/review_context.md
.claude/review_regression.md
# END ai.skillz: runtime:code-review-changes
# BEGIN ai.skillz: direct:symlink:claude:code-review-changes
/.claude/skills/code-review-changes
# END ai.skillz: direct:symlink:claude:code-review-changes
# BEGIN ai.skillz: direct:symlink:opencode:code-review-changes
/.opencode/skills/code-review-changes
# END ai.skillz: direct:symlink:opencode:code-review-changes
# BEGIN ai.skillz: runtime:commit-msg
.claude/skills/commit-msg/msgs/
.claude/skills/commit-msg/conf.toml
.claude/git_commit_msg_LATEST.md
# END ai.skillz: runtime:commit-msg
# BEGIN ai.skillz: direct:symlink:claude:commit-msg
/.claude/skills/commit-msg/SKILL.md
# END ai.skillz: direct:symlink:claude:commit-msg
# BEGIN ai.skillz: direct:symlink:opencode:commit-msg
/.opencode/skills/commit-msg/SKILL.md
# END ai.skillz: direct:symlink:opencode:commit-msg
# BEGIN ai.skillz: direct:symlink:claude:dep-supersede-scan
/.claude/skills/dep-supersede-scan
# END ai.skillz: direct:symlink:claude:dep-supersede-scan
# BEGIN ai.skillz: direct:symlink:opencode:dep-supersede-scan
/.opencode/skills/dep-supersede-scan
# END ai.skillz: direct:symlink:opencode:dep-supersede-scan
# BEGIN ai.skillz: direct:symlink:claude:gish
/.claude/skills/gish
# END ai.skillz: direct:symlink:claude:gish
# BEGIN ai.skillz: direct:symlink:opencode:gish
/.opencode/skills/gish
# END ai.skillz: direct:symlink:opencode:gish
# BEGIN ai.skillz: direct:symlink:claude:inter-skill-review
/.claude/skills/inter-skill-review
# END ai.skillz: direct:symlink:claude:inter-skill-review
# BEGIN ai.skillz: direct:symlink:opencode:inter-skill-review
/.opencode/skills/inter-skill-review
# END ai.skillz: direct:symlink:opencode:inter-skill-review
# BEGIN ai.skillz: runtime:open-wkt
.claude/wkts/
claude_wkts
/wkts/
# END ai.skillz: runtime:open-wkt
# BEGIN ai.skillz: direct:symlink:claude:open-wkt
/.claude/skills/open-wkt
# END ai.skillz: direct:symlink:claude:open-wkt
# BEGIN ai.skillz: direct:symlink:opencode:open-wkt
/.opencode/skills/open-wkt
# END ai.skillz: direct:symlink:opencode:open-wkt
# BEGIN ai.skillz: direct:symlink:claude:plan-io
/.claude/skills/plan-io
# END ai.skillz: direct:symlink:claude:plan-io
# BEGIN ai.skillz: direct:symlink:opencode:plan-io
/.opencode/skills/plan-io
# END ai.skillz: direct:symlink:opencode:plan-io
# BEGIN ai.skillz: runtime:pr-msg
.claude/skills/pr-msg/msgs/
.claude/skills/pr-msg/pr_msg_LATEST.md
# END ai.skillz: runtime:pr-msg
# BEGIN ai.skillz: direct:symlink:claude:pr-msg
/.claude/skills/pr-msg/SKILL.md
/.claude/skills/pr-msg/references
/.claude/skills/pr-msg/scripts
# END ai.skillz: direct:symlink:claude:pr-msg
# BEGIN ai.skillz: direct:symlink:opencode:pr-msg
/.opencode/skills/pr-msg/SKILL.md
/.opencode/skills/pr-msg/references
/.opencode/skills/pr-msg/scripts
# END ai.skillz: direct:symlink:opencode:pr-msg
# BEGIN ai.skillz: direct:symlink:claude:prompt-io
/.claude/skills/prompt-io
# END ai.skillz: direct:symlink:claude:prompt-io
# BEGIN ai.skillz: direct:symlink:opencode:prompt-io
/.opencode/skills/prompt-io
# END ai.skillz: direct:symlink:opencode:prompt-io
# BEGIN ai.skillz: direct:symlink:claude:py-codestyle
/.claude/skills/py-codestyle
# END ai.skillz: direct:symlink:claude:py-codestyle
# BEGIN ai.skillz: direct:symlink:opencode:py-codestyle
/.opencode/skills/py-codestyle
# END ai.skillz: direct:symlink:opencode:py-codestyle
# BEGIN ai.skillz: direct:symlink:claude:resolve-conflicts
/.claude/skills/resolve-conflicts
# END ai.skillz: direct:symlink:claude:resolve-conflicts
# BEGIN ai.skillz: direct:symlink:opencode:resolve-conflicts
/.opencode/skills/resolve-conflicts
# END ai.skillz: direct:symlink:opencode:resolve-conflicts
# BEGIN ai.skillz: runtime:taken-export
.ai/taken/exports/
# END ai.skillz: runtime:taken-export
# BEGIN ai.skillz: direct:symlink:claude:taken-export
/.claude/skills/taken-export
# END ai.skillz: direct:symlink:claude:taken-export
# BEGIN ai.skillz: direct:symlink:opencode:taken-export
/.opencode/skills/taken-export
# END ai.skillz: direct:symlink:opencode:taken-export
# BEGIN ai.skillz: direct:symlink:claude:yt-url-lookup
/.claude/skills/yt-url-lookup
# END ai.skillz: direct:symlink:claude:yt-url-lookup
# BEGIN ai.skillz: direct:symlink:opencode:yt-url-lookup
/.opencode/skills/yt-url-lookup
# END ai.skillz: direct:symlink:opencode:yt-url-lookup
# BEGIN ai.skillz: direct:symlink:claude:command:branch-in-new-terminal
/.claude/commands/branch-in-new-terminal.md
# END ai.skillz: direct:symlink:claude:command:branch-in-new-terminal
# BEGIN ai.skillz: runtime:branch-in-new-terminal
.claude/.current_session
# END ai.skillz: runtime:branch-in-new-terminal
# BEGIN ai.skillz: direct:symlink:opencode:command:commit-msg
/.opencode/commands/commit-msg.md
# END ai.skillz: direct:symlink:opencode:command:commit-msg
# BEGIN ai.skillz: direct:symlink:opencode:command:taken-export
/.opencode/commands/taken-export.md
# END ai.skillz: direct:symlink:opencode:command:taken-export
# Local worktrees and taken runtime artifacts
/.ai/state/
.*.taken.lock
# Version the federated project store despite local excludes
!/.taken/
!/.taken/current.org
!/.taken/sources.toml
!/.taken/README.md
# BEGIN ai.skillz: workflow-state
/.ai/state/
/.ai/commit-msg/conf.toml
/.ai/pr-msg/conf.toml
# END ai.skillz: workflow-state

22
.taken/README.md 100644
View File

@ -0,0 +1,22 @@
# piker project store
`current.org` contains the intact piker scope moved from the
LNS working corpus. Task/checkbox states, carry metadata and prose
remain human-owned. `sources.toml` selects this branch's store.
Read the local `taken` skill through `.agents/skills/taken/SKILL.md`.
It requires a complete-file revision guard for each authorized save.
Use an explicitly selected source; do not write a global LNS copy.
This migration preserves a historical project scope, not a claim that
its old task descriptions or branch paths reflect current reality.
The global storespace is registered in the LNS migration worktree's
`taken/sources.toml`. Modden, Tractor and Piker include snapshots of their pre-existing
local stores. The LNS consolidation ledger pins those inputs. Before
landing, reconcile any newer edits there and retarget active agents;
do not overwrite another agent's source or infer acceptance.
Before removing this migration worktree after landing, update the
global registration and the cross-repository taken skill link to the
landed locations. The skill link intentionally points to the Taken
migration branch until that code and documentation land.

651
.taken/current.org 100644
View File

@ -0,0 +1,651 @@
# vim: et sw=2 ts=2
#+TITLE: piker project store
#+TODO: TODO(t) WIP(w!) WAIT(a@) | DONE(d!) NOPE(n@)
# Checkbox states: [ ] open, [-] partial, [x] complete
* piker
:PROPERTIES:
:CATEGORY: piker
:END:
** TODO prs
:PROPERTIES:
:CREATED: [2026-07-15]
:CARRIED: 29
:END:
- [ ] move the below into *** todos with pr/branch links
* [ ] DPI-aware font scaling + manual kb bindings.
* [ ] gap_annotator for time gaps.
* [ ] moar `claude`-y skills,
* [ ] /lint with ruff
* [ ] /run-tests
*** WIP qualify known-FQME history backfill
:PROPERTIES:
:CREATED: [2026-07-24]
:CARRIED: 21
:REPO: /home/goodboy/repos/piker
:END:
- [x] preserve the dirty `flake_update` main checkout before
switching branches
- [x] check out `backfiller_deep_fixes` in the main checkout and
verify every process imports it
- [ ] complete the below qualification plan
* [ ] select one known-problem provider, FQME, timeframe, date range,
and original symptom
* [ ] preserve and checksum any known-bad parquet
* [ ] bootstrap `pikerd` with disposable config and NativeDB storage
* [ ] record baseline timestamps, rows, uniqueness, nulls,
ordering,
indexes, gaps, and readability
* [ ] reproduce fresh-backfill, restart, and append behavior for
only
the selected case
* [ ] convert each reproduced failure into a failing deterministic
test before fixing it
* [ ] rerun the exact manual lifecycle after every production
correction
* [ ] add deterministic subprocess coverage for process death and
cross-process locking
* [ ] exercise Ctrl-C, SIGTERM, SIGKILL, provider interruption, and
restart recovery
* [ ] qualify MarketStore and actual sampler backpressure separately
* [ ] expand to other timeframes and providers only after the
selected FQME passes
**** TODO fixed-FQME-case backfill qualify
:PROPERTIES:
:CREATED: [2026-07-29]
:CARRIED: 17
:REPO: piker/
:END:
- [ ] follow through using both new files,
* ./docs/manual_backfill_qualification.rst
* ./snippets/nativedb_backfill_audit.xsh
- [ ] choose and record one known-problem FQME, timeframe, UTC range,
and original chart symptom
- [ ] source `snippets/nativedb_backfill_audit.xsh` and initialize a
private disposable root with only the required provider config
- [ ] run repository and environment preflight at commit `0846cbd4`
- [ ] seed and audit the known-bad baseline, then replay repair and run
`bfq_compare()` without losing original timestamps
- [ ] clear only the disposable Parquet and qualify a fresh backfill
- [ ] restart against fresh storage and compare `fresh` to `restart`
- [ ] leave one subsequent run active long enough to append, then compare
`restart` to `append` with `require_newer=True`
- [ ] restart QQQ 1s on fresh datad SHM and verify the former 653-row
zero-time reservation does not recur
- [ ] preserve chart logs, audit JSON, Parquet snapshots, and exact UTC
gap endpoints for review
**** TODO review ^qualify^ evidence and close follow-ups
:PROPERTIES:
:CREATED: [2026-07-29]
:CARRIED: 17
:REPO: /piker
:END:
- [ ] verify every qualification process imports commit `0846cbd4`
- [ ] review baseline, repair, fresh, restart, and append reports for
structural, timestamp, endpoint, and gap regressions
- [ ] distinguish durable Parquet gaps from SHM hydration or chart-only
gaps using the runbook triage table
- [ ] convert each reproduced failure into a deterministic regression
before changing production code
- [ ] prepare the final qualification summary and any required patch,
PR, or worktree-lifecycle follow-ups
*** TODO cross-harness AI skill integration
:PROPERTIES:
:CREATED: [2026-07-21]
:CARRIED: 24
:REPO: /home/goodboy/repos/piker
:ISSUE: https://www.pikers.dev/pikers/piker/issues/79
:END:
- [ ] review and land the portable piker skill and OpenCode docs patch
- [ ] reconcile the active global `commit-msg` override with the
repo-local shared skill and style guide
- [ ] verify skill and command discovery after restarting Claude Code
and OpenCode
- [ ] fold remaining skill-content review into the canonical trackers:
* [ ] https://www.pikers.dev/pikers/piker/issues/69
* [ ] https://www.pikers.dev/pikers/piker/issues/79
*** TODO vectorize `gap_annotator` client spec building
:PROPERTIES:
:CREATED: [2026-07-21]
:CARRIED: 24
:REPO: /home/goodboy/repos/piker
:END:
- [ ] replace per-row Polars slicing and filtering in
`piker/tsp/_annotate.py:139` with extracted columns and batched
lookups
- [ ] preserve missing-prior-row, epoch-skip, direction, label, and
batch-payload behavior
- [ ] benchmark the 1285-gap case against the handoff in
`notes_to_self/claudy/gapsannotator_sesh.md`
- [ ] add focused correctness coverage for generated rectangle, arrow,
and text specs
*** TODO integrate the piker `ruff` lint skill
:PROPERTIES:
:CREATED: [2026-07-21]
:CARRIED: 24
:REPO: /home/goodboy/repos/piker
:END:
- [ ] review the clean but unmerged `claudy_writes_ruff_conf` branch
- [ ] validate its `ruff.toml` policy and staged-file checker against
the current tree
- [ ] port the `/lint` skill to shared Agent Skills frontmatter with
thin harness-specific commands
- [ ] rebase and prepare the accepted lint/config changes for landing
*** TODO reconcile stale Claude plans and worktrees
:PROPERTIES:
:CREATED: [2026-07-21]
:CARRIED: 24
:REPO: /home/goodboy/repos/piker
:END:
- [ ] reconcile `ai/claude-code/plans/shm_port.md` with the implemented
`tractor.ipc._shm` shim
- [ ] classify `datad_service.md` as an architecture record rather than
an active implementation plan
- [ ] inspect generated state before retiring the merged
`wkt/flake_update` and `worktree-ngi0_submission` worktrees
- [ ] preserve the unmerged lint worktree until its branch is reviewed
** TODO chart-local gap overlay follow-ups
:PROPERTIES:
:CREATED: [2026-08-14]
:CARRIED: 7
:REPO: /home/goodboy/repos/piker
:END:
- [ ] trigger `GapOverlayMngr.refresh()` from an explicit history
revision/publication event after repair or prepend
- [ ] expose gap-overlay UX through UI config
* [ ] startup visibility and enabled timeframes
* [ ] default `Ctrl+G` key binding
- [ ] live-qualify chart-local overlay behavior
* [ ] actor startup and focused 1s/60s toggling
* [ ] multi-symbol and duplicate-FQME cached charts
* [ ] secondary-`Viz` placement and backfill repositioning
* [ ] remote disconnect and owner-scoped graphics cleanup
- [ ] add focused Qt/actor regressions for the qualified lifecycle
** TODO factor typed chart annotation IPC dialogs
:PROPERTIES:
:CREATED: [2026-08-14]
:CARRIED: 7
:REPO: /home/goodboy/repos/piker
:END:
- [ ] exercise the strict `SetGapOverlay`/`GapOverlay`
dialog under a live Tractor context
* [ ] wrong-direction and unrelated payload rejection
* [ ] timeout, cancellation, stale reply, and disconnect recovery
- [ ] factor keyed send/receive locking, fresh request IDs, and stale
response correlation into a generic IPC dialog helper after a
second typed consumer establishes the interface
- [ ] migrate legacy `remote_annotate()` dict commands to tagged
request/response message types
- [ ] decide whether to fold `remote_gap_overlays()` into the general
annotation endpoint after the legacy protocol is typed
** proj_mgmt
:PROPERTIES:
:CATEGORY: project
:END:
*** TODO ngi0 aug01 funding submit!
DEADLINE: <2026-08-01 Sat>
:PROPERTIES:
:CREATED: [2026-06-01]
:CARRIED: 33
:END:
- [ ] wip pr: https://github.com/goodboy/tractor/pull/431
https://nlnet.nl/propose/
https://nlnet.nl/foundation/policies/generativeAI/
https://nlnet.nl/commonsfund/
*** TODO MEETING(s)
:PROPERTIES:
:CREATED: [2026-05-21]
:CARRIED: 33
:END:
** ui
:PROPERTIES:
:CATEGORY: piker.ui
:END:
*** TODO dpi_auto_calc
:PROPERTIES:
:CREATED: [2026-07-15]
:CARRIED: 29
:END:
- [ ] PUT PRs here
** brokers
:PROPERTIES:
:CATEGORY: piker.brokers
:END:
*** TODO IB: follow-up for search results bs!
:PROPERTIES:
:CREATED: [2026-05-21]
:CARRIED: 33
:END:
* https://www.pikers.dev/pikers/piker/issues/85
- [X] fix the `si.expiry..ib` search result?
- [ ] introspect why `Client.search_symbols()` is returning the
empty `.<venue>` case!
- presumably by something weird in the server-side endpoint?
- [ ] figure out better tuning of client side fuzzy-filter versus
server-side versus UI final display pass!
*** TODO ish for port to `uv`,
:PROPERTIES:
:CREATED: [2026-05-21]
:CARRIED: 33
:END:
* https://github.com/ib-api-reloaded/ib_async/issues/204
- [ ] mention to track in #68
*** TODO profiling: try newer libs,
:PROPERTIES:
:CREATED: [2026-05-21]
:CARRIED: 33
:END:
- [ ] https://github.com/pikers/piker/issues/337
** perf/tooling
:PROPERTIES:
:CATEGORY: piker.profile
:END:
- [ ] link existing GH issues for trying new
libs!
** FSP backfill: branch work
*** TODO review and commit FSP backfill synchronization
:PROPERTIES:
:ID: piker-fsp-backfill-sync-review-and-commit
:CREATED: [2026-09-03]
:CARRIED: 0
:REPO: /home/goodboy/repos/piker/wkts/fsp_backfill_sync
:END:
- [ ] review committed token-targeted history graphics change
(`fadab3d2`)
- [ ] review and commit the FSP stream-contract boundary
* yo yo yo
* `piker/data/__init__.py`
* `piker/data/ticktools.py`
* `piker/fsp/_api.py`
* `piker/fsp/_momo.py`
* staged portion of `piker/fsp/_volume.py`
- [ ] review and commit the synchronization and live-volume boundary
* explicit `Flume` RT/history cadence
* immutable history snapshots and absolute SHM-bound synchronization
* relevant backfill filtering and non-duplicating destination steps
* nonnegative sampled and derived volume
* M4 graphics-cache invalidation and unchanged-update suppression
* FSP synchronization regressions and Prompt-IO provenance
- [ ] review and commit the `piker-fsp-expert` skill boundary
- [ ] run boundary checks and the final test suite
- [ ] inspect each staged diff and commit message before committing
*** TODO add cumulative M4 downsampling for volume curves
:PROPERTIES:
:ID: piker-cumulative-m4-volume-downsampling
:CREATED: [2026-09-03]
:CARRIED: 0
:REPO: /home/goodboy/repos/piker/wkts/fsp_backfill_sync
:END:
- [ ] define cumulative bucket semantics for step-style data
* sum all source values represented by each pixel bucket
* preserve bucket time/index placement and zero-volume gaps
- [ ] add a sum-reducer variant alongside price min/max M4
- [ ] route unit and dollar-volume curves through cumulative downsampling
- [ ] keep price, OHLC, and non-cumulative signal curves on min/max M4
- [ ] preserve incremental appends, view changes, and cache invalidation
- [ ] test sum conservation across zoom levels and partial edge buckets
- [ ] profile the cumulative path against the existing M4 implementation
** TODO review and save the Trend Calculus prototype
:PROPERTIES:
:ID: piker-trend-calculus-prototype-review
:CREATED: [2026-10-05]
:CARRIED: 0
:REPO: /home/goodboy/repos/piker
:AI_HARNESS: codex
:AI_WORKTREE: /home/goodboy/repos/piker
:AI_BRANCH: trend_calc_on_jev
:ISSUE: https://github.com/pikers/piker/issues/187
:END:
[[file:/home/goodboy/repos/piker/docs/trend_calculus.rst][Research and prototype guide]]
- [ ] Regenerate and review six Trend Calculus behavior boundaries
on trend_calc_on_jev after workflow commit f148fbcc on
moar_ai_skillz lands; retain source/test pairs and inspect every
message before saving.
- [ ] Run each boundary check from the pinned package and the final
safe regression sequence; the working-tree offline suite passed 253
tests.
- [ ] Qualify one selected live source through startup, quiet bars,
skipped wakeups, history repair, replay and owner cancellation.
- [ ] Coordinate inherited stream/backfill work with
[[id:piker-fsp-backfill-sync-review-and-commit][the existing FSP synchronization task]].
*** TODO split draft TC commit after upstream history is settled
:PROPERTIES:
:ID: piker-trend-calculus-draft-commit-split
:CREATED: [2026-10-06]
:CARRIED: 0
:REPO: /home/goodboy/repos/piker
:AI_HARNESS: codex
:AI_WORKTREE: /home/goodboy/repos/piker
:AI_BRANCH: trend_calc_on_jev
:TAKEN_APPEND_OP: 85c6bdbe-7655-4323-b366-2732ac86e16c
:TAKEN_APPEND_REQUEST: 10de3a5f301761e43ba80988385d9eb180d41fc3025ed1cd66255f80e3c9fa98
:AI_DLOG: 01a109d6-d0ec-70d3-b2cf-2112a6780aa3
:AI_WKT: /home/goodboy/repos/piker
:END:
Draft snapshot: =e815fd77cfbf89b241bfabf19bda7b1c5f25c784=.
- [ ] Wait until upstream branches and their landing order are
settled; preserve a reference to the draft snapshot.
- [ ] Reset/unpack draft commit e815fd77 while retaining the full TC
changeset and preserving unrelated working-tree and staged work.
- [ ] Prepare a proper commit plan for that changeset with coherent
behavior boundaries, matching tests, docs and provenance.
- [ ] Run each boundary's checks and review its diff and message
before recommitting.
** TODO qualify Jev regime judgments against real responses
:PROPERTIES:
:ID: piker-jev-live-regime-qualification
:CREATED: [2026-10-05]
:CARRIED: 0
:REPO: /home/goodboy/repos/piker
:AI_HARNESS: codex
:AI_WORKTREE: /home/goodboy/repos/piker
:AI_BRANCH: trend_calc_on_jev
:END:
[[file:/home/goodboy/repos/piker/docs/trend_calculus.rst][Research and prototype guide]]
- [ ] Once TYPESAFE_API_KEY is available and a live request is
authorized, send one small synthetic multi-timeframe request
through the HTTPX/Trio client.
- [ ] Verify the documented request/response schema, model identity,
distributions, usage, errors and measured end-to-end latency.
- [ ] Evaluate useful regime labels and confidence on held-out trend
snapshots, including flat, reversing, aligned and conflicting
timeframes.
- [ ] Measure sustained pacing, coalescing, source freshness and cost
before enabling realtime judgments.
- [ ] Consider an SDK AnyIO port or Trio backend only after the
direct HTTP prototype demonstrates useful classifications.
** TODO connect finalized trends to the Jev provider-service
:PROPERTIES:
:ID: piker-jev-provider-service-integration
:CREATED: [2026-10-05]
:CARRIED: 0
:REPO: /home/goodboy/repos/piker
:AI_HARNESS: codex
:AI_WORKTREE: /home/goodboy/repos/piker
:AI_BRANCH: trend_calc_on_jev
:END:
[[file:/home/goodboy/repos/piker/docs/trend_calculus.rst][Research and prototype guide]]
- [ ] Obtain the separate system-1 provider-service repository or
interface and identify its input, output, lifecycle and ownership
contracts.
- [ ] Adapt serve_regimes and RegimeClassifier to the service nursery
and typed publication interface without mixing model labels into
deterministic trend state.
- [ ] Configure market, periods, model, credentials, request pacing,
evidence retention and freshness limits explicitly.
- [ ] Verify reconnect, revision/gap invalidation, backpressure,
stale-response rejection and shutdown with the actual service.
** TODO recover Pine source and check Trend Calculus parity
:PROPERTIES:
:ID: piker-trend-calculus-pine-parity
:CREATED: [2026-10-05]
:CARRIED: 0
:REPO: /home/goodboy/repos/piker
:AI_HARNESS: codex
:AI_WORKTREE: /home/goodboy/repos/piker
:AI_BRANCH: trend_calc_on_jev
:ISSUE: https://github.com/pikers/piker/issues/103
:END:
[[file:/home/goodboy/repos/piker/docs/trend_calculus.rst][Research and prototype guide]]
- [ ] Recover the human's Pine implementation and record its
provenance/version alongside the pinned Lua and Scala research.
- [ ] Compare startup seeding, extrema ties, FHLS splitting, opposing
pivots and recursive reversal orders.
- [ ] Add independent fixtures for meaningful semantic differences;
distinguish extremum time from confirmation time and mutable-bar
previews.
** TODO qualify multitimeframe pivots and regime annotations
:PROPERTIES:
:ID: piker-trend-regime-chart-qualification
:CREATED: [2026-10-05]
:CARRIED: 0
:REPO: /home/goodboy/repos/piker
:AI_HARNESS: codex
:AI_WORKTREE: /home/goodboy/repos/piker
:AI_BRANCH: trend_calc_on_jev
:END:
[[file:/home/goodboy/repos/piker/docs/trend_calculus.rst][Research and prototype guide]]
- [ ] Inspect actual 1/5/15-second layers and hover information on a
selected live market; compare confirmed pivots and regime
boundaries to finalized source evidence.
- [ ] Verify chart-axis placement, gaps, revisions, hidden charts,
duplicate cached feeds and owner-scoped cleanup.
- [ ] Qualify model rectangles at response receipt and expiry,
including stale classifications and source-segment changes.
- [ ] Measure quiet-bar display delay before deciding whether
publication needs an explicit output notification.
- [ ] Coordinate shared history-overlay lifecycle with the existing
chart-local gap overlay follow-ups; route any annotation IPC
factoring to the existing typed-dialog task.
** TODO qualify a native subsecond source for trend regimes
:PROPERTIES:
:ID: piker-trend-native-subsecond-sampling
:CREATED: [2026-10-05]
:CARRIED: 0
:REPO: /home/goodboy/repos/piker
:AI_HARNESS: codex
:AI_WORKTREE: /home/goodboy/repos/piker
:AI_BRANCH: trend_calc_on_jev
:END:
[[file:/home/goodboy/repos/piker/docs/trend_calculus.rst][Research and prototype guide]]
- [ ] Define the required high-frequency periods and producer
contract; the numeric/MTF APIs accept fractional periods but the
current native clock uses whole seconds.
- [ ] Extend or select the source/sampler time grid before enabling
subsecond chart/service periods; retain strictly increasing
coordinates and complete aligned buckets.
- [ ] Measure compute, SHM publication, service latency and chart
refresh under the selected cadence, including gaps, replay and
cancellation.
** TODO migrate clearing to strict typed messaging
:PROPERTIES:
:ID: piker-typed-clearing-migration
:CREATED: [2026-10-06]
:CARRIED: 0
:REPO: /home/goodboy/repos/piker
:AI_HARNESS: codex
:AI_WORKTREE: /home/goodboy/repos/piker/wkts/typed_clearing_protocol
:AI_BRANCH: wkt/typed_clearing_protocol
:END:
[[file:/home/goodboy/repos/piker/wkts/typed_clearing_protocol/plans/opencode/typed-ems-messaging.md::823][Migration plan and commit boundaries]]
Foundation evidence: plan commit 1793b47c and implementation commit
7fee9b86; human reports both pushed. Strict schemas, validation,
directional stream helpers and the pure OrderState reducer are
implemented. Prior qualification: 65 offline tests and eight IPC
tests passed. Production endpoints still use the legacy protocol.
- [ ] Port paper and its paired EMS consumer together.
* [ ] Typed startup, requests, events and open-order completion.
* [ ] Deterministic synthetic ticks and distinct execution IDs.
- [ ] Port Binance, Kraken and IB individually with paired EMS handling.
* [ ] Shared offline conformance and provider translation fixtures.
* [ ] Preserve execution identity, signed positions and account mapping.
- [ ] Integrate OrderState into EMS routing and broker-event handling.
* [ ] Bound pre-ack buffering by age in its asynchronous owner.
* [ ] Preserve cancel intent, fill deduplication and late-event handling.
- [ ] Migrate client/EMS startup, local queues and UI consumers together.
* [ ] Replace wire snapshot mutation and dictionary reconstruction.
- [ ] Enforce each migrated endpoint pair with Tractor payload specs.
* [ ] Narrow receive sets; validate startup, RPC arguments and results.
* [ ] Reject malformed, wrong-direction and wrong-phase messages.
- [ ] Qualify real adapter and actor behavior offline.
* [ ] Cancel-before-ack; early, duplicate and late fills.
* [ ] Empty/populated startup snapshots and two-client order isolation.
* [ ] Abrupt transport EOF, cancellation and bounded failure teardown.
* [ ] Repeated lifecycle tests under supported spawn methods.
- [ ] Resolve the duplicate pytest --ll option before a Tractor pin upgrade.
- [ ] Document the position-broadcast isolation limitation and policy.
- [ ] Remove legacy IPC paths after all paired migrations qualify.
- [ ] Propose a reviewable commit plan for each qualified implementation.
* TODO Qualify chart disconnect and reattach with independent service ownership
:PROPERTIES:
:ID: piker-chart-disconnect-reattach-e2e
:CREATED: [2026-10-20]
:CARRIED: 0
:REPO: /home/goodboy/repos/piker
:CATEGORY: piker.testing
:AI_HARNESS: opencode
:TAKEN_APPEND_OP: piker-chart-disconnect-reattach-e2e-20261020-v2
:TAKEN_APPEND_REQUEST: 5963b29d2e6372c8864831fdee8945a3e064f5084d9abf0331f92c0b5171647c
:END:
Build process-isolated chart journeys that distinguish surviving shared
services and consumers from clean chart exit. Preserve original errors
and deterministic phase evidence; do not declare a journey green merely
because another chart remains usable.
Next action: reconcile the current testing and evaluation branches and
their actual Piker/Tractor imports, then implement a suite-owned pikerd
with independent chart processes. No implementation has started for this
handoff as of this entry; source/state inspection is recorded below.
Context:
Human-directed assignment to e2e_qa_mngr from proj_super. The source
handoff describes proposed tests, not existing runnable nodes:
[[file:/home/goodboy/repos/piker/wkts/ib_downstream_pin_eval/ai/plans/20261019-chart-disconnect-e2e-handoff.md][Chart disconnect and reattach specification and manual evidence]]
Existing foundations, inspected 2026-10-20:
- wkt/live_chart_qtractor at 4ab49082: structured lifecycle 1a2d4893,
prerequisite fixes 5c17419c, opt-in Kraken reproducer 4ab49082.
Application/test work is committed; only local workflow configuration
remains dirty. Use this as the initial working candidate, not proof
that the evaluation fixes are integrated.
- wkt/human_iface_e2e at aadfd1cf is clean and supplies the headless
QApplication isolation foundation.
- wkt/replay_provider_e2e at 06fee700 is clean. Its real feed/control
fixture has acknowledged sequence barriers. The observed disconnect
races justify narrow controlled input, not a replay-first rewrite.
- wkt/ib_downstream_pin_eval remains at eed58566 with dirty dependency,
logger, daemon and EMS compatibility changes.
- wkt/ib_disconnect_integration at c0dae89a contains committed IB
disconnect work; its provenance/tooling state remains dirty.
- Tractor wkt/ctx_final_return_disconnect in address_logging_levels is
at 0553fcf8 with dirty final-Return guard and regression changes.
Read them as candidate evidence, not an already-landed fix.
Reuse tests/ui/conftest.py, the Qtractor lifecycle regression,
tests/conftest.py open_test_pikerd and owned config propagation, and
the replay branch's acknowledged feed controls where applicable.
[[file:/home/goodboy/repos/piker/wkts/live_chart_qtractor/tests/ui/test_live_chart.py][Committed live-chart reproducer]]
[[file:/home/goodboy/repos/piker/wkts/replay_provider_e2e/plans/opencode/live-backend-chart-e2e-review.md][Live-provider-first roadmap]]
The current sanitized live_chart_qtractor interpreter resolves Piker
from that worktree but Tractor from its py313 site-packages; installed
Trio is 0.32.0 and PyQt6 is 6.10.1. This is not the manual evaluation's
Tractor checkout or PyQt6 6.11.0 environment. Resolve exact versions and
module paths again before any comparison. Inspect the duplicate pytest
--ll registration blocker before selecting the evaluation environment.
Concerns and authority:
This assignment does not authorize commits, history changes, live IB
connections, any orders, or attachment to the human's running daemon.
Order journeys may be designed and implemented as gated tests; do not
execute them without explicit authorization. Live-provider qualification
requires explicit selection and applicable human permission.
The existing Kraken run reached history and paper EMS but readiness
preceded rt_linked.mode, Qt reported abstract paint calls, and teardown
left a paperboi child. That exact orphan was subsequently stopped by
human-authorized SIGINT. Guest completion did not prove descendant exit.
Manual IB trials demonstrate daemon/survivor continuity, not clean exit.
Cancellation-only groups, DebugRequestError, hard-reaping and SIGSEGV
remain unresolved. Do not suppress any of these to make E2E green.
Canonical native-crash investigation, separate from this test assignment:
[[id:piker-qt6-chart-teardown-sigsegv][Diagnose Qt6 chart teardown SIGSEGV with native backtraces]]
[[file:../ai/diagnostics/20261019-chart-253598/README.md][SIP wrapper access during PyQt atexit: diagnostics and limits]]
The core locates the native fault; it does not establish the underlying
ownership cause. This task records exit evidence without claiming repair.
Coordinate order replay/cancellation assertions with
[[id:piker-typed-clearing-migration][Strict typed clearing migration]];
do not duplicate its protocol migration or infer it has landed.
Execution:
- [ ] Reconcile source revisions and dirty work before integrating; record
the selected branch, reused fixtures, and exact runtime imports.
- [ ] Own registrar/pikerd A independently of chart processes B and C;
give each chart its own QApplication and Tractor root. Separate discovery
from listener addresses, use a test-unique registry and loopback port
zero, and assert actual chart listeners are nonzero and distinct.
- [ ] Isolate config, storage, settings and account state before imports
in every child; never reuse human ports 6116/3333 or human accounts.
- [ ] Provide an external observation/control channel and owned process
handles that outlive charts. Capture actor UUID/name/PID/listeners,
child relations and exact SHM ownership including unpublished child SHM.
- [ ] Establish readiness after feed/history attachment, bound views,
FSP streams, search handlers and EMS entry. Race waits against original
process/session failure, not only timeouts.
- [ ] Implement same-market alternating departures: ready B/C, close B,
prove the SAME C consumes a post-close quote/FSP update, reopen B,
close C, prove replacement B survives, then close/reopen the last chart.
Add the distinct-market variant to prove sibling-feed independence.
- [ ] Implement last-consumer repeated reattachment to the SAME A and
service/client generations. Compare actual window close, structured
shutdown, root-only SIGINT and terminal-equivalent process-group SIGINT
as separate variants, with PID/PGID ownership recorded before signaling.
- [ ] Drive actual search results with QtBot in the chart worker; compare
visible versus normally dismissed results before detach and reattach.
- [ ] Gate startup cancellation with entered/release acknowledgements at
early acquisition, FSP child bootstrap before imports/registration,
history publication and FSP stream establishment. Hold only C's work,
retain B's progress, permit unwind, and prove a new C can start afterward.
- [ ] Repeat gated startup aborts with no other consumer. Include debug
mode during FSP imports; retain the original interruption independently
from a secondary local-actor IPC stdio-lock DebugRequestError.
- [ ] Implement separately selected order continuity journeys for Piker
paper EMS, IB paper-account broker-routed limits, and EMS-held dark
orders. Execution remains blocked pending explicit order authorization.
Include multi-expiry market-specific dialogs and cancellation of both
order types after reattach.
Verification:
- [ ] Report service survival and chart exit as separate outcomes even
when either fails. Collect survivor evidence after departure errors,
then aggregate originals rather than hiding them behind cleanup.
- [ ] Prove fresh service RPCs and post-action provider data in the same
survivor, including FSP consumption. Deterministic inputs need a matched
sequence; live quotes need not change price. Sampler ticks alone fail.
- [ ] Assert stable service/provider generations, owned child exit and
SHM lifetime according to its actual owner. Shared cached feed SHM may
remain with A; endpoint closure alone does not prove PID exit.
- [ ] Fail clean-exit checks for SIGSEGV/abort, stuck children, hard reap,
unexpected debugger entry and cancellation-only application errors.
Do not blanket-suppress TransportClosed or mixed exception groups.
- [ ] Preserve baseline/fixed results, stdout/stderr, return codes,
source versions, phase acknowledgements, actor/RPC/context identities,
cancellation provenance, and available fault-handler/core evidence.
- [ ] For authorized order qualification, verify canonical OID/reqid
continuity, idempotent replay, market routing, working-order counts,
position continuity and terminal cancellation. Repeated status messages
alone do not prove duplicate orders. Cover reqid 5042-style error replies
with oid=None and the unsubmitted-order-None cancellation symptom.
- [ ] Run each selected tier headlessly with bounded ownership-scoped
cleanup. Preserve fresh-process fixture isolation; no broad process
killing, SHM cleanup, or human-daemon access.
Handback:
Return results to source piker, task piker-chart-disconnect-reattach-e2e:
- Selected implementation branch, exact source revisions and reused
fixtures, including any unintegrated prerequisites.
- Exact implemented runnable test node IDs and synchronization gates;
distinguish proposed names from implemented nodes.
- Baseline/fixed results with service survival and chart/process exit
reported independently, including unresolved failures.
- Remaining coverage gaps, required public/live-IB qualification and
order authorization, linked to the separate native-crash concern.
Use guarded store updates for evidence. Preserve all human-owned task
and checkbox states; passing tests and commits are not acceptance.

View File

@ -0,0 +1,8 @@
version = 1
[[sources]]
id = "piker"
path = "current.org"
repo = ".."
enabled = true
agenda = true

View File

@ -17,6 +17,21 @@ track new integration ideas and proposals in
| Tool | Directory | Status | | Tool | Directory | Status |
|------|-----------|--------| |------|-----------|--------|
| [Claude Code](https://github.com/anthropics/claude-code) | [`claude-code/`](claude-code/) | active | | [Claude Code](https://github.com/anthropics/claude-code) | [`claude-code/`](claude-code/) | active |
| [OpenCode](https://opencode.ai/) | [`opencode/`](opencode/) | active |
## Shared Skills
Provider-neutral skill bundles should use the portable Agent Skills
subset and live under `.agents/skills/` as their single source.
OpenCode discovers that project directory natively. Relative links in
provider directories such as `.claude/skills/` may bridge clients which
do not yet scan `.agents/skills/`; never copy the skill body.
Existing repo skills under `.claude/skills/` predate this convention
and can migrate separately without coupling their history to new skills.
Harness-specific command files should only delegate to a
shared skill. They must not duplicate the skill body.
## Adding a New Integration ## Adding a New Integration
@ -32,7 +47,7 @@ ai/
├── README.md # <- you are here ├── README.md # <- you are here
├── claude-code/ ├── claude-code/
│ └── README.md │ └── README.md
├── opencode/ # future ├── opencode/
│ └── README.md │ └── README.md
└── <your-tool>/ └── <your-tool>/
└── README.md └── README.md
@ -45,6 +60,7 @@ ai/
- Each integration doc should describe **what** - Each integration doc should describe **what**
the skill does, **how** to invoke it, and any the skill does, **how** to invoke it, and any
**output** artifacts it produces **output** artifacts it produces
- Keep docs concise; link to the actual skill - Keep docs concise; link to the shared skill source files
source files (under `.claude/skills/`, etc.)
rather than duplicating content rather than duplicating content
- Keep skill bodies harness-neutral; isolate slash-command
syntax and permissions in each harness integration

View File

@ -1,15 +1,18 @@
# Claude Code Integration # Claude Code Integration
[Claude Code](https://github.com/anthropics/claude-code) [Claude Code](https://github.com/anthropics/claude-code)
skills and workflows for piker development. integration for piker's shared coding-harness skills.
## Skills ## Skills
| Skill | Invocable | Description | | Skill | Invocable | Description |
|-------|-----------|-------------| |-------|-----------|-------------|
| [`commit-msg`](#commit-msg) | `/commit-msg` | Generate piker-style commit messages | | [`commit-msg`](#commit-msg) | `/commit-msg` | Generate piker-style commit messages |
| `piker-clearing-expert` | auto | Clearing, order-control, and accounting invariants |
| `piker-conc-expert` | auto | Actor-tree and structured-concurrency invariants |
| `piker-profiling` | auto | `Profiler` API patterns for perf work | | `piker-profiling` | auto | `Profiler` API patterns for perf work |
| `piker-slang` | auto | Communication style + slang guide | | `piker-slang` | auto | Communication style + slang guide |
| `py-codestyle` | auto | Python source conventions |
| `pyqtgraph-optimization` | auto | Batch rendering patterns | | `pyqtgraph-optimization` | auto | Batch rendering patterns |
| `timeseries-optimization` | auto | NumPy/Polars perf patterns | | `timeseries-optimization` | auto | NumPy/Polars perf patterns |
@ -17,8 +20,12 @@ Skills marked **auto** are background knowledge
applied automatically when Claude detects relevance. applied automatically when Claude detects relevance.
Only `commit-msg` is user-invoked via slash command. Only `commit-msg` is user-invoked via slash command.
Skill source files live under New portable skill source files live under
`.claude/skills/<skill-name>/SKILL.md`. `.agents/skills/<skill-name>/SKILL.md`. Relative links under
`.claude/skills/` expose them to Claude Code without copying bodies.
Existing pre-migration skills can remain under `.claude/skills/`.
Claude-specific behavior belongs in command or settings files, not
shared skill bodies.
--- ---
@ -84,8 +91,7 @@ description context. Examples:
**Footer** (always): **Footer** (always):
``` ```
(this patch was generated in some part by (this patch was generated in some part by
[`claude-code`][claude-code-gh]) `claude-code` using `<model>` (`<provider>`))
[claude-code-gh]: https://github.com/anthropics/claude-code
``` ```
### Output Files ### Output Files
@ -94,7 +100,8 @@ After generation, the commit message is written to:
``` ```
.claude/ .claude/
├── <timestamp>_<hash>_commit_msg.md # archived ├── skills/commit-msg/msgs/
│ └── <timestamp>_<hash>_commit_msg.md
└── git_commit_msg_LATEST.md # latest └── git_commit_msg_LATEST.md # latest
``` ```
@ -102,18 +109,10 @@ Where `<timestamp>` is ISO-8601 with seconds and
`<hash>` is the first 7 chars of the current `<hash>` is the first 7 chars of the current
`HEAD` commit. `HEAD` commit.
Use the latest file to feed into `git commit`: Use the latest file with an editor review before commit:
```bash ```bash
git commit -F .claude/git_commit_msg_LATEST.md git commit --edit --file .claude/git_commit_msg_LATEST.md
```
Or review/edit before committing:
```bash
cat .claude/git_commit_msg_LATEST.md
# edit if needed, then:
git commit -F .claude/git_commit_msg_LATEST.md
``` ```
### Examples ### Examples
@ -128,56 +127,32 @@ Add `MktPair.fqme` property for symbol resolution
Factor `.claude/skills/` into proper subdirs Factor `.claude/skills/` into proper subdirs
Deats, Deats,
- `commit_msg/` -> `commit-msg/` w/ enhanced - use portable Agent Skills frontmatter
frontmatter - keep harness commands as thin delegates
- all background skills set `user-invocable: false` - share supporting references across harnesses
- content split into supporting files
(this patch was generated in some part by (this patch was generated in some part by
[`claude-code`][claude-code-gh]) `claude-code` using `<model>` (`<provider>`))
[claude-code-gh]: https://github.com/anthropics/claude-code
``` ```
### Frontmatter Reference ### Shared Frontmatter
The skill's `SKILL.md` uses these Claude Code Skills use the Agent Skills fields understood by both
frontmatter fields: Claude Code and OpenCode:
```yaml ```yaml
--- ---
name: commit-msg name: commit-msg
description: > description: >
Generate piker-style git commit messages... Generate piker-style git commit messages. Use when...
argument-hint: "[optional-scope-or-description]" compatibility: Requires git.
disable-model-invocation: true metadata:
allowed-tools: author: goodboy
- Bash(git *) version: "1.0"
- Read
- Grep
- Glob
- Write
--- ---
``` ```
| Field | Purpose | Live git context is gathered by the skill at execution
|-------|---------| time. Claude-only dynamic interpolation and tool-policy
| `argument-hint` | Shows hint in autocomplete | frontmatter are intentionally excluded so another harness
| `disable-model-invocation` | Only user can trigger via `/commit-msg` | can execute the same workflow.
| `allowed-tools` | Tools the skill can use |
### Dynamic Context
The skill injects live data at invocation time
via `!`backtick`` syntax in the `SKILL.md`:
```markdown
## Current staged changes
!`git diff --staged --stat`
## Recent commit style reference
!`git log --oneline -10`
```
This means the staged diff stats and recent log
are always fresh when the skill runs -- no stale
context.

View File

@ -0,0 +1,143 @@
# Split `brokerd.<broker>` into trading-only `brokerd` + new `datad.<broker>`
## Context
Today a single `brokerd.<broker>` actor hosts BOTH concerns:
- **data feed service tasks**: the `_FeedsBus` + `open_feed_bus()` ep (`piker/data/feed.py:464`), per-symbol `allocate_persistent_feed()` tasks (shm writers via `sample_and_broadcast()`, history backfill via `piker.tsp`), plus backend eps `stream_quotes`, `open_history_client`, `open_symbol_search`, `get_mkt_info` from `piker/brokers/<backend>/feed.py`/`symbols.py`,
- **live order-control tasks**: `open_trade_dialog()` from `piker/brokers/<backend>/broker.py`, driven by `emsd`.
The codebase already anticipates this split: `piker/data/validate.py:70-91` groups backend eps into `'datad'` vs `'brokerd'` kinds, `piker/brokers/ib/__init__.py:62-70` already declares `_brokerd_mods`/`_datad_mods`, and `piker/brokers/_daemon.py:62` carries the literal TODO *"rename the daemon to datad prolly once we split up broker vs. data tasks into separate actors?"*. This work executes that split.
**User-decided constraints:**
- `datad.<broker>` is a **sibling** of `brokerd.<broker>` under `pikerd`, spawned via the existing `Services` + `maybe_spawn_daemon()` machinery (`piker/service/_daemon.py:46`).
- **Hard cutover, staged by layer** — no dual-mode runtime flag; every stage lands fully working.
- Post-split `brokerd` is **trading-only and lazily spawned solely by emsd's** `open_brokerd_dialog` path; UI/CLI/feed code never spawns it. Chart-only and paper sessions run with **zero** brokerd processes.
## Target topology
```
pikerd
├── datad.ib feed bus, shm writers, tsp history, symbol search
├── brokerd.ib open_trade_dialog only (EMS-spawned, lazy)
├── emsd
│ └── paperboi.ib (paper mode; opens its feed via datad)
└── samplerd
```
## Key verified facts (load-bearing)
- `feed.portals` has exactly ONE trading consumer: `piker/clearing/_ems.py:671` (`portal = feed.portals[brokermod]` → handed to `open_brokerd_dialog`). This is the single coupling forcing feed + trading into one actor.
- `assert 'brokerd' in servicename` at `piker/data/feed.py:502` is the only actor-name assert in the tree.
- `piker ledger` (`piker/accounting/cli.py:100-157`) is a hidden consumer: it calls `broker_init()` directly, spawns its own ad-hoc actor, enters `_setup_persistent_brokerd`, then calls `open_brokerd_dialog(brokermod, portal, ...)` **with an explicit portal** — the new signature must keep a `portal:` override param.
- kraken's feed→broker coupling is mild: `kraken/broker.py:77-81` imports `NoBsWs`/`open_autorecon_ws` (really from `piker.data._web_bs`) and `stream_messages` (pure parser) from `feed.py`. In-process imports only — `enable_modules` gates RPC, not imports. No live shared state; each actor opens its own WS.
- ib: default `client_id=6116` (`ib/api.py:1320`) with linear retry `clientId=client_id + i` (`:1403`). Two ib actors connecting concurrently will collide → needs a role-based id offset. Both daemons need `_spawn_kwargs={'infect_asyncio': True}` (copied by both init fns).
- binance has no `symbols.py` (`get_mkt_info`/`open_symbol_search` live in `binance/feed.py`); kucoin/questrade/robinhood are flat single-file with NO `__enable_modules__`; deribit has no `broker.py`.
- `tests/test_services.py:80,185` assert `brokerd` actor names incl. the paper-mode flow asserting `brokerd.kraken` spawns (`:229,:237`) — must invert post-split.
- `_root_modules` (`piker/service/_actor_runtime.py:156`) must gain the new datad daemon mod so `pikerd_portal.run(spawn_datad, ...)` resolves.
- `piker/brokers/core.py:175` `symbol_search` does `portal.run(search_w_brokerd)` where the target fn lives in `piker.brokers.core` → that mod must stay RPC-enabled in datad.
## Module decision
New file **`piker/data/_daemon.py`** hosts all datad machinery (`_setup_persistent_datad`, `datad_init`, `spawn_datad`, `maybe_spawn_datad`, `_datad_service_mods`). Mirrors the `piker.brokers._daemon` / `piker.data._sampling` (samplerd) per-subsystem convention and satisfies the existing TODO at `brokers/_daemon.py:49` ("move this def to the `.data` subpkg"). `piker.brokers._daemon` keeps brokerd-only code, slimmed. Do NOT over-DRY the two ~40-line init fns into a shared factory yet (samplerd precedent accepts the duplication).
---
## Stage 0 — prep: backend module grouping + ep introspection (no topology change)
1. `piker/data/validate.py`: add a `get_eps(mod, kind) -> dict[str, Callable]` helper returning the backend's defined eps for a daemon-kind from `_eps` (missing eps excluded). Used later by both init fns + fail-fast checks.
2. Declare daemon-kind module groups in each split backend's `__init__.py`, keeping `__enable_modules__` as the (deduped) union so behavior is unchanged:
- `kraken`: `_brokerd_mods = ['api', 'broker']`, `_datad_mods = ['api', 'feed', 'symbols']`
- `binance`: `_brokerd_mods = ['api', 'broker']`, `_datad_mods = ['api', 'feed']`
- `deribit`: `_brokerd_mods = []`, `_datad_mods = ['api', 'feed']`
- `ib`: adjust existing groups so each includes `'api'`
- flat backends (kucoin etc.): no attrs — init fns fall back to enabling just `modpath` (existing behavior).
3. Hygiene: `kraken/broker.py:77-81` imports `NoBsWs`/`open_autorecon_ws` from `piker.data._web_bs` directly (keep the `stream_messages` import from `.feed`).
**Gate**: full pytest green, zero behavior change.
## Stage 1 — introduce `datad` daemon machinery (additive)
New `piker/data/_daemon.py`:
- `_datad_service_mods: list[str]` — datad-always-enabled mods (successor to the data side of `_data_mods` from `brokers/_daemon.py:52`): `['piker.brokers.core', 'piker.brokers.data', 'piker.data', 'piker.data.feed', 'piker.data._sampling', 'piker.data._daemon']`.
- `_setup_persistent_datad(ctx, brokername, loglevel)` — `@tractor.context` fixture; logging boilerplate (as `_setup_persistent_brokerd:81-88`), then allocates the actor-global feed bus exactly as the brokerd fixture does today (`brokers/_daemon.py:105-121`): `assert not feed._bus`, open service nursery, `feed.get_feed_bus(brokername, service_nursery)`, `ctx.started()`, `sleep_forever()`.
- `datad_init(brokername, ...)` — mirrors `broker_init()` (`brokers/_daemon.py:132`): actor name `f'datad.{brokername}'`, copies backend `_spawn_kwargs` (**critical for ib infect_asyncio**), builds `enable_modules` from `getattr(brokermod, '_datad_mods', getattr(brokermod, '__enable_modules__', []))`.
- `spawn_datad(brokername, ...)` — mirrors `spawn_brokerd()` (`brokers/_daemon.py:202`): `Services.actor_n.start_actor(dname, enable_modules=_datad_service_mods + backend_mods, ...)` + `Services.start_service_task(dname, portal, _setup_persistent_datad, ...)`.
- `maybe_spawn_datad(brokername, ...)` — wraps `maybe_spawn_daemon(service_name=f'datad.{brokername}', service_task_target=spawn_datad, ...)` exactly like `maybe_spawn_brokerd` (`brokers/_daemon.py:256`).
Supporting edits:
- `piker/service/_actor_runtime.py:156`: add `'piker.data._daemon'` to `_root_modules` (keep `'piker.brokers._daemon'`).
- `piker/service/__init__.py`: re-export `spawn_datad`/`maybe_spawn_datad` next to the brokerd ones (`:56-57`).
- `tests/test_services.py`: new `test_datad_spawn` — `open_test_pikerd` + `maybe_spawn_datad('kraken')` + `ensure_service('datad.kraken')`. (Do NOT route a feed through it yet — `open_feed_bus`'s assert still says brokerd.)
**Gate**: suite green + new spawn test; nothing routes through datad yet.
## Stage 2 — clearing layer: emsd self-spawns brokerd (decouple from `feed.portals`)
Sequenced BEFORE the feed cutover so live trading works at every boundary (otherwise `feed.portals` would hand emsd a datad portal).
`piker/clearing/_ems.py`:
1. `open_brokerd_dialog()` (`:336`) new signature: `(brokermod, exec_mode, fqme=None, portal: tractor.Portal|None = None, loglevel=None)`. Internals: keep trades-ep detection (`:400-417`); acquire the brokerd actor ONLY when a live ep will actually open, via a small inner `@acm _acquire_live_portal()` that yields the passed `portal` if given (the `piker ledger` path) else `async with maybe_spawn_brokerd(brokermod.name, loglevel=loglevel)` — **the single place brokerd boots post-split**. Move the eager `portal.open_context(trades_endpoint, ...)` construction (`:425`) inside that block; the paper short-circuit (`:432`) never touches it.
2. `Router.maybe_open_brokerd_dialog()` (`:581`) — drop the `portal` param; `Router.open_trade_relays()` (`:640`) — delete `portal = feed.portals[brokermod]` (`:671`) and the pass-through (`:685`).
3. `piker/accounting/cli.py:144`: switch to keyword form `open_brokerd_dialog(brokermod, exec_mode=..., portal=portal, loglevel=...)`.
Pre-cutover this is a pure refactor: emsd's `maybe_spawn_brokerd` just *finds* the already-running brokerd via the registry.
**Gate**: `tests/test_ems.py` + services suite green; manual paper order on kraken; `piker ledger` smoke.
## Stage 3 — feed layer hard cutover to datad
1. `piker/data/feed.py`: import `maybe_spawn_datad` (replacing `maybe_spawn_brokerd`, `:56-58`); `open_feed()` `:895` → `maybe_spawn_datad(...)` (rename `brokerd_ctxs` → `datad_ctxs`); `open_feed_bus()` `:502` → `assert 'datad' in servicename`; comment sweep.
2. `piker/brokers/_daemon.py` `_setup_persistent_brokerd()`: slim to trading-only fixture — logging setup, `ctx.started()`, `sleep_forever()`. Drop the bus alloc, `assert not feed._bus`, the service nursery (backend `open_trade_dialog` ctxs own their task trees), the `eg.ExceptionGroup` handler, and the stale `_FeedsBus` TYPE_CHECKING import. (`piker ledger`'s ad-hoc actor enters this same slimmed fixture — exactly what it needs.)
3. Repoint remaining data-flavored spawn sites `maybe_spawn_brokerd` → `maybe_spawn_datad`:
- `piker/ui/_app.py:40,57` (symbol search; optionally rename `install_brokerd_search` → `install_datad_search`, def at `piker/data/feed.py:766`)
- `piker/brokers/core.py:33,175` (`symbol_search`)
- `piker/brokers/cli.py:38,190,320` (`brokercheck`, `record`)
- `piker/ui/cli.py:30,72,121` (legacy kivy monitor/optschain) + best-effort `piker/ui/kivy/option_chain.py:498` `wait_for_actor('brokerd')` → `'datad'`
4. `tests/test_services.py`: `:80,:185` `actor_name = 'datad'`; paper-mode test asserts `datad.kraken` + `paperboi.kraken` + `emsd` AND adds the headline negative check: `assert 'brokerd.kraken' not in services.service_tasks`.
**Gate**: full suite with inverted assertions; manual: chart on binance/kraken shows `datad.<broker>` in `piker services` and NO brokerd; paper order fills; symbol search works; history backfill sane.
## Stage 4 — caps-sec slimming, validation, ib client-id
1. `piker/brokers/_daemon.py`: delete `_data_mods` (`:52`); `spawn_brokerd()` `:236` → `enable_modules=tractor_kwargs.pop('enable_modules')`; `broker_init()` `:184` reads `getattr(brokermod, '_brokerd_mods', getattr(brokermod, '__enable_modules__', []))`. Resulting brokerd enable set has no `piker.data.*` at all.
2. Fail-fast ep validation via Stage-0 `validate.get_eps()`: `broker_init` raises a clear error when `get_eps(mod, 'brokerd')` is empty (e.g. "kucoin is datad-only — use paper mode"); `datad_init` warns analogously.
3. ib client-id mitigation in `load_aio_clients()` (`ib/api.py:~1316`): role-based offset when `client_id` is the 6116 default (e.g. `+16` when `'datad' in tractor.current_actor().name`), optionally configurable via `brokers.toml` keys.
4. Docs/comment sweep: resolve `brokers/_daemon.py:62` TODO, `piker/cli/__init__.py:253` TODO, daemon list in `piker/service/__init__.py:21` docstring.
**Gate**: full suite; audit greps clean: `grep -rn maybe_spawn_brokerd piker/` hits only `clearing/_ems.py`, `service/__init__.py`, `brokers/_daemon.py`; no spawn-path `'brokerd'` literals left in `piker/data`, `piker/ui`, `piker/brokers/cli.py`.
---
## Risk register
| Risk | Sev | Mitigation |
|---|---|---|
| ib: `datad.ib` + `brokerd.ib` both connect to TWS/gw; `client_id=6116` collision burns `connect_timeout` retries | High (live ib only) | Stage 4 role-based id offset; both daemons inherit `infect_asyncio` via `_spawn_kwargs` copy in both init fns |
| `piker ledger` ad-hoc actor path silently broken by signature change | Med | `portal:` override kept on `open_brokerd_dialog` (Stage 2); explicit smoke test |
| datad-only backends (kucoin/deribit) → useless brokerd spawn | Low | already covered: `open_brokerd_dialog` forces `exec_mode='paper'` when no trades ep (`_ems.py:413-417`) BEFORE the lazy spawn; Stage 4 fail-fast |
| brokerd in-process symbology/ledger needs (ib `broker.py` imports `.symbols`; kraken uses `stream_messages`) | None | verified pure in-process imports; `enable_modules` gates RPC only |
| paper-mode test asserts brokerd spawns (`test_services.py:237`) | Low | Stage 3 deliberately inverts it |
| doubled per-broker clients (HTTP/WS, symcache loads) | Low | each actor already opens its own WS today; symcache is disk-read-mostly |
## Verification (per stage gates above, plus end-to-end)
- `pytest tests/test_services.py tests/test_feeds.py tests/test_ems.py` at every stage (kraken/binance public endpoints, no creds needed).
- Manual smoke matrix post-Stage-3: `pikerd -l info` + `piker chart btcusdt.spot.binance` → `piker services` shows `datad.binance`, no brokerd; submit paper order → `paperboi.binance` appears, still no brokerd; symbol search; `piker ledger <broker>.paper`.
- If ib creds/gateway available: live ib chart + small live order to exercise dual-actor client-id path (Stage 4).
## Critical files
- **new** `piker/data/_daemon.py` — all datad machinery
- `piker/brokers/_daemon.py` — slimmed brokerd fixture/init/spawn
- `piker/data/feed.py` — spawn cutover + actor-name assert
- `piker/clearing/_ems.py` — emsd lazy brokerd spawn (`open_brokerd_dialog`, `Router`)
- `piker/service/_actor_runtime.py`, `piker/service/__init__.py` — root mods + re-exports
- `piker/data/validate.py` — `get_eps()` helper
- backend `__init__.py`s (kraken/binance/deribit/ib) — `_datad_mods`/`_brokerd_mods`
- `piker/accounting/cli.py` — keyword-form dialog call
- `tests/test_services.py` — inverted + new assertions

View File

@ -0,0 +1,55 @@
# OpenCode Integration
[OpenCode](https://opencode.ai/) discovers provider-neutral skills
directly from `.agents/skills/` and pre-migration piker skills from
`.claude/skills/`. There is no second copy of each skill to keep in
sync.
## Available Skills
| Skill | Activation | Specialization |
|-------|------------|----------------|
| `commit-msg` | `/commit-msg` or explicit request | Piker commit-message style and artifacts |
| `piker-clearing-expert` | automatic | Clearing, order-control, and accounting invariants |
| `piker-conc-expert` | automatic | `tractor` actor topology, RPC, and cancellation |
| `piker-profiling` | automatic | Cross-actor `Profiler` instrumentation |
| `piker-slang` | automatic | Project communication style |
| `py-codestyle` | automatic | Python source conventions |
| `pyqtgraph-optimization` | automatic | Batched Qt graphics rendering |
| `timeseries-optimization` | automatic | NumPy and Polars timeseries performance |
OpenCode's project command lives at
`.opencode/commands/commit-msg.md`. It delegates to the
shared `commit-msg` skill and only supplies command
arguments; workflow logic remains in the skill.
A user-local skill with the same name may override a repo
skill. Treat `opencode debug skill` as authoritative when
diagnosing which source is active.
## Verify Discovery
From the repository root:
```console
opencode debug skill
opencode debug config
```
`opencode debug skill` should list the piker skills from
the project checkout. Restart OpenCode after changing a
skill or command because configuration-time files are not
hot-reloaded into an active session.
## Portability Rules
- Use only portable Agent Skills frontmatter in shared
`SKILL.md` files.
- Keep harness-specific command syntax under
`.opencode/commands/`.
- Detect the active harness and model at runtime; do not
hardcode Claude Code attribution in shared workflows.
- Keep supporting references beside each shared skill so
relative links resolve in every harness.
- Put new provider-neutral bundles under `.agents/skills/`; use
relative provider links only when a harness requires one.

View File

@ -0,0 +1,34 @@
---
created_at: 2026-10-03T00:18:37.208688+00:00
generated_by:
harness: codex
provider: openai
model: gpt-6
git_ref: 9115d24
scope: data
substantive: true
raw_file: 20261003T001837Z_9115d24_project_stores.raw.md
---
## Prompt
Split the LNS working corpus into per-repository federated stores on
open-wkt branches and connect them to one global storespace. Preserve
human task states. Add a local agent workflow, update taken-export,
and design rollover, supervised concurrency, activity and ranked views.
## Response summary
Create the repository's migration branch and preserve its imported
scope, with revision-aware local source configuration and a shared
local taken skill. Keep original checkout stores and indexes intact.
Existing live branch stores remain independently registered. The LNS
manifest and migration ledger describe the authoritative split paths.
## Human edits
The human chose the per-repo split and worktree delivery, authorized
local auto-mode, and required supervisor-based locking: the supervisor
owns write authority and can revoke delegated human/subagent edit
grants through cancellation. The agent applied these directions.
No task/checkbox acceptance changes or Git commits were authorized.

View File

@ -0,0 +1,36 @@
---
created_at: 2026-10-03T00:18:37.208688+00:00
generated_by:
harness: codex
provider: openai
model: gpt-6
git_ref: 9115d24
scope: data
substantive: true
raw_file: 20261003T001837Z_9115d24_project_stores.raw.md
---
## Prompt excerpts
“break it up into appropriate per repo federated store files”
“intro the sub-store updates each on /open-wkt branches”
“a concise entrypoint to view all sub-project repo's federated stores”
“supervisor based locking”
“an edit capability can only be acquired from the supervisor”
“a human editor can be cancelled from their write access”
## Generated output (diff reference)
> `git diff HEAD~1..HEAD -- .taken/current.org`
Create the repository's migration branch and preserve its imported
scope, with revision-aware local source configuration and a shared
local taken skill. Keep original checkout stores and indexes intact.
Existing live branch stores remain independently registered. The LNS
manifest and migration ledger describe the authoritative split paths.
## Explanation
The global launch now works: one Neovim window shows all 13 sources,
with normal highlighting and the new help menu. The full AI export
reports complete coverage with no diagnostics.

View File

@ -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.

View File

@ -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.

View File

@ -0,0 +1,53 @@
---
model: claude-fable-5[1m]
service: claude
session: 32d15f9a-b2d3-4c26-bdc9-190219141a25
timestamp: 2026-06-10T17:10:22Z
git_ref: datad_service
diff_cmd: git log -1 -p --follow -- ai/prompt-io/claude/20260610T171022Z_4485f2b9_prompt_io.md
scope: code
substantive: true
raw_file: 20260610T171022Z_4485f2b9_prompt_io.raw.md
---
## Prompt
Same session-initiating `brokerd`-split instruction (see
`20260610T170859Z_75cefe10_prompt_io.md`). Proximate
driver: the approved plan's per-stage test gates could
not run AT ALL — the branch base was broken vs.
`tractor` git `main` (`AttributeError: 'tuple' object
has no attribute 'unwrap'` at `pikerd` boot, stale
`arbiter_sockaddr`/`.type`/`.size` API refs). The agent
fixed forward autonomously to (re)establish the gate
baseline, continuing the `repair_tests` branch lineage
(verified already merged in ancestry via `git cherry`
during a user-requested branch-overlap survey).
## Response summary
Port the service layer + test suites to current
`tractor` APIs: addr-type normalization in
`open_pikerd()`, `query_actor()`/`wait_for_actor()`
kwarg renames, modern self-cancel absorption semantics
in the cancel-method test harness, exc/position attr
renames, a paper-EMS startup-budget bump and a syntax
fix in `.deribit.api`.
## Files changed
- `piker/service/_actor_runtime.py` — `wrap_address()`
normalize before `.unwrap()` in `open_pikerd()`
- `piker/service/_registry.py` — `check_for_service()`
-> `query_actor(regaddr=)` + 2-tuple yield +
`open_registry(addrs=)`
- `piker/brokers/deribit/api.py` — missing comma in
`tractor.trionics` import tuple
- `tests/test_services.py` — `registry_addr=` kwarg,
raddr unwraps, cancel-semantics harness rewrite,
`fail_after` 9 -> 19s
- `tests/test_ems.py` — `.boxed_type`, `pp.cumsize`
## Human edits
None — committed as generated.

View File

@ -0,0 +1,56 @@
---
model: claude-fable-5[1m]
service: claude
timestamp: 2026-06-10T17:10:22Z
git_ref: datad_service
diff_cmd: git log -1 -p --follow -- ai/prompt-io/claude/20260610T171022Z_4485f2b9_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/service/_actor_runtime.py`
Generated: normalize each registry addr via
`tractor.discovery._addr.wrap_address()` before
`.unwrap()`-ing for the `accept_addrs` bind check —
entries may be raw `tuple`s when passed in from (test)
client code. Import-path precedent taken from
`piker/cli/__init__.py:336`.
> `git log -1 -p --follow -- piker/service/_registry.py`
Generated: `check_for_service()` ported to
`tractor.query_actor(name, regaddr=...)` (kwarg was
`arbiter_sockaddr=`), unpacking the new
`(sockaddr, portal)` yield, and passing the
now-required `open_registry(addrs=Registry.addrs)`.
> `git log -1 -p --follow -- tests/test_services.py`
> `git log -1 -p --follow -- tests/test_ems.py`
> `git log -1 -p --follow -- piker/brokers/deribit/api.py`
Key diagnostic reasoning (verbatim from session):
- the "DID NOT RAISE ContextCancelled" failure: in this
test the client actor IS pikerd (in-proc), and
current `tractor` main absorbs a `ContextCancelled`
whose canceller is your own actor — self-requested
cancels now exit cleanly instead of raising; the
'sigint' variant propagates a bare collapsed
`KeyboardInterrupt` rather than a
`BaseExceptionGroup`.
- the hard-coded `trio.fail_after(9)` startup budget is
marginal — full stack boot (pikerd -> emsd ->
brokerd.kraken -> paperboi + live kraken symbology
fetch) occasionally exceeds 9s -> bumped to 19s.
- `RemoteActorError.type` -> `.boxed_type`;
`Position.size` -> `.cumsize` (the paper engine
populates `BrokerdPosition.size` from `pp.cumsize`).
- overlap survey (user-requested): all of the
`repair_tests` branch commits are already in this
stack's ancestry; this commit finishes that branch's
port mission (its f4c4f1e2 fixed `conftest.py`'s
`arbiter_sockaddr` usage; this fixes the remaining
`test_services.py` + `check_for_service()` sites).

View File

@ -0,0 +1,54 @@
---
model: claude-fable-5[1m]
service: claude
session: 32d15f9a-b2d3-4c26-bdc9-190219141a25
timestamp: 2026-06-10T17:11:05Z
git_ref: datad_service
diff_cmd: git log -1 -p --follow -- ai/prompt-io/claude/20260610T171105Z_bc6e18d7_prompt_io.md
scope: code
substantive: true
raw_file: 20260610T171105Z_bc6e18d7_prompt_io.raw.md
---
## Prompt
Same session-initiating `brokerd`-split instruction (see
`20260610T170859Z_75cefe10_prompt_io.md`); this is the
approved plan's "stage 0" prep. Plan-shaping user
decisions captured via in-session Q&A:
- `datad.<broker>` topology: sibling of
`brokerd.<broker>` under `pikerd` (vs. child
subactor).
- migration: hard cutover, staged by layer (no
dual-mode runtime flag).
- post-split `brokerd` scope: trading-only +
EMS-lazy-spawned (charts/CLI never touch it).
## Response summary
Declare per-daemon-kind backend submod groups
(`_datad_mods`/`_brokerd_mods`) in the split-style
backends, keyed to the pre-existing
`piker.data.validate._eps` contract; add a
`validate.get_eps()` introspection helper; ws-import
hygiene in `.kraken.broker`. Zero behavior change
(`__enable_modules__` unions unchanged).
## Files changed
- `piker/data/validate.py` — add `get_eps()`
- `piker/brokers/kraken/__init__.py` — mod groups
- `piker/brokers/binance/__init__.py` — mod groups
(note: no `symbols.py`; search eps live in `.feed`)
- `piker/brokers/deribit/__init__.py` — datad-only
groups (no `broker.py` yet)
- `piker/brokers/ib/__init__.py` — add `'api'` to the
pre-existing `_datad_mods`
- `piker/brokers/kraken/broker.py` — import
`NoBsWs`/`open_autorecon_ws` from
`piker.data._web_bs` directly
## Human edits
None — committed as generated.

View File

@ -0,0 +1,46 @@
---
model: claude-fable-5[1m]
service: claude
timestamp: 2026-06-10T17:11:05Z
git_ref: datad_service
diff_cmd: git log -1 -p --follow -- ai/prompt-io/claude/20260610T171105Z_bc6e18d7_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/data/validate.py`
Generated: `get_eps(mod, kind) -> dict[str, Callable]`
returning the daemon-kind's ep funcs defined by a
backend mod, keyed by ep name, missing eps excluded;
sourced from the existing `_eps` grouping table
(`'middleware' | 'datad' | 'brokerd'`).
> `git log -1 -p --follow -- piker/brokers/kraken/__init__.py`
> `git log -1 -p --follow -- piker/brokers/binance/__init__.py`
> `git log -1 -p --follow -- piker/brokers/deribit/__init__.py`
> `git log -1 -p --follow -- piker/brokers/ib/__init__.py`
> `git log -1 -p --follow -- piker/brokers/kraken/broker.py`
Key exploration findings driving the design (from the
planning phase, 3 parallel explore agents + 1 architect
agent):
- the codebase already anticipated this split:
`validate._eps` groups eps into 'datad' vs 'brokerd'
kinds and `ib/__init__.py` already declared
`_brokerd_mods`/`_datad_mods`; `brokers/_daemon.py`
carried the literal TODO "rename the daemon to datad
prolly once we split up broker vs. data tasks into
separate actors?".
- the feared kraken feed<->broker "shared ws state"
hazard is mild: `NoBsWs`/`open_autorecon_ws`
originate in `piker.data._web_bs` (re-exported via
`.kraken.feed`); only `stream_messages` is feed-local
and it's a pure parser — in-process imports only,
each actor opens its own ws conn.
- `enable_modules` gates RPC entry, NOT python imports,
so backend trading mods may keep importing feed-side
helpers in-process post-split.

View File

@ -0,0 +1,47 @@
---
model: claude-fable-5[1m]
service: claude
session: 32d15f9a-b2d3-4c26-bdc9-190219141a25
timestamp: 2026-06-10T17:11:42Z
git_ref: datad_service
diff_cmd: git log -1 -p --follow -- ai/prompt-io/claude/20260610T171142Z_119d2c04_prompt_io.md
scope: code
substantive: true
raw_file: 20260610T171142Z_119d2c04_prompt_io.raw.md
---
## Prompt
Same session-initiating `brokerd`-split instruction (see
`20260610T170859Z_75cefe10_prompt_io.md`); this is the
approved plan's "stage 1": introduce the `datad` daemon
machinery additively (nothing routes through it yet).
User-decided constraint applied: `datad.<broker>` is a
SIBLING of `brokerd.<broker>` under `pikerd`, spawned
via the existing `Services` + `maybe_spawn_daemon()`
machinery.
## Response summary
New `piker/data/_daemon.py` hosting the
`datad.<broker>` feed-only daemon-actor: lifetime
fixture owning the actor-global `_FeedsBus`, init/spawn
fns mirroring `.brokers._daemon` conventions and the
`samplerd` sub-daemon precedent, plus root-mod
registration, `piker.service` re-exports and a spawn
test.
## Files changed
- `piker/data/_daemon.py` — NEW:
`_setup_persistent_datad()`, `datad_init()`,
`spawn_datad()`, `maybe_spawn_datad()`,
`_datad_service_mods`
- `piker/service/_actor_runtime.py` — add
`piker.data._daemon` to `_root_modules`
- `piker/service/__init__.py` — re-export spawn eps
- `tests/test_services.py` — add `test_datad_spawn`
## Human edits
None — committed as generated.

View File

@ -0,0 +1,60 @@
---
model: claude-fable-5[1m]
service: claude
timestamp: 2026-06-10T17:11:42Z
git_ref: datad_service
diff_cmd: git log -1 -p --follow -- ai/prompt-io/claude/20260610T171142Z_119d2c04_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/data/_daemon.py`
Generated symbols + key design decisions:
- `_datad_service_mods: list[str]` — datad-always
enabled mods, the data-side successor to the old
`piker.brokers._daemon._data_mods` set; kept minimal
per the caps-sec model.
- `_setup_persistent_datad()` — `@tractor.context`
lifetime fixture: console-log setup then allocates
the actor-global feed bus via
`feed.get_feed_bus(brokername, service_nursery)`
exactly as the old brokerd fixture did, pinned open
with `ctx.started()` + `sleep_forever()`.
- `datad_init()` — actor name `f'datad.{brokername}'`;
copies backend `_spawn_kwargs` (CRITICAL for `ib`'s
`infect_asyncio=True`); builds `enable_modules` from
`getattr(brokermod, '_datad_mods',
getattr(brokermod, '__enable_modules__', []))` as
the flat-backend fallback.
- `spawn_datad()` — `Services.actor_n.start_actor()` +
`Services.start_service_task()` exactly mirroring
`spawn_brokerd()`; dedup-composes enable mods via
`list(dict.fromkeys(...))`.
- `maybe_spawn_datad()` — wraps `maybe_spawn_daemon(
service_name=f'datad.{brokername}', ...)`.
> `git log -1 -p --follow -- piker/service/_actor_runtime.py`
> `git log -1 -p --follow -- piker/service/__init__.py`
> `git log -1 -p --follow -- tests/test_services.py`
Design rationale (verbatim from session):
- `_root_modules` must gain `piker.data._daemon` so
`pikerd_portal.run(spawn_datad, ...)` resolves in
the root.
- the `Services`-based impl style deliberately mirrors
`spawn_brokerd()` so the eventual `tractor.hilevel`
`ServiceMngr` port (see the `service_mng_to_tractor`
branch's d8c21d44 prep, surfaced by the
user-requested branch-overlap survey) lands
symmetrically on both spawn fns.
- mod placement (`piker/data/_daemon.py` vs.
generalizing `piker.brokers._daemon`) follows the
per-subsystem daemon-mod convention
(`.clearing._ems`, `.data._sampling`) and resolves
the existing TODO at `brokers/_daemon.py:49` ("move
this def to the `.data` subpkg").

View File

@ -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.<broker>` 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.

View File

@ -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.<broker>` 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.

View File

@ -0,0 +1,48 @@
---
model: claude-fable-5[1m]
service: claude
session: 32d15f9a-b2d3-4c26-bdc9-190219141a25
timestamp: 2026-06-10T17:12:59Z
git_ref: datad_service
diff_cmd: git log -1 -p --follow -- ai/prompt-io/claude/20260610T171259Z_59d5d9a6_prompt_io.md
scope: code
substantive: true
raw_file: 20260610T171259Z_59d5d9a6_prompt_io.raw.md
---
## Prompt
Same session-initiating `brokerd`-split instruction (see
`20260610T170859Z_75cefe10_prompt_io.md`); this is the
approved plan's "stage 3": the feed-layer hard cutover
(user-decided: no dual-mode runtime flag, each stage
lands fully working).
## Response summary
The topology flip: all data-feed consumers route to
`datad.<broker>`; the `brokerd` lifetime fixture slims
to trading-only; UI/CLI data-flavoured spawn sites
repointed; test expectations inverted incl. an explicit
negative assert that paper sessions spawn NO
`brokerd.<broker>`.
## Files changed
- `piker/data/feed.py` — `open_feed()` ->
`maybe_spawn_datad()`; `open_feed_bus()` actor-name
assert -> `'datad'`; `install_brokerd_search` ->
`install_datad_search`; comment sweep
- `piker/brokers/_daemon.py` —
`_setup_persistent_brokerd()` slimmed (feed-bus
alloc moved to the datad fixture)
- `piker/ui/_app.py`, `piker/ui/cli.py`,
`piker/brokers/core.py`, `piker/brokers/cli.py`,
`piker/ui/kivy/option_chain.py` — spawn-site
repoints
- `tests/test_services.py` — datad assertions + the
no-`brokerd.kraken` negative check
## Human edits
None — committed as generated.

View File

@ -0,0 +1,54 @@
---
model: claude-fable-5[1m]
service: claude
timestamp: 2026-06-10T17:12:59Z
git_ref: datad_service
diff_cmd: git log -1 -p --follow -- ai/prompt-io/claude/20260610T171259Z_59d5d9a6_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/data/feed.py`
Generated: `open_feed()` spawn cutover with the
`maybe_spawn_datad` import done "relative-direct" from
`._daemon` — NOT via `piker.service` — to dodge a
partial-init cycle: when `piker.data.feed` loads as
part of `piker.service.__init__` executing its own
`piker.data._daemon` import, the service pkg is
mid-init and its `maybe_spawn_datad` binding does not
exist yet. The `open_feed_bus()` local-state sanity
assert flips `'brokerd' in servicename` ->
`'datad' in servicename` (the only actor-name assert
in the tree, verified by grep).
> `git log -1 -p --follow -- piker/brokers/_daemon.py`
Generated: `_setup_persistent_brokerd()` slimmed to
console-log setup + pinned-open ctx; drops the bus
alloc, the `assert not feed._bus`, the service nursery
(backend `open_trade_dialog()` ctxs own their task
trees) and the `eg.ExceptionGroup` handler. The
`piker ledger` ad-hoc actor enters this same slimmed
fixture — exactly what it needs.
> `git log -1 -p --follow -- piker/ui/_app.py`
> `git log -1 -p --follow -- piker/ui/cli.py`
> `git log -1 -p --follow -- piker/brokers/core.py`
> `git log -1 -p --follow -- piker/brokers/cli.py`
> `git log -1 -p --follow -- piker/ui/kivy/option_chain.py`
> `git log -1 -p --follow -- tests/test_services.py`
Verification (from session): per-suite gates green at
this commit (services 5-passed incl. the new negative
assert, feeds 3-passed); a headless live smoke
(`maybe_open_pikerd` + `open_feed(['xbtusdt.kraken'])`
on an alt registry port) confirmed quotes flowing via
`datad.kraken` + `samplerd` with `check_for_service(
'brokerd.kraken') is None`. Known pre-existing flake
documented: `test_multi_fill_positions`' second
in-proc runtime boot wedges ~50% (zombie subactor w/
unread parent-IPC bytes); reproduced with the split
fully reverted so NOT a regression of this work.

View File

@ -0,0 +1,43 @@
---
model: claude-fable-5[1m]
service: claude
session: 32d15f9a-b2d3-4c26-bdc9-190219141a25
timestamp: 2026-06-10T17:13:44Z
git_ref: datad_service
diff_cmd: git log -1 -p --follow -- ai/prompt-io/claude/20260610T171344Z_eee19de0_prompt_io.md
scope: code
substantive: true
raw_file: 20260610T171344Z_eee19de0_prompt_io.raw.md
---
## Prompt
Same session-initiating `brokerd`-split instruction (see
`20260610T170859Z_75cefe10_prompt_io.md`); this is the
approved plan's final "stage 4": caps-sec slimming of
the (live, credentialed) trading actor + the `ib`
dual-daemon `client_id` collision mitigation flagged in
the plan's risk register.
## Response summary
`brokerd` loses ALL `piker.data.*` (feed) RPC mods;
spawn fails fast for datad-only backends with a "use
paper-mode" error; `ib`'s default api-gw `client_id`
gets a per-daemon-kind offset so `datad.ib` +
`brokerd.ib` don't collide on connect.
## Files changed
- `piker/brokers/_daemon.py` — `_data_mods` -> minimal
`_brokerd_service_mods`; `broker_init()` reads
`_brokerd_mods` (fallback `__enable_modules__`);
`spawn_brokerd()` fail-fast via `validate.get_eps()`
- `piker/brokers/ib/api.py` — role-based `client_id`
offset in `load_aio_clients()`
- `piker/cli/__init__.py` — resolved "expose datad"
TODO
## Human edits
None — committed as generated.

View File

@ -0,0 +1,41 @@
---
model: claude-fable-5[1m]
service: claude
timestamp: 2026-06-10T17:13:44Z
git_ref: datad_service
diff_cmd: git log -1 -p --follow -- ai/prompt-io/claude/20260610T171344Z_eee19de0_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/brokers/_daemon.py`
Generated: the fail-fast originally landed in
`broker_init()` but was relocated to `spawn_brokerd()`
mid-implementation after realizing `piker ledger`
calls `broker_init()` directly even for paper accounts
on datad-only backends (would have crashed the cli);
the service-spawn path is the correct enforcement
seam. Error text:
Backend 'kucoin' offers NO `brokerd` (live
order-control) eps!? It is likely a datad-only
provider, use paper-mode for clearing instead.
(verified live via a `trio.run()` unit check.)
> `git log -1 -p --follow -- piker/brokers/ib/api.py`
Generated: in `load_aio_clients()`, when `client_id`
is the 6116 default: `datad`-named actors offset +16
(disjoint from `brokerd`'s linear `client_id + i`
retry range), other non-`brokerd` (ad-hoc test/cli)
actors +32. Rationale from the plan's risk register:
post-split BOTH per-broker daemons connect to the same
TWS/gw endpoint; a shared default id collides and
burns up to `connect_timeout * retries` (90s) in
retry cycles.
> `git log -1 -p --follow -- piker/cli/__init__.py`

View File

@ -0,0 +1,66 @@
---
model: claude-fable-5[1m]
service: claude
session: 32d15f9a-b2d3-4c26-bdc9-190219141a25
timestamp: 2026-06-10T17:33:09Z
git_ref: datad_service
diff_cmd: git log -1 -p --follow -- ai/prompt-io/claude/20260610T173309Z_f15f8178_prompt_io.md
scope: docs
substantive: true
raw_file: 20260610T173309Z_f15f8178_prompt_io.raw.md
---
## Prompt
The session-initiating instruction, verbatim (this doc
IS its "mega detailed plan" deliverable):
> 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 atttempt to factor our current
> brokerd service daemon into 2 daemons:
>
> - a brokerd which only hosts live/paper trading
> endoint tasks as defined namely within all
> piker/brokers/<backend>/broker.py mods
> - a new `datad` subdaemon which in a separate
> subactor serves all the data feed service tasks
> namely delivered by endpoints in
> piker/brokers/<backend>/feed.py (or similar for
> less mature backends) and as more rigorously
> defined by the validation machinery in
> piker.data.validate.
>
> give me a mega detailed plan on how to approach
> this, and a staged approach for the implementation.
Plan-shaping user decisions (in-session Q&A): sibling
topology under `pikerd`; hard cutover staged by layer;
trading-only EMS-lazy-spawned `brokerd`. The human
staged the doc copy into `ai/claude-code/plans/` and
requested this commit after the 7 implementation
commits landed.
## Response summary
The staged design doc for the `brokerd` -> (`datad` +
`brokerd`) split: context, target supervision topology,
load-bearing verified facts, per-stage file-level
changes with gates, risk register and an end-to-end
verification matrix. Produced via 3 parallel explore
agents + 1 architect agent + human Q&A before any code
was written; all 7 implementation commits in this
branch reference its stages in their provenance
entries.
## Files changed
- `ai/claude-code/plans/datad_service.md` — the design
plan doc (human-staged copy of the AI-authored plan)
## Human edits
None to the doc content — committed as generated. NB:
the implementation deviated from the plan-as-written in
4 places, see the raw file's deviation log.

View File

@ -0,0 +1,51 @@
---
model: claude-fable-5[1m]
service: claude
timestamp: 2026-06-10T17:33:09Z
git_ref: datad_service
diff_cmd: git log -1 -p --follow -- ai/prompt-io/claude/20260610T173309Z_f15f8178_prompt_io.md
---
NOTE: diff-ref mode entry; the AI output here is the
committed doc itself (a design plan, not code) so no
verbatim copy is duplicated:
> `git log -1 -p --follow -- ai/claude-code/plans/datad_service.md`
## Implementation deviation log
Where the landed 7-commit series differs from the plan
as written (recorded for reviewer transparency; the
plan doc is committed unmodified):
1. Commit ordering: the `pytest` config-dir isolation
fix lands BEFORE the `tractor`-API drift port. The
plan's stage gates assumed a runnable+isolated test
baseline; per-commit gating exposed that without
isolation the paper-EMS test reads the user's real
(polluted) `account.kraken.paper.toml` and reddens.
2. The plan's "stage 0: full pytest green" gate
required first repairing pre-existing branch
breakage vs `tractor` git `main` (boot
`AttributeError`, stale discovery/exc/position
APIs) — that repair became its own commit
("Port service+tests to latest `tractor` APIs")
rather than plan-stage work.
3. Stage-4 fail-fast placement: the plan said
`broker_init()` raises on brokerd-ep-less backends;
implementation moved the raise to `spawn_brokerd()`
since `piker ledger` calls `broker_init()` directly
even for paper accounts on datad-only backends.
4. `brokers/_daemon.py` change grouping: the
import-cleanup hunks (`exceptiongroup`, `_FeedsBus`
type-only import) landed with the caps-sec slim
commit instead of the fixture-slim commit, keeping
each intermediate tree import-clean without
sub-hunk surgery.
Also of record: the plan's "Verification" matrix was
executed as written (per-suite gates each stage, the
headless datad-feed smoke, the kucoin fail-fast unit
check); the known pre-existing ~50% second-runtime-boot
test wedge was characterized and excluded as a
regression via revert-testing.

View File

@ -0,0 +1,60 @@
---
model: claude-fable-5[1m]
service: claude
session: 32d15f9a-b2d3-4c26-bdc9-190219141a25
timestamp: 2026-06-10T21:35:49Z
git_ref: datad_service
diff_cmd: git log -1 -p --follow -- ai/prompt-io/claude/20260610T213549Z_f084e899_prompt_io.md
scope: code
substantive: true
raw_file: 20260610T213549Z_f084e899_prompt_io.raw.md
---
## Prompt
User bug report from live `ib` testing of the
(datad|brokerd)-split branch:
> ok doing some testing and noticing live orders do not
> work otb, pikerd show this on live submission,
> [pdb traceback: `RuntimeError("Can not order
> {symbol}, no live feed?")` at `ib/api.py:1152`
> `submit_limit()` with `self._contracts == {}` in the
> `brokerd.ib` actor + a `TrioTaskExited` teardown
> cascade]
Follow-up user direction fixing the design intent:
> i think the main thing here is that [brokerd] should
> be able to always submit orders without a live feed
> being up and registered in the same subactor right?
## Response summary
Root cause: pre-split the feed code populated the
actor-local `Client._contracts` cache (via
`get_mkt_info()` writing `mkt.bs_fqme` keys) in the
SAME process as order submission; post-split the
trading actor's client is never warmed. Fix: lazily
qualify + cache the contract per order request in
`handle_order_requests()` by running the same
`get_mkt_info(fqme, proxy=...)` ep the feed side uses,
plus per-order error relay (`BrokerdError`) so one bad
submission can't crash the whole trades dialog (the
`TrioTaskExited` storm was teardown cascade from the
original raise).
## Files changed
- `piker/brokers/ib/broker.py` — thread `proxies` into
`handle_order_requests()`; lazy contract qualify on
cache-miss; guard `submit_limit()` w/ `BrokerdError`
relay; uncomment the (anticipatory) `get_mkt_info`
import
- `piker/brokers/ib/api.py` — fix the non-f-string
raise msg + document the new qualification contract
## Human edits
None — committed as generated. Live `ib` order retest
performed by the human post-commit.

View File

@ -0,0 +1,57 @@
---
model: claude-fable-5[1m]
service: claude
timestamp: 2026-06-10T21:35:49Z
git_ref: datad_service
diff_cmd: git log -1 -p --follow -- ai/prompt-io/claude/20260610T213549Z_f084e899_prompt_io.md
---
NOTE: diff-ref mode entry (code committed in the same
commit as this log); recorded from the live debug
session per the `/prompt-io` skill rules.
> `git log -1 -p --follow -- piker/brokers/ib/broker.py`
> `git log -1 -p --follow -- piker/brokers/ib/api.py`
Key diagnostic chain (from session):
- pdb showed `Client._contracts == {}` inside
`brokerd.ib`'s `submit_limit()`; the cache has
exactly TWO write sites: `Client.find_contracts()`
(api.py, keys `f'{sym}.{exch}.ib'`) and
`symbols.get_mkt_info()` (key `mkt.bs_fqme`, eg.
'nvda.nasdaq' — NO `.ib` suffix).
- `BrokerdOrder.symbol` arrives as the bs_fqme form
('nvda.nasdaq') so ONLY the `get_mkt_info()` write
site produces the key `submit_limit()` reads —
ie. pre-split it was the feed's in-proc
`get_mkt_info(sym, proxy=proxy)` call keeping orders
working, NOT `find_contracts()`.
- the existing TODO at `symbols.py:642-644` literally
predicted this: "this is going to be problematic
if/when we split out the datad vs. brokerd actors
since the mktmap lookup table will now be
inaccessible.."
- instance identity verified: `proxy._aio_ns` IS the
same `Client` obj as `_accounts2clients[account]`
(both sourced from the `load_aio_clients()` cache via
`open_client_proxies()`), so a brokerd-side
`get_mkt_info(fqme, proxy=proxies[account])` warms
exactly the dict `submit_limit()` reads. It also
populates `client._cons2mkts` which the
position-audit path (`broker.py` backup-table code)
needs in this actor anyway.
- the `TrioTaskExited` storm in the user's log
(`recv_trade_updates`, `open_aio_client_method_relay`
aio tasks) is teardown cascade: the raise crashed
`handle_order_requests` -> nursery teardown ripped
the trio sides of still-running aio relay tasks.
Hence the added per-order try/except ->
`BrokerdError` relay hardening so a single bad
submission degrades to an EMS error msg instead of
killing the backend's entire order-ctl dialog.
Verification: `tests/test_services.py` (5 passed) +
`tests/test_ems.py` (6 passed) regression-green; live
`ib` submission retest delegated to the human (needs a
running TWS/gw).

View File

@ -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.

View File

@ -0,0 +1,58 @@
---
created_at: 2026-10-05T23:00:13.353081+00:00
generated_by:
harness: codex
provider: OpenAI
git_ref: 9555e20c
scope: data
substantive: true
raw_file: 20261005T230013Z_9555e20c_trend_followups_d48a77_prompt_io.raw.md
---
## Prompt
The human asked to capture all ongoing Trend Calculus/Jev follow-ups
in Taken, supplied the federated_project_stores worktree, and then
requested a complete commit plan for that branch. They confirmed
that workflow configuration already exists on moar_ai_skillz and
asked why the earlier trend package duplicated it.
## Response summary
Resolved the registered Piker corpus and unique structural parent.
Used Taken's guarded whole-document save to append six identified
TODO tasks and 24 unchecked items. Preserved every prior byte,
existing state, property and order. Linked the existing FSP review
instead of duplicating it and kept annotation IPC follow-ups with
the existing task. Recorded known harness/worktree/branch provenance.
Corrected only the new prototype-review task's prose to require the
existing f148fbcc/moar_ai_skillz workflow dependency and regenerate
six Trend Calculus behavior boundaries. No task marker was changed.
Added a whitespace attribute limited to .taken Org documents so the
required final empty subtree separator passes standard Git checks.
No other workflow configuration is included in this commit package.
## Verification
Taken lint previously passed on the saved corpus and standard Git
whitespace checks pass with the path-scoped Org attribute. Saved
bytes were checked against the guarded candidate; all original
corpus content and state markers are intact. The commit package
selects a read-only lint against its exact boundary store, with
application tests excluded for this data-only change. That check
remains pending until package execution; no earlier whole-worktree
result is claimed as an exact-boundary reusable pass.
## Human edits
The human authorized loading the registered store and requested
planning, not execution. Their workflow-history correction directly
changed the new review task. Existing human tasks remain unaccepted;
no commits, pushes, rebases, mainlining or existing-state changes
were performed. The main checkout's staged content was preserved.
## Files changed
- `.taken/current.org`
- `.gitattributes`

View File

@ -0,0 +1,52 @@
---
created_at: 2026-10-05T23:00:13.353081+00:00
generated_by:
harness: codex
provider: OpenAI
git_ref: 9555e20c
scope: data
substantive: true
---
## Human instruction (verbatim)
after this pass i want us to start loading our taken store with all the ongoing / todos/ follow ups anyway - we can hash it all out there.
it's on another unmerged wkt we have to mainline, not sure what the
best stopgap for that is rn but the existing taken is here,
wkt/federated_project_stores
can we do a $commit-plan for that wkt branch with this stuff then as
well? i'm going to try and get it landed to main asap so we can start
always updating it directly from our working branch/wkt.
uhh isn't this already in f148fbcc and the moar_ai_skillz branch??
## Captured assistant output (verbatim)
The style pass and seven-commit package are finished. I added
six follow-up tasks with 24 unchecked items to the registered
Taken corpus and verified that all prior content is unchanged.
Taken lint passes.
You’re right: the three `.ai` files are byte-for-byte identical
to `f148fbcc` on `moar_ai_skillz`. That commit isn’t in this
checkout or local `main` yet, but I should have treated it as
an existing branch dependency, not proposed a duplicate commit.
I’m withdrawing that workflow step from the Trend Calculus plan.
Those files are now staged here; I’ll leave your index untouched.
## Data/configuration pointers
> `git diff HEAD~1..HEAD -- .taken/current.org`
> `git diff HEAD~1..HEAD -- .gitattributes`
These pointers name the completed one-boundary commit after human
review/execution. The prepared package captures the pending delta.
Six identified tasks retain all existing states and record prototype
review, live Jev qualification, service wiring, Pine parity, chart
qualification and native subsecond source work. A narrow Org
attribute permits Taken's required blank separator at EOF while
retaining other whitespace diagnostics. No Git commit ran.

View File

@ -0,0 +1,64 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
session: ses_0799212ebffe42arY96czXn89F
timestamp: 2026-07-21T20:52:28Z
git_ref: 58ffe487
scope: config
substantive: true
raw_file: 20260721T205228Z_58ffe487_prompt_io.raw.md
---
## Prompt
The user asked to scan Claude's repo-local skills and
documentation, summarize their specializations, and then
rework repo-specific skills for use across coding
harnesses, particularly OpenCode. The user intends to
choose one of Claude's unfinished tasks afterward.
## Response summary
Standardized the tracked piker skills on portable Agent
Skills frontmatter, made `commit-msg` harness-aware,
documented OpenCode discovery and command delegation, and
fixed an unsafe `np.searchsorted()` example found during
the audit. Preserved the existing `.claude` artifact path
as a shared legacy location to avoid breaking established
commit workflows.
## Files changed
- `.claude/skills/commit-msg/SKILL.md` - portable,
harness-aware commit-message workflow
- `.claude/skills/commit-msg/style-guide-reference.md` -
generic coding-harness attribution
- `.claude/skills/piker-conc-expert/SKILL.md` - portable
frontmatter
- `.claude/skills/piker-profiling/SKILL.md` - portable
frontmatter
- `.claude/skills/piker-slang/SKILL.md` - portable
frontmatter
- `.claude/skills/pyqtgraph-optimization/SKILL.md` -
portable frontmatter
- `.claude/skills/timeseries-optimization/SKILL.md` -
portable frontmatter and safe search bounds handling
- `ai/README.md` - shared-skill integration model
- `ai/claude-code/README.md` - cross-harness Claude Code
documentation
- `ai/opencode/README.md` - OpenCode usage and discovery
documentation
- `.opencode/commands/commit-msg.md` - thin OpenCode
command delegating to the shared skill
- `ai/prompt-io/opencode/README.md` - OpenCode provenance
policy
- `ai/prompt-io/opencode/20260721T205228Z_58ffe487_prompt_io.raw.md`
- unedited response record
- `ai/prompt-io/opencode/20260721T205228Z_58ffe487_prompt_io.md`
- provenance metadata and summary
## Human edits
None - generated changes have not yet been reviewed or
edited by the human.

View File

@ -0,0 +1,33 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
timestamp: 2026-07-21T20:52:28Z
git_ref: 58ffe487
diff_cmd: git diff HEAD~1..HEAD
---
The repo-local skill inventory contains seven domains:
commit-message generation, actor-runtime concurrency,
distributed profiling, project communication style,
Python source style, PyQtGraph rendering, and NumPy/Polars
timeseries optimization.
I converted the six tracked repo-specific skills to the
portable Agent Skills frontmatter subset so Claude Code
and OpenCode can load the same source files. The
commit-message workflow now gathers live git context at
execution time, uses runtime harness/model attribution,
preserves human-owned task state, and keeps the existing
message artifact paths for compatibility.
I added OpenCode integration documentation and updated
the shared and Claude Code docs to describe a single
cross-harness skill source with thin harness commands.
During validation I also corrected an out-of-bounds
`np.searchsorted()` example in the timeseries skill.
The unfinished artifacts found during the scan are a
shared-memory migration plan and a gap-annotation
performance handoff. The earlier datad split plan appears
implemented and is now captured by the concurrency skill.

View File

@ -0,0 +1,41 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
session: ses_0799212ebffe42arY96czXn89F
timestamp: 2026-07-21T21:50:55Z
git_ref: c6ec3d41
scope: docs
substantive: true
raw_file: 20260721T215055Z_c6ec3d41_prompt_io.raw.md
---
## Prompt
The user asked to boot a plan to continue work on the
time-series gap checker and bug fixing of the history
backfiller. The user authorized prompt-I/O capture for the
plan documentation after the repository audit.
## Response summary
Produced a bounded repair plan based on current source,
historical issue caches, existing profiling notes, and a
test-coverage audit. The plan prioritizes reproducible
correctness failures and storage durability before venue
classification, annotation performance, or manual repair
UX.
## Files changed
- `plans/opencode/gap-checker-backfiller-repair.md` -
phased implementation and verification plan
- `ai/prompt-io/opencode/20260721T215055Z_c6ec3d41_prompt_io.raw.md`
- unedited response record
- `ai/prompt-io/opencode/20260721T215055Z_c6ec3d41_prompt_io.md`
- provenance metadata and response summary
## Human edits
None - generated plan has not yet been edited by the
human.

View File

@ -0,0 +1,26 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
timestamp: 2026-07-21T21:50:55Z
git_ref: c6ec3d41
diff_cmd: git diff HEAD~1..HEAD
---
> `git diff -- plans/opencode/gap-checker-backfiller-repair.md`
Generated a phased implementation plan for continuing
piker's gap checker and history backfiller repairs. The
plan separates data correctness from annotation
performance and orders work by dependency: deterministic
tests, canonical frame/gap normalization, structured
backfill completion, non-destructive atomic NativeDB
writes, exact null repair, provider/venue classification,
annotation vectorization, and crash qualification.
The plan is grounded in current code paths, local issue
caches `#62`, `#71`, and `#75`, and the existing 1,285-gap
profiling handoff. It records confirmed present defects,
design invariants, per-phase acceptance gates, test
commands, commit boundaries, and explicitly deferred UX
and provider work.

View File

@ -0,0 +1,46 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
session: ses_0799212ebffe42arY96czXn89F
timestamp: 2026-07-22T00:13:57Z
git_ref: 16f3cd6f
scope: code
substantive: true
raw_file: 20260722T001357Z_16f3cd6f_prompt_io.raw.md
---
## Prompt
Continue the gap-checker and history-backfiller repair plan. Start
with deterministic analysis characterization, then fix canonical
sorting, dedupe-derived columns, positive gap detection, and null
grouping.
During review, the user challenged the replacement null-grouping
algorithm because its initial tests did not assert exact endpoints.
The user explicitly required retaining detailed, human-oriented
algorithm documentation such as the existing ASCII segment map.
## Response summary
Added deterministic analysis regressions and corrected normalization
so sorting and duplicate resolution precede derived-column
calculation. Unified null grouping for NumPy and Polars inputs,
documented its exact inclusive-boundary contract with an adapted
visual map, and verified all small null layouts against an independent
state-machine oracle. Retained sorted smart-dedupe output.
## Files changed
- `piker/tsp/_anal.py` - canonical gap and documented null grouping
- `piker/tsp/_dedupe_smart.py` - sorted duplicate resolution
- `tests/test_tsp_analysis.py` - exact and exhaustive regressions
- `ai/prompt-io/opencode/20260722T001357Z_16f3cd6f_prompt_io.raw.md`
- unedited response record
- `ai/prompt-io/opencode/20260722T001357Z_16f3cd6f_prompt_io.md`
- provenance metadata and response summary
## Human edits
None - generated changes have not yet been edited by the human.

View File

@ -0,0 +1,40 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
timestamp: 2026-07-22T00:13:57Z
git_ref: 16f3cd6f
diff_cmd: git diff HEAD~1..HEAD
---
> `git diff HEAD~1..HEAD -- piker/tsp/_anal.py`
Generated canonical gap and null-segment normalization. Derived
datetime and delta columns are recomputed after final sorting and
duplicate resolution, and negative deltas no longer become ordinary
gaps.
NumPy and Polars null grouping share one implementation while
retaining the human-oriented visual map of contiguous zero runs. The
documented contract defines absolute inclusive endpoints, margin
expansion, frame boundary clamping, contiguous-index validation, and
requested-column selection.
> `git diff HEAD~1..HEAD -- piker/tsp/_dedupe_smart.py`
Adjusted smart dedupe to honor sorted output and resolve duplicate
timestamps without retaining stale order.
> `git diff HEAD~1..HEAD -- tests/test_tsp_analysis.py`
Added deterministic synthetic OHLCV coverage for sorted dedupe, fresh
derived columns, and positive gap detection. Null grouping is checked
against readable endpoint examples and an independent state-machine
oracle over every layout through six rows, three margin widths, and
both NumPy and Polars inputs.
Verification generated with the patch:
`timeout -k 5 30 python -m pytest -q tests/test_tsp_analysis.py`
Result: 11 passed.

View File

@ -0,0 +1,36 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
session: ses_0799212ebffe42arY96czXn89F
timestamp: 2026-07-22T00:13:58Z
git_ref: 16f3cd6f
scope: code
substantive: true
raw_file: 20260722T001358Z_16f3cd6f_prompt_io.raw.md
---
## Prompt
Continue the structured backfill phase after the analysis regressions.
Characterize provider exhaustion and genuinely empty frames, then remove
completion paths that can hang after child-task exit.
## Response summary
Removed orphan-prone completion events from the structured nursery flow
and checked empty provider frames before indexing timestamp endpoints.
Added deterministic timeout-bounded regressions for both exit paths.
## Files changed
- `piker/tsp/_history.py` - structured completion and empty-frame fixes
- `tests/test_history_backfill.py` - deterministic exit regressions
- `ai/prompt-io/opencode/20260722T001358Z_16f3cd6f_prompt_io.raw.md`
- unedited response record
- `ai/prompt-io/opencode/20260722T001358Z_16f3cd6f_prompt_io.md`
- provenance metadata and response summary
## Human edits
None - generated changes have not yet been edited by the human.

View File

@ -0,0 +1,25 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
timestamp: 2026-07-22T00:13:58Z
git_ref: 16f3cd6f
diff_cmd: git diff HEAD~1..HEAD
---
> `git diff HEAD~1..HEAD -- piker/tsp/_history.py`
Removed redundant completion events whose consumers waited only after
their owning nurseries had joined. Added empty provider-frame handling
before timestamp endpoint indexing so provider exhaustion exits cleanly.
> `git diff HEAD~1..HEAD -- tests/test_history_backfill.py`
Added bounded, actor-free regressions for `DataUnavailable` and empty
provider-frame completion.
Verification generated with the patch:
`timeout -k 5 60 python -m pytest -q tests/test_history_backfill.py`
Result: 2 passed.

View File

@ -0,0 +1,39 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
session: ses_0799212ebffe42arY96czXn89F
timestamp: 2026-07-22T02:41:33Z
git_ref: 3a4f6737
scope: code
substantive: true
raw_file: 20260722T024133Z_3a4f6737_prompt_io.raw.md
---
## Prompt
The user challenged the analysis commit message's claim that only
positive sample-step gaps are detected and asked whether out-of-order
timestamps were simply ignored. After confirming the missing separate
reporting path, the user instructed the agent to implement the fix.
## Response summary
Separated missing-history gaps from timestamp-ordering errors without
changing the existing `dedupe()` return shape. Added a structured
detector and made normalization warn before sorting removes evidence of
duplicates or reversed timestamps.
## Files changed
- `piker/tsp/_anal.py` - ordering detector and dedupe reporting
- `piker/tsp/__init__.py` - public detector export
- `tests/test_tsp_analysis.py` - exact ordering-error regression
- `ai/prompt-io/opencode/20260722T024133Z_3a4f6737_prompt_io.raw.md`
- unedited response record
- `ai/prompt-io/opencode/20260722T024133Z_3a4f6737_prompt_io.md`
- provenance metadata and response summary
## Human edits
None - generated changes have not yet been edited by the human.

View File

@ -0,0 +1,30 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
timestamp: 2026-07-22T02:41:33Z
git_ref: 3a4f6737
diff_cmd: git diff HEAD~1..HEAD
---
> `git diff HEAD~1..HEAD -- piker/tsp/_anal.py`
Added `detect_time_ordering_errors()` to return non-positive timestamp
steps as structured rows. `dedupe()` now warns with those rows before
sorting and duplicate removal erase the original ordering evidence.
> `git diff HEAD~1..HEAD -- piker/tsp/__init__.py`
Exported the ordering detector through the public TSP package.
> `git diff HEAD~1..HEAD -- tests/test_tsp_analysis.py`
Changed the negative-delta regression to verify exact predecessor,
timestamp, and delta values, confirm exclusion from missing-history
gaps, and require explicit normalization reporting.
Verification generated with the patch:
- `tests/test_tsp_analysis.py`: 11 passed
- Ruff: passed
- `git diff --check`: passed

View File

@ -0,0 +1,43 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
session: ses_0799212ebffe42arY96czXn89F
timestamp: 2026-07-22T02:41:34Z
git_ref: 3a4f6737
scope: code
substantive: true
raw_file: 20260722T024134Z_3a4f6737_prompt_io.raw.md
---
## Prompt
The user asked why the in-progress `piker/storage` changes were omitted
from the earlier commit plan. After receiving an inventory of unfinished
Phase 3 requirements, the user instructed the agent to complete the work.
## Response summary
Finished non-destructive, atomic NativeDB updates and integrated them
with history startup and reverse backfill. Expanded tests through two
adversarial review rounds that caught and corrected cross-client lock
blocking, stale temp indexing, discontinuous persisted indexes, startup
snapshot races, missing latest-frame persistence, notification wedges,
and mutable/truncated SHM persistence.
## Files changed
- `piker/storage/__init__.py` - incremental storage contract
- `piker/storage/nativedb.py` - validated atomic merge persistence
- `piker/storage/marketstore/__init__.py` - non-duplicate update mode
- `piker/tsp/_history.py` - provider-delta and startup persistence
- `tests/test_storage_nativedb.py` - NativeDB durability regressions
- `tests/test_history_backfill.py` - persistence lifecycle regressions
- `ai/prompt-io/opencode/20260722T024134Z_3a4f6737_prompt_io.raw.md`
- unedited response record
- `ai/prompt-io/opencode/20260722T024134Z_3a4f6737_prompt_io.md`
- provenance metadata and response summary
## Human edits
None - generated changes have not yet been edited by the human.

View File

@ -0,0 +1,43 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
timestamp: 2026-07-22T02:41:34Z
git_ref: 3a4f6737
diff_cmd: git diff HEAD~1..HEAD
---
> `git diff HEAD~1..HEAD -- piker/storage/__init__.py piker/storage/nativedb.py piker/storage/marketstore/__init__.py`
Added an incremental storage update contract. NativeDB now validates
canonical numeric OHLCV data, serializes writers both actor-locally and
through cancellable filesystem locks, merges with incoming-wins seam
resolution, regenerates contiguous indexes, reopens and validates a
same-directory temporary parquet, fsyncs it, atomically replaces the
target, fsyncs the directory, and publishes cache only after replacement.
NativeDB indexing ignores lock and crash-left temporary files.
MarketStore uses its non-duplicate write mode for incremental updates.
> `git diff HEAD~1..HEAD -- piker/tsp/_history.py`
Separated full provider deltas from capacity-truncated SHM pushes.
Backfill persists before SHM mutation, publishes bounded notifications,
and delays latest-frame publication until the concurrent pre-update
storage snapshot completes.
> `git diff HEAD~1..HEAD -- tests/test_storage_nativedb.py tests/test_history_backfill.py`
Added deterministic coverage for merge preservation, conflict policy,
atomic failures, reopened-candidate validation, cache consistency,
schema and timestamp invariants, contiguous index regeneration,
separate series, stale temp indexing, cancellable file-lock contention,
full provider-delta persistence, latest-frame ordering, and bounded
notification teardown.
Verification generated with the patch:
- deterministic analysis/backfill/storage set: 31 passed
- Ruff: passed
- `git diff --check`: passed
- final adversarial review: no findings

View File

@ -0,0 +1,32 @@
---
model: openai/gpt-5.6-sol
service: opencode
session: ses_092a25886ffeT0nrIrdkMEcgJK
timestamp: 2026-07-24T22:01:56Z
git_ref: flake_update
scope: config
substantive: true
raw_file: 20260724T220156Z_43d7a3ed_prompt_io.raw.md
---
## Prompt
After committing Piker's provider-neutral deployment, the user asked to
extract Piker-specialized `/run-tests` content from the historical
`wkt/run_tests_skill` implementation into the new deployment.
## Response summary
Create a repository-owned `test-harness-reference.md` for the canonical Run
base. Revalidate historical commands and assumptions against current pytest,
uv/Nix environments, tests, fixtures, and Tractor runtime behavior, retaining
only current Piker-specific guidance.
## Files changed
- `.claude/skills/run-tests/test-harness-reference.md` - define Piker's safe
commands, opt-in boundaries, mappings, known outcomes, and runtime notes.
## Human edits
None at generation time; pending human review.

View File

@ -0,0 +1,32 @@
---
model: openai/gpt-5.6-sol
service: opencode
timestamp: 2026-07-24T22:01:56Z
git_ref: flake_update
diff_cmd: git diff HEAD~1..HEAD
---
The user asked to extract useful Piker-specific `/run-tests` behavior from
`wkt/run_tests_skill` after committing the provider-neutral deployment.
> `git diff HEAD~1..HEAD -- .claude/skills/run-tests/test-harness-reference.md`
Generate a repository-owned harness reference rather than restoring the old
standalone `SKILL.md`. Preserve verified Piker-specific environment, command,
scope, fixture, test-layout, change-mapping, known-outcome, and Tractor-runtime
details while leaving shared execution and cleanup policy in the canonical
`ai.skillz` base.
Repository inspection found that the historical skill needs correction:
- the comments-only `pytest.ini` shadows `pyproject.toml` pytest options, so
`-p no:xonsh` must be explicit;
- no marker-based offline suite exists, and several live/GUI/container tests
are unsafe defaults;
- skipped Docker and Questrade modules can fail during import before marks;
- actor timeout retry remains relevant only for the exact timed-out command;
- `py313` and frozen no-sync `uv run` are the current local environment paths.
Validate with checkout import resolution, safe collection, and deterministic
watchlist/accounting tests. Do not run live broker, GUI, container,
credentialed, or user-config-dependent cases during harness verification.

View File

@ -0,0 +1,40 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
session: unavailable
timestamp: 2026-07-27T20:47:11Z
git_ref: 84a6d47b
scope: code
substantive: true
raw_file: 20260727T204711Z_84a6d47b_prompt_io.raw.md
---
## Prompt
The user identified that `backfiller_deep_fixes` introduced persistent
`.parquet.lock` files, questioned whether Parquet or Arrow already handled
writer coordination, and clarified that piker's `tractor` structured
concurrency design already provides single-writer ownership through the
persistent `datad` feed task. The user then asked to open the backfiller
worktree and resolve the redundant locking.
## Response summary
Removed redundant actor-local and filesystem locking while preserving
atomic crash-safe Parquet replacement. Replaced the synthetic contention
test with regressions for the actual cross-branch failure: writes create
no lock sidecars, and legacy lock artifacts are ignored during indexing.
## Files changed
- `piker/storage/nativedb.py` - rely on `datad` writer ownership
- `tests/test_storage_nativedb.py` - lock-sidecar regressions
- `ai/prompt-io/opencode/20260727T204711Z_84a6d47b_prompt_io.raw.md`
- unedited response record
- `ai/prompt-io/opencode/20260727T204711Z_84a6d47b_prompt_io.md`
- provenance metadata and response summary
## Human edits
None - generated changes have not been edited by the human.

View File

@ -0,0 +1,30 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
timestamp: 2026-07-27T20:47:11Z
git_ref: 84a6d47b
diff_cmd: git diff HEAD~1..HEAD
---
> `git diff HEAD~1..HEAD -- piker/storage/nativedb.py`
Removed actor-local and filesystem writer locks from NativeDB. The
persistent `datad` feed task already owns writes for each series, while
its timeframe children write distinct files. NativeDB retains validated
temporary writes, file and directory synchronization, and atomic target
replacement for crash durability. The exact `.parquet` suffix filter is
retained so legacy lock sidecars are harmless.
> `git diff HEAD~1..HEAD -- tests/test_storage_nativedb.py`
Removed the synthetic cross-client lock-contention test. Added regression
coverage proving replacement and incremental writes create no lock
sidecars. Extended indexing coverage with the legacy
`.parquet.lock` artifact that caused upstream `flake_update` to crash.
Verification generated with the patch:
- NativeDB and history regression set: 20 passed
- Ruff: passed
- `git diff --check`: passed

View File

@ -0,0 +1,42 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
session: ses_0799212ebffe42arY96czXn89F
timestamp: 2026-07-27T21:24:54Z
git_ref: 689df816
scope: code
substantive: true
raw_file: 20260727T212454Z_689df816_prompt_io.raw.md
---
## Prompt
The user reran the known IB FQME on `backfiller_deep_fixes` and reported
that `publish_latest_frame()` still failed because NativeDB required an
`index` absent from IB's provider frame. The user called out the missed
tabular schema boundary and requested an actual end-to-end test suite
rather than storage fakes.
## Response summary
Repaired the IB-provider-to-NativeDB boundary by canonicalizing durable
fields and mapping reloads into provider-specific SHM buffers. Added a
local actor/SHM/Parquet integration regression covering first startup and
restart, plus focused conversion and timestamp-coercion tests. Two review
rounds found and resolved restart hydration and fractional-time hazards.
## Files changed
- `piker/storage/nativedb.py` - canonical storage and SHM field mapping
- `piker/tsp/_anal.py` - name-based Polars-to-NumPy conversion
- `tests/test_history_backfill.py` - actor/SHM/Parquet integration test
- `tests/test_storage_nativedb.py` - schema conversion regressions
- `ai/prompt-io/opencode/20260727T212454Z_689df816_prompt_io.raw.md`
- unedited response record
- `ai/prompt-io/opencode/20260727T212454Z_689df816_prompt_io.md`
- provenance metadata and response summary
## Human edits
None - generated changes have not been edited by the human.

View File

@ -0,0 +1,34 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
timestamp: 2026-07-27T21:24:54Z
git_ref: 689df816
diff_cmd: git diff HEAD~1..HEAD
---
> `git diff HEAD~1..HEAD -- piker/storage/nativedb.py piker/tsp/_anal.py`
Normalized index-less provider frames to NativeDB's canonical names,
order, and dtypes before merge and publication. Provider-only columns
are excluded from durable files, derived indexes are regenerated, and
fractional timestamps are rejected before integer conversion. Added a
canonical field map for hydrating provider-specific SHM buffers while
leaving provider-only fields at their defaults. Changed Polars-to-NumPy
conversion to select columns by field name rather than position.
> `git diff HEAD~1..HEAD -- tests/test_history_backfill.py tests/test_storage_nativedb.py`
Added deterministic coverage using IB's actual provider dtype and a real
local `tractor` root actor, IB-typed shared-memory buffers, NativeDB,
Parquet persistence, first-start publication, and restart hydration.
Added focused regressions for reordered/extra Polars columns and
fractional timestamp rejection.
Verification generated with the patch:
- NativeDB and history integration set: 23 passed
- Ruff: passed
- `git diff --check`: passed
- second adversarial review: no findings
- no live gateway or network tests run

View File

@ -0,0 +1,41 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
session: ses_0799212ebffe42arY96czXn89F
timestamp: 2026-07-27T21:59:06Z
git_ref: 689df816
scope: code
substantive: true
raw_file: 20260727T215906Z_689df816_prompt_io.raw.md
---
## Prompt
After the actor/SHM/Parquet integration test exposed a stale SHM triplet,
the user requested leak-cleaner machinery in the pytest harness using
Tractor's existing ownership and lifetime patterns.
## Response summary
Added creator-scoped SHM leak tracking to pytest without scanning
`/dev/shm` or treating attachments as owned. The fixture cooperates with
normal Tractor teardown, verifies POSIX object identity before fallback
cleanup, restores process-local token state, and fails after cleaning a
leak. Regressions cover the exact pre-actor-registration allocation window,
positional creators, and external attachment safety.
## Files changed
- `tests/conftest.py` - creator-scoped SHM leak tracking fixture
- `tests/test_shm_cleanup.py` - ownership and failure-window regressions
- `.claude/skills/run-tests/test-harness-reference.md`
- document current-process SHM cleanup behavior
- `ai/prompt-io/opencode/20260727T215906Z_689df816_prompt_io.raw.md`
- unedited response record
- `ai/prompt-io/opencode/20260727T215906Z_689df816_prompt_io.md`
- provenance metadata and response summary
## Human edits
None - generated changes have not been edited by the human.

View File

@ -0,0 +1,25 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
timestamp: 2026-07-27T21:59:06Z
git_ref: 689df816
diff_cmd: git diff HEAD~1..HEAD
---
> `git diff HEAD~1..HEAD -- tests/conftest.py tests/test_shm_cleanup.py .claude/skills/run-tests/test-harness-reference.md`
Added function-scoped SHM ownership tracking around Tractor's test-process
factory. The fixture records only successful creators, removes ownership
records during normal Tractor teardown, verifies POSIX object identity
before fallback unlink, restores the token-cache baseline, and fails after
cleaning any leak. Regressions reproduce allocation failure before actor
lifetime registration and prove external attachments remain untouched.
Verification generated with the patch:
- SHM cleanup regressions: 2 passed
- combined SHM, NativeDB, and history set: 25 passed
- Ruff: passed
- `git diff --check`: passed
- final adversarial review: no findings

View File

@ -0,0 +1,42 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
session: 24fb9765-a550-4570-8350-f0fc9b7e17db
timestamp: 2026-07-28T00:32:47Z
git_ref: ed85721c
scope: code
substantive: true
raw_file: 20260728T003247Z_ed85721c_prompt_io.raw.md
---
## Prompt
Build a safe xonsh workflow for qualifying NativeDB and reverse-backfill
correctness against a known gappy IB chart, beginning with
`mnq.cme.20260918`. Cover baseline, fresh, restart, append, and expansion
testing through implementation, adversarial review, and verification.
## Response summary
Implemented a private disposable xonsh qualification workflow. Evidence is
tied to a fixed case, report checksum, snapshot checksum, worktree import,
and phase metadata. Added explicit path and symlink safety, source
replacement handling, xonsh child environments, and lifecycle preservation
comparisons.
## Files changed
- `snippets/nativedb_backfill_audit.xsh` - disposable qualification helpers
- `docs/manual_backfill_qualification.rst` - operator lifecycle runbook
- `tests/test_backfill_audit_snippet.py` - xonsh and comparison regressions
- `.claude/skills/run-tests/test-harness-reference.md`
- qualification test-harness guidance
- `ai/prompt-io/opencode/20260728T003247Z_ed85721c_prompt_io.raw.md`
- unedited response record
- `ai/prompt-io/opencode/20260728T003247Z_ed85721c_prompt_io.md`
- provenance metadata and response summary
## Human edits
None - generated changes have not been edited by the human.

View File

@ -0,0 +1,32 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
timestamp: 2026-07-28T00:32:47Z
git_ref: ed85721c
diff_cmd: git diff HEAD~1..HEAD
---
> `git diff HEAD~1..HEAD -- snippets/nativedb_backfill_audit.xsh docs/manual_backfill_qualification.rst tests/test_backfill_audit_snippet.py .claude/skills/run-tests/test-harness-reference.md`
Added sourceable xonsh helpers that create a private disposable root, bind
child processes to the reviewed worktree, capture checksum-bound phase
evidence, safely seed and clear test history, and compare lifecycle
preservation.
The workflow rejects path traversal, pre-existing symlinks, output
collisions, mismatched case/report/snapshot evidence, and same-phase
comparison. The accompanying runbook covers known-bad replay, fresh
backfill, restart, append, gap triage, and expansion cases.
Verification generated with the patch:
- xonsh qualification helper regressions: passed
- xonsh sourceability: passed
- Ruff: passed
- `git diff --check`: passed
Adversarial review drove additional evidence binding, worktree executable,
environment propagation, source replacement, and descriptor-level safety
fixes. Residual concurrent mutation risk is stated explicitly in the private
disposable-root runbook.

View File

@ -0,0 +1,40 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
session: 24fb9765-a550-4570-8350-f0fc9b7e17db
timestamp: 2026-07-28T00:50:34Z
git_ref: ed85721c
scope: code
substantive: true
raw_file: 20260728T005034Z_ed85721c_prompt_io.raw.md
---
## Prompt
Add reusable read-only tooling to qualify raw NativeDB Parquet against a
known gappy IB chart, beginning with `mnq.cme.20260918`. Preserve malformed
evidence and distinguish structural validity from unclassified positive
gaps without opening or mutating the storage runtime.
## Response summary
Implemented raw Parquet audit reporting with human and JSON output, bounded
cadence details, exact integer arithmetic, strict status, collision-safe
snapshot capture, and source path protections. Regressions cover malformed
schema and values, timestamp precision, gap arithmetic, corrupt bytes,
copy failures, symlinks, traversal, and read-only CLI behavior.
## Files changed
- `piker/storage/_audit.py` - raw Parquet audit and snapshot implementation
- `piker/storage/cli.py` - `piker store audit` command
- `tests/test_storage_audit.py` - audit and CLI regressions
- `ai/prompt-io/opencode/20260728T005034Z_ed85721c_prompt_io.raw.md`
- unedited response record
- `ai/prompt-io/opencode/20260728T005034Z_ed85721c_prompt_io.md`
- provenance metadata and response summary
## Human edits
None - generated changes have not been edited by the human.

View File

@ -0,0 +1,27 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
timestamp: 2026-07-28T00:50:34Z
git_ref: ed85721c
diff_cmd: git diff HEAD~1..HEAD
---
> `git diff HEAD~1..HEAD -- piker/storage/_audit.py piker/storage/cli.py tests/test_storage_audit.py`
Added a raw, read-only NativeDB Parquet audit with human and JSON output,
bounded cadence evidence, exact integer arithmetic, pre-parse snapshot
capture, and strict qualification status.
The command rejects unsafe FQME paths, symlinked sources and outputs,
destination collisions, non-regular source nodes, malformed canonical
schema, non-finite values, invalid timestamps, noncanonical indexes, and
sub-period cadence. Copy-failure cleanup preserves source bytes while
completed corrupt snapshots remain available for diagnosis.
Verification generated with the patch:
- storage audit and CLI regressions: passed
- Ruff: passed
- `git diff --check`: passed
- final adversarial review: no findings

View File

@ -0,0 +1,39 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
session: 24fb9765-a550-4570-8350-f0fc9b7e17db
timestamp: 2026-07-28T23:27:18Z
git_ref: ed85721c
scope: code
substantive: true
raw_file: 20260728T232718Z_ed85721c_prompt_io.raw.md
---
## Prompt
Diagnose the live `pikerd` failure where backfill publication raised
`ValueError: OHLCV timestamps must be finite and positive` while merging the
known gappy MNQ NativeDB baseline with fresh IB history. Determine whether to
commit the pending audit stack or iterate first.
## Response summary
Traced the primary failure through `publish_latest_frame()` to
`NativeStorageClient.update_ohlcv()`. The incoming frame was valid, but the
canonicalized persisted baseline retained its epoch-zero row and caused the
merged frame to fail validation. Added persisted-row repair before merge and
a regression using the observed legacy schema characteristics.
## Files changed
- `piker/storage/nativedb.py` - drop non-positive persisted rows during merge
- `tests/test_storage_nativedb.py` - live MNQ failure-shape regression
- `ai/prompt-io/opencode/20260728T232718Z_ed85721c_prompt_io.raw.md`
- unedited response record
- `ai/prompt-io/opencode/20260728T232718Z_ed85721c_prompt_io.md`
- provenance metadata and response summary
## Human edits
None - generated changes have not been edited by the human.

View File

@ -0,0 +1,27 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
timestamp: 2026-07-28T23:27:18Z
git_ref: ed85721c
diff_cmd: git diff HEAD~1..HEAD
---
> `git diff HEAD~1..HEAD -- piker/storage/nativedb.py tests/test_storage_nativedb.py`
Fixed incremental NativeDB updates against legacy persisted history with
non-positive timestamps. Incoming provider frames remain strictly validated,
while merge repair discards invalid persisted rows before concat, dedupe,
ordering, canonical-index rewrite, and atomic publication.
The regression reproduces the observed MNQ baseline shape: one epoch-zero
row, noncanonical absolute indexes, extra derived columns, and a valid fresh
IB frame. It proves the invalid row is removed while all valid old and new
bars survive with canonical indexes.
Verification generated with the patch:
- NativeDB regressions: 18 passed
- history backfill regressions: 6 passed
- focused Ruff: passed
- `git diff --check`: passed

View File

@ -0,0 +1,40 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
session: 24fb9765-a550-4570-8350-f0fc9b7e17db
timestamp: 2026-07-29T03:41:06Z
git_ref: 922a5df8
scope: code
substantive: true
raw_file: 20260729T034106Z_922a5df8_prompt_io.raw.md
---
## Prompt
Diagnose the live IB reverse-fill failure where
`reqHistoricalDataAsync()` raised API error 10314 because its end date,
time, or timezone was invalid.
## Response summary
Traced the failure to `Client.bars()`, where an unfinished EST conversion
left the original datetime for `ib_async` to serialize as
`YYYYMMDD HH:MM:SS UTC`. Converted aware boundaries explicitly to UTC and
passed IB's accepted `YYYYMMDD-HH:MM:SS` string while retaining a blank
latest-request boundary. Added exact request-argument regressions.
## Files changed
- `piker/brokers/ib/api.py` - explicit UTC-dash history boundary format
- `tests/test_ib_history.py` - IB request-format regressions
- `.claude/skills/run-tests/test-harness-reference.md`
- deterministic IB history test mapping
- `ai/prompt-io/opencode/20260729T034106Z_922a5df8_prompt_io.raw.md`
- unedited response record
- `ai/prompt-io/opencode/20260729T034106Z_922a5df8_prompt_io.md`
- provenance metadata and response summary
## Human edits
None - generated changes have not been edited by the human.

View File

@ -0,0 +1,25 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
timestamp: 2026-07-29T03:41:06Z
git_ref: 922a5df8
diff_cmd: git diff HEAD~1..HEAD
---
> `git diff HEAD~1..HEAD -- piker/brokers/ib/api.py tests/test_ib_history.py .claude/skills/run-tests/test-harness-reference.md`
Fixed IB reverse-history requests to serialize aware end boundaries with
IB's explicit UTC-dash syntax, `YYYYMMDD-HH:MM:SS`. This bypasses
`ib_async`'s trailing-`UTC` representation, which the live Gateway rejected
with API error 10314, and removes the unfinished EST comparison breakpoint.
The regression captures the exact `reqHistoricalDataAsync()` argument for
latest, stdlib-aware, Pendulum UTC, and America/New_York inputs.
Verification generated with the patch:
- IB history request regressions: 4 passed
- focused Ruff: passed
- `git diff --check`: passed
- adversarial review: no findings

View File

@ -0,0 +1,40 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
session: 24fb9765-a550-4570-8350-f0fc9b7e17db
timestamp: 2026-07-29T04:17:23Z
git_ref: ce33deb6
scope: code
substantive: true
raw_file: 20260729T041723Z_ce33deb6_prompt_io.raw.md
---
## Prompt
Diagnose why the chart still showed a zero first OHLCV row after the
epoch-zero timestamp repair, causing auto-y-ranging to include zero.
## Response summary
Audited live 60s and 1s NativeDB files and found one exact 60s first-slot
sentinel at epoch 60 with a completely zero OHLCV payload. Added narrowly
scoped read and merge repair, matching incoming validation, sentinel-only
startup handling, canonical reindexing, and explicit audit classification.
Preserved modern all-zero bars as unclassified evidence rather than assuming
all zero-priced instruments are corrupt.
## Files changed
- `piker/storage/nativedb.py` - first-slot sentinel repair and validation
- `piker/storage/_audit.py` - sentinel classification and zero-row evidence
- `tests/test_storage_nativedb.py` - hydration, merge, and boundary regressions
- `tests/test_storage_audit.py` - sentinel and modern-zero audit regressions
- `ai/prompt-io/opencode/20260729T041723Z_ce33deb6_prompt_io.raw.md`
- unedited response record
- `ai/prompt-io/opencode/20260729T041723Z_ce33deb6_prompt_io.md`
- provenance metadata and response summary
## Human edits
None - generated changes have not been edited by the human.

View File

@ -0,0 +1,31 @@
---
model: gpt-5.6-sol
provider: openai
service: opencode
timestamp: 2026-07-29T04:17:23Z
git_ref: ce33deb6
diff_cmd: git diff HEAD~1..HEAD
---
> `git diff HEAD~1..HEAD -- piker/storage/nativedb.py piker/storage/_audit.py tests/test_storage_nativedb.py tests/test_storage_audit.py`
Fixed the remaining legacy first-slot sentinel observed in live MNQ 60s
history: `time=60` with zero open, high, low, close, and volume. NativeDB now
repairs the exact sentinel before read hydration and merge, reindexes the
filtered view, and persists the repaired frame on the next valid update.
The repair remains narrowly scoped. Modern all-zero OHLC bars stay valid for
spreads or synthetic instruments, null-containing malformed rows still fail
validation, new writes can not recreate the first-slot sentinel, and a file
containing only sentinels loads as no history so fresh backfill can proceed.
The raw audit reports total all-zero price rows, classifies first-slot
sentinels as structural violations, and leaves later all-zero rows visible as
unclassified warnings.
Verification generated with the patch:
- NativeDB, audit, history, xonsh, and IB regressions: 61 passed
- focused Ruff: passed
- `git diff --check`: passed
- final adversarial review: no findings

View File

@ -0,0 +1,34 @@
---
model: openai/gpt-5.6-sol
service: opencode
session: 6409e3bb-1a9a-4ada-b6c4-c554115143e9
timestamp: 2026-07-29T18:54:06Z
git_ref: d359b1cb
scope: code
substantive: true
raw_file: 20260729T185406Z_d359b1cb_prompt_io.raw.md
---
## Prompt
The user reported that QQQ 60-second zeros appeared as a flat line after
reload, clarified that the outstanding concern was the prior `ldshm`
failure, and instructed the agent to implement the more focused fix.
## Response summary
Changed `ldshm` to classify invalid SHM snapshots before downstream
analysis. Cadence is inferred only from observed finite positive steps;
buffers containing non-finite or non-positive timestamps, or lacking a
positive cadence, are skipped rather than partially replacement-written
or reloaded. Added deterministic helper and command-level regressions.
## Files changed
- `piker/storage/cli.py` - inspect cadence and reject invalid SHM snapshots before mutation.
- `tests/test_ldshm.py` - cover QQQ-like zeros, corrupt timestamps, cadence selection, degenerate buffers, and mutation prevention.
- `.claude/skills/run-tests/test-harness-reference.md` - register the deterministic regression target.
## Human edits
None - generated output remains uncommitted.

View File

@ -0,0 +1,58 @@
---
model: openai/gpt-5.6-sol
service: opencode
timestamp: 2026-07-29T18:54:06Z
git_ref: d359b1cb
diff_cmd: git diff HEAD~1..HEAD
---
The user clarified that the QQQ 60-second chart now displayed a flat
forward-filled segment, then asked whether the previously reported
`piker store ldshm` failure had been fixed. After being told it had not,
the user instructed: "ok but i read above you have a more focussed fix
todo now? so have at it!"
The implementation focused on the exact failure boundary. It does not
filter invalid rows into a partial replacement and does not mutate live
SHM. Instead, `ldshm` inspects one captured Polars snapshot, infers its
cadence from observed finite positive timestamp steps, and skips the
entire buffer before analysis, persistence, or reload when any timestamp
is non-finite or non-positive.
> `git diff HEAD~1..HEAD -- piker/storage/cli.py`
Generated `_shm_period_and_invalid_count()` and integrated its result in
`ldshm`. The helper selects the most frequent observed positive step,
using the smallest observed step as the deterministic tie-break. Short
or cadence-degenerate buffers are skipped. Invalid snapshots are logged
and cannot reach deduplication, NativeDB replacement, or SHM reload.
> `git diff HEAD~1..HEAD -- tests/test_ldshm.py`
Generated deterministic regressions for the QQQ-shaped interior zero
run, observed-step cadence selection, all invalid timestamp classes,
short and degenerate buffers, and command-level prevention of dedupe,
storage writes, and reload. The command-level test uses an immutable
Polars snapshot and failing spies, without live SHM mutation or timing.
> `git diff HEAD~1..HEAD -- .claude/skills/run-tests/test-harness-reference.md`
Added the deterministic `ldshm` regression target and its change-to-test
mapping to the repository test harness reference.
Verification output:
```text
.......................................................... [100%]
58 passed in 0.70s
```
`git diff --check` passed. Ruff was unavailable in the worktree's
existing environment. Three adversarial review passes found and drove
fixes for partial-replacement data loss, invented median timeframes,
mixed SHM observations, and pre-guard null-analysis failures. The final
review reported no remaining findings.
The patch intentionally does not claim that `ldshm` is safe to run as a
general concurrent repair writer. It handles invalid live snapshots by
refusing mutation rather than weakening NativeDB validation.

View File

@ -0,0 +1,37 @@
---
model: openai/gpt-5.6-sol
service: opencode
session: 253ade33-07ba-45b5-8839-be35d17ec164
timestamp: 2026-07-29T20:40:46Z
git_ref: ad6c560f
scope: code
substantive: true
raw_file: 20260729T204046Z_ad6c560f_prompt_io.raw.md
---
## Prompt
The user reported that the QQQ 1-second chart still contained a gap after
the `ldshm` fix, asked the agent to inspect it, and authorized continued
implementation after a short pause and shutdown of the live workspace.
## Response summary
Diagnosed a 653-row active RT SHM null reservation caused by inclusive IB
reverse-query overlaps and startup repair orchestration. Prevented extra
physical SHM overlap rows, serialized and bounded IB history recovery,
made blank/error responses terminate, and constrained synthetic repair to
valid interior timestamp boundaries. Added deterministic concurrency and
data-layout regressions.
## Files changed
- `piker/brokers/ib/feed.py` - bound and serialize IB history reset/retry behavior.
- `piker/tsp/_history.py` - prevent SHM overlap skew and safely finalize null repair.
- `tests/test_ib_history.py` - cover blank, cancellation, reset, and pacing interleavings.
- `tests/test_history_backfill.py` - cover overlap and null-repair invariants.
- `.claude/skills/run-tests/test-harness-reference.md` - register deterministic coverage.
## Human edits
None - generated output remains uncommitted.

View File

@ -0,0 +1,72 @@
---
model: openai/gpt-5.6-sol
service: opencode
timestamp: 2026-07-29T20:40:46Z
git_ref: ad6c560f
diff_cmd: git diff HEAD~1..HEAD
---
The user reported that QQQ still displayed a gap on the 1-second chart
after the focused `ldshm` fix and asked the agent to inspect it. The user
then paused and resumed implementation while shutting down the live piker
trading workspace.
Read-only live inspection found the active datad generation held 653
contiguous zero-time rows in QQQ's 1-second RT SHM, while durable Parquet
contained no zero rows. The run occupied absolute indexes 168148 through
168800 between timestamps 1785348398 and 1785349051. The stale actor
generation and active 60-second history were separately identified.
> `git diff HEAD~1..HEAD -- piker/tsp/_history.py`
Generated history changes that exclude each inclusive reverse-query
endpoint already present in SHM while retaining the full provider frame
for NativeDB merge. Startup null repair now runs after reverse backfill,
uses bounded UI notifications, handles provider no-data without an
interactive pause, fills only exact interior zero groups, and refuses
synthetic timestamps unless both valid boundaries establish the expected
cadence.
> `git diff HEAD~1..HEAD -- piker/brokers/ib/feed.py`
Generated IB history reset changes that make blank responses complete,
bound repeated cancellation and reset failures per request, serialize
timeout and pacing farm resets with one actor-global Trio lock, carry
explicit reset results, and coordinate pacing handoff with queued timeout
waiters.
> `git diff HEAD~1..HEAD -- tests/test_history_backfill.py`
Generated regressions for inclusive endpoint exclusion, full NativeDB
provider deltas, strict synthetic gap boundaries, empty-provider local
fallback, bounded notifications, and preservation of valid margin rows.
> `git diff HEAD~1..HEAD -- tests/test_ib_history.py`
Generated deterministic regressions for blank IB responses, cancellation
limits, stalled reset cancellation, pacing reset failure limits,
cross-request reset serialization, timeout-to-pacing handoff, and
successful handoff timeout restart.
> `git diff HEAD~1..HEAD -- .claude/skills/run-tests/test-harness-reference.md`
Registered the deterministic history-backfill target and expanded the IB
history mapping to include `feed.py`.
Verification output:
```text
........................................................................ [ 93%]
..... [100%]
77 passed in 2.12s
```
`git diff --check` passed. Multiple adversarial review rounds identified
and drove fixes for unbounded IB empty/cancellation/reset paths, stale
reset ownership, actor-global failure poisoning, concurrent farm resets,
double-counted pacing handoffs, physical endpoint overlap, and unsafe
synthetic timestamp boundaries. Final review reported no findings.
Live validation was intentionally deferred because the user shut down the
trading workspace. A fresh datad/chart startup is required to regenerate
SHM and verify that QQQ no longer contains the 653-row null run.

View File

@ -0,0 +1,41 @@
---
model: openai/gpt-5.6-sol
service: opencode
session: ses_0799212ebffe42arY96czXn89F
timestamp: 2026-07-31T16:52:15Z
git_ref: 0846cbd4
scope: code
substantive: true
raw_file: 20260731T165215Z_0846cbd4_prompt_io.raw.md
---
## Prompt
Evaluate and update `piker store` so bare groups and endpoints immediately
show help; prepare for eventual optional fuzzy selection in `anal` and
`ldshm`; aggregate recent time-series diagnostics into CLI endpoints. The
user selected the safe-foundation scope.
## Response summary
Added immediate help UX, exact NativeDB and SHM identities, read-only
`series` and `shm` diagnostics, selector-ready exact SHM names, and robust
discovery/attachment. Fixed related `ldshm` option and no-match failures.
Deferred optional `fzf` and mutation-default changes.
## Files changed
- `piker/storage/cli.py` - help UX and read-only series/SHM diagnostics.
- `piker/storage/nativedb.py` - canonical compound series identities.
- `piker/tsp/_history.py` - exact SHM discovery and dtype resolution.
- `piker/data/_sharedmem.py` - attach-only existing-segment helper.
- `piker/tsp/__init__.py` - SHM identity helper exports.
- `tests/test_store_cli.py` - command UX and endpoint regressions.
- `tests/test_ldshm.py` - SHM diagnostics and operational regressions.
- `tests/test_storage_nativedb.py` - series parsing/index regressions.
- `docs/manual_backfill_qualification.rst` - triage command documentation.
- `.claude/skills/run-tests/test-harness-reference.md` - test mapping.
## Human edits
None - generated output remains uncommitted.

View File

@ -0,0 +1,93 @@
---
model: openai/gpt-5.6-sol
service: opencode
timestamp: 2026-07-31T16:52:15Z
git_ref: 0846cbd4
diff_cmd: git diff HEAD~1..HEAD
---
The user requested an evaluation and possible update of `piker store`,
including immediate help output when a group or endpoint is invoked with
no arguments. The user also proposed eventual optional `fzf` selection for
`anal` and `ldshm`, and authorized aggregating the recent time-series and
SHM debugging work into CLI endpoints. After reviewing three scopes, the
user selected the recommended safe foundation rather than implementing
interactive fuzzy selection or changing mutation defaults immediately.
> `git diff HEAD~1..HEAD -- piker/storage/cli.py`
Generated no-argument help behavior for the `store` group and every
endpoint. Added read-only `series` and `shm` commands, JSON and Rich output,
exact `--shm-name` plumbing, SHM cadence/invalid/order/gap summaries, and
safe corrupt-epoch formatting. Fixed existing `ldshm` no-write/no-reload
control flow and no-match debugger behavior.
> `git diff HEAD~1..HEAD -- piker/storage/nativedb.py`
Generated canonical `NativeSeriesRef` parsing and deterministic listing.
Changed the internal index to preserve `(fqme, period)` identities while
keeping unique-FQME compatibility listing and cardinality. Hardened
malformed, sidecar, Unicode-period, symlink, and traversal handling.
> `git diff HEAD~1..HEAD -- piker/tsp/_history.py`
Generated exact `ShmBufferRef` parsing and stable discovery, replacing
unsafe substring globbing. Added dtype resolution that mirrors history
allocation, exact-ref attachment, disappearance handling, and optional
exact-name iteration.
> `git diff HEAD~1..HEAD -- piker/data/_sharedmem.py`
Generated attach-only SHM access which never creates a segment when a
discovered object disappears.
> `git diff HEAD~1..HEAD -- piker/tsp/__init__.py`
Exported selector-ready SHM identity, parsing, listing, and dtype helpers.
> `git diff HEAD~1..HEAD -- tests/test_store_cli.py`
Generated command-level help, exact durable-series JSON, read-only SHM JSON,
and unknown exact-name regressions.
> `git diff HEAD~1..HEAD -- tests/test_ldshm.py`
Generated SHM identity, summary, ordering, dtype, forged-name, disabled
mutation-flag, and gap-markup regressions.
> `git diff HEAD~1..HEAD -- tests/test_storage_nativedb.py`
Generated compound index, canonical parser, malformed filename, and dotted
FQME regressions.
> `git diff HEAD~1..HEAD -- docs/manual_backfill_qualification.rst`
Documented `store series` and `store shm` as separate read-only durable and
live-memory triage layers.
> `git diff HEAD~1..HEAD -- .claude/skills/run-tests/test-harness-reference.md`
Registered the deterministic storage CLI UX target and mapping.
Verification output:
```text
........................................................................ [ 71%]
............................. [100%]
101 passed in 1.72s
```
Real `piker store` and `piker store audit` invocations printed complete
help without `--help`, exiting with Click's normal no-args help status 2.
`git diff --check` passed. Ruff was unavailable in the existing worktree
environment.
Adversarial review drove fixes for provider dtype reconstruction, observed
versus expected cadence, exact SHM grammar, discovery races, Typer direct
defaults, malformed NativeDB periods, compound cardinality compatibility,
undefined `ldshm` reload payloads, no-match debugger hangs, ordering
corruption reporting, and dtype precedence. Final review found no issues.
Optional external `fzf` or built-in numbered selection remains deferred.
The exact series and SHM identities added here are the intended foundation
for that later patch.

View File

@ -0,0 +1,39 @@
---
model: openai/gpt-5.6-sol
service: opencode
session: ses_0799212ebffe42arY96czXn89F
timestamp: 2026-08-01T01:53:21Z
git_ref: 74ea2152
scope: code
substantive: true
raw_file: 20260801T015321Z_74ea2152_prompt_io.raw.md
---
## Prompt
Make `piker store ls` default to all NativeDB series as a compact counterpart
to detailed `series`, with sensible no-flag output and a visible help
description. Consolidate SHM inspection and repair under canonical `shm`,
because attachment to volatile segments is implied, and make inspection the
safe default while retaining explicit persistence and reload controls.
## Response summary
Replaced backend-oriented `ls` with compact NativeDB discovery and made both
listing commands default to all series. Consolidated SHM behavior under a
read-only-by-default `shm` command, guarded explicit repair options, updated
executable references and docs, and added command regressions.
## Files changed
- `piker/storage/cli.py` - listing defaults and unified SHM command modes.
- `tests/test_store_cli.py` - listing, help, and SHM mode regressions.
- `tests/test_ldshm.py` - explicit persistence-path regressions.
- `docs/manual_backfill_qualification.rst` - revised safe CLI workflow.
- `snippets/claude_debug_helper.py` - explicit SHM persistence commands.
- `.claude/skills/run-tests/test-harness-reference.md` - safety and test map.
- `.claude/skills/piker-slang/examples.md` - canonical command example.
## Human edits
None - generated output incorporates the user's design refinements.

View File

@ -0,0 +1,78 @@
---
model: openai/gpt-5.6-sol
service: opencode
timestamp: 2026-08-01T01:53:21Z
git_ref: 74ea2152
diff_cmd: git diff HEAD~1..HEAD
---
The user requested command-surface refinements after reviewing the new
storage CLI. Bare `ls` should default to all series and act as a compact
form of detailed `series`; commands should produce sensible default output
without requiring trigger flags; and `ls` needed a parent-help description.
The user first proposed folding read-only SHM inspection into `ldshm -r`,
then clarified that `shm` is the better canonical command because attaching
to volatile segments is implied by the resource name.
The resulting design uses `piker store ls` as a compact NativeDB FQME and
period view, while `piker store series` reports each exact sub-series with
size and path details. Both default to all records and accept an optional
FQME substring.
`piker store shm FQME` is read-only by default. Persistence and reload enter
the prior repair workflow only through explicit `--write-parquet` and
`--reload-parquet-to-shm` flags. Reload requires persistence, and read-only
formatting flags are rejected in repair mode instead of being ignored.
> `git diff HEAD~1..HEAD -- piker/storage/cli.py`
Generated shared NativeDB payload collection, compact and detailed output,
numeric period ordering, default-all semantics, the canonical `shm` command,
safe mode dispatch, explicit repair validation, and debugger propagation in
read-only mode.
> `git diff HEAD~1..HEAD -- tests/test_store_cli.py`
Generated regressions for no-flag listing, parent-help descriptions, numeric
period display, default read-only SHM JSON, reload dependencies, and repair
versus read-only option conflicts.
> `git diff HEAD~1..HEAD -- tests/test_ldshm.py`
Adjusted persistence regressions for the renamed command and explicit repair
entry while preserving the historical failure rationale.
> `git diff HEAD~1..HEAD -- docs/manual_backfill_qualification.rst`
Documented compact versus detailed NativeDB discovery, default read-only SHM
inspection, and the explicitly mutating repair invocation.
> `git diff HEAD~1..HEAD -- snippets/claude_debug_helper.py`
Updated executable debug helper commands to select explicit SHM persistence.
> `git diff HEAD~1..HEAD -- .claude/skills/run-tests/test-harness-reference.md`
Updated operational safety guidance and SHM regression mappings.
> `git diff HEAD~1..HEAD -- .claude/skills/piker-slang/examples.md`
Updated the command example to the canonical `shm` spelling.
Verification output:
```text
........................................................................ [ 69%]
................................ [100%]
104 passed in 5.77s
```
Real command-help rendering was checked for the parent group, `ls`, `series`,
and `shm`. Real read-only `ls` and `series --json` output was also exercised
against the configured NativeDB. `git diff --check` passed.
Adversarial review identified and drove fixes for lexicographic period
ordering, stale runbook safety wording, ignored mode-specific options, and an
explicit-default `--max-gaps 10` validation bypass. Final review found no
remaining issues. Real OS-backed SHM attachment and successful repair/reload
remain integration-level validation concerns.

View File

@ -0,0 +1,33 @@
---
model: openai/gpt-5.6-sol
service: opencode
session: ses_0799212ebffe42arY96czXn89F
timestamp: 2026-08-05T19:50:01Z
git_ref: f0f3c806
scope: code
substantive: true
raw_file: 20260805T195001Z_f0f3c806_prompt_io.raw.md
---
## Prompt
Inspect the completed NVDA 60s SHM backfill before workspace restart, explain
the persistent zero region, and continue fixing the indexing logic so sparse
IB history does not leave physical null reservations.
## Response summary
Captured a 42,720-row live SHM null span whose valid margins shared the same
timestamp. Replaced wall-clock row reservation with actual provider-boundary
placement, fixed stale SHM first-index, endpoint insertion and field-map
semantics, published the final storage prepend, and added a full multi-frame
`tsdb_backfill()` regression using a real `ShmArray`.
## Files changed
- `piker/tsp/_history.py` - contiguous provider and NativeDB prepends.
- `tests/test_history_backfill.py` - sparse multi-frame SHM regression.
## Human edits
None - generated output incorporates the user's live observations.

View File

@ -0,0 +1,71 @@
---
model: openai/gpt-5.6-sol
service: opencode
timestamp: 2026-08-05T19:50:01Z
git_ref: f0f3c806
diff_cmd: git diff HEAD~1..HEAD
---
The user reported that a completed NVDA 60s backfill left a large zero
region in live SHM and asked for immediate inspection before restarting the
workspace. The captured buffer contained 42,720 invalid rows at absolute
indexes `3065895..3108614`. The valid rows on both sides had the identical
timestamp `1781271360`, proving the region was an erroneous physical-index
reservation rather than a real temporal gap.
The user then asked the agent to continue fixing the backfill logic.
> `git diff HEAD~1..HEAD -- piker/tsp/_history.py`
Removed wall-clock-based SHM reservation from `tsdb_backfill()`. Reverse
provider frames now extend the visible `ShmArray._first` boundary on every
prepend, beginning exactly at the current first index after inclusive query
endpoints are removed. Added `_prepend_tsdb_history()` to trim storage rows
against the actual earliest provider timestamp and prepend them directly
adjacent only after reverse provider retrieval completes.
This preserves sparse venue history as ordinal samples rather than treating
every elapsed calendar period as a physical SHM row. It also prevents the
old `_first + 1` insertion point from overwriting the earliest published bar.
> `git diff HEAD~1..HEAD -- tests/test_history_backfill.py`
Added a full `tsdb_backfill()` regression modeled on the live NVDA failure.
It uses a real `ShmArray`, NativeDB's field map, two inclusive reverse-provider
frames, and overlapping stored history. Assertions prove the complete
timestamp sequence is contiguous and lossless, contains no non-positive
timestamps, sends complete provider frames to durable storage, and notifies
chart/FSP consumers after the final storage prepend.
Live evidence captured before workspace restart:
```text
rows 1575296 first_index 1568233 last_index 3143528
invalid_count 42720
left boundary
[(3065893, 1781271300, 204.03, 204.82)
(3065894, 1781271360, 204.81, 204.72)
(3065895, 0, 0. , 0. )]
right boundary
[(3108614, 0, 0. , 0. )
(3108615, 1781271360, 204.81, 205.44)
(3108616, 1781271420, 205.46, 205.48)]
```
Verification output:
```text
........................................................................ [ 68%]
................................. [100%]
105 passed in 4.80s
```
Adversarial review caught stale `_first` semantics and an off-by-one
insertion point in the initial patch. The first live restart then exposed an
undefined `time_key` local at the new `tsdb_backfill()` call site, which
crashed `datad.ib` and propagated to the chart as `RemoteActorError`. The
helper now derives the source timestamp field from its field map, and the
test enters the full enclosing lifecycle so the original `NameError` can not
recur unnoticed. A final review also drove explicit publication after stored
history is prepended and deterministic final-stage synchronization. Final
review found no remaining issues; another live restart remains pending.

View File

@ -0,0 +1,36 @@
---
model: openai/gpt-5.6-sol
service: opencode
session: ses_0799212ebffe42arY96czXn89F
timestamp: 2026-08-11T20:58:26Z
git_ref: 06d5ea5d
scope: code
substantive: true
raw_file: 20260811T205826Z_06d5ea5d_prompt_io.raw.md
---
## Prompt
Diagnose and fix an IB live-order failure where lazy NVDA contract
qualification raised `tractor.trionics.Lagged` after the order reached
`brokerd.ib`. Determine whether recent history changes or cancelled symbol
searches caused the stale state.
## Response summary
Traced the failure to competing consumers of a one-slot Trio broadcast, not
the recent SHM change or stale asyncio client state. Replaced broadcast
fan-out with one response/status demultiplexer, retained request-ID
correlation, handled cancellation and teardown, documented the task/channel
ownership model, warned on diagnostic metadata eviction, and added
deterministic proxy regressions.
## Files changed
- `piker/brokers/ib/api.py` - sole-reader IB proxy response routing.
- `tests/test_ib_method_proxy.py` - status, concurrency, cancellation and EOF regressions.
- `.claude/skills/run-tests/test-harness-reference.md` - proxy test mapping.
## Human edits
None - generated output follows the user's live failure diagnosis.

View File

@ -0,0 +1,66 @@
---
model: openai/gpt-5.6-sol
service: opencode
timestamp: 2026-08-11T20:58:26Z
git_ref: 06d5ea5d
diff_cmd: git diff HEAD~1..HEAD
---
The user reported that certain IB order entries might have broken and
provided a live failure for a first NVDA sell order. EMS delivered the
request to `brokerd.ib`, but lazy `get_mkt_info('nvda.nasdaq')` contract
qualification failed in `MethodProxy._run_method()` with:
```text
tractor.trionics._broadcast.Lagged:
Task `piker.brokers.ib.broker.handle_order_requests` overrun and
dropped `0` values
```
The user suspected a cancelled symbol-search task might have left stale
asyncio state. Cancellation can leave an orphaned method response, but the
observed failure was stale Trio broadcast state. `open_client_proxy()`
broadcast a one-slot `LinkedTaskChannel` to an always-running IB status relay
and an idle method receiver. Status traffic advanced only the relay, so the
first method call for an uncached symbol entered an already-lagged receiver.
Orders for eagerly cached contracts bypassed this path.
The immediately preceding sparse-history commit did not modify IB order,
symbol, or proxy code; it exposed no direct dependency on this failure.
> `git diff HEAD~1..HEAD -- piker/brokers/ib/api.py`
Generated a sole-reader message demultiplexer for each IB client proxy.
`relay_client_proxy_messages()` owns every asyncio-to-Trio receive, handles
status/error traffic, and dispatches `mid`-tagged responses into per-call
`MethodProxy` wait slots. Cancelled calls remove their pending slot, causing
late responses to be logged and dropped without skewing later calls. Clean
channel EOF is accepted during normal proxy teardown.
Follow-up review restored the masked reconnect TODO and per-message case
rationale, documented the one-reader/many-writer task hierarchy and separate
per-proxy channels, and added a warning with request method metadata when the
bounded unresolved-request diagnostic table evicts its oldest entry.
> `git diff HEAD~1..HEAD -- tests/test_ib_method_proxy.py`
Generated deterministic regressions for idle status traffic before first
qualification, two concurrent calls with reverse-order responses, cancelled
calls with late responses followed by a successful call, and graceful
channel EOF. Every synchronization wait is timeout-bounded.
> `git diff HEAD~1..HEAD -- .claude/skills/run-tests/test-harness-reference.md`
Registered the deterministic IB method-proxy target and source mapping.
Verification output:
```text
........................ [100%]
25 passed, 4 warnings in 3.44s
```
The warnings are existing Tractor/Trio deprecations from the EMS test.
Adversarial review drove a clean-EOF guard and bounded polling. Final review
found no actionable issues. Live IB order validation remains pending and
requires restarting `brokerd.ib`.

View File

@ -0,0 +1,43 @@
---
model: openai/gpt-5.6-sol
service: opencode
session: unavailable
timestamp: 2026-08-14T03:27:16Z
git_ref: 2aa66c2c
scope: code
substantive: true
raw_file: 20260814T032716Z_2aa66c2c_prompt_io.raw.md
---
## Prompt
Prototype chart-actor-owned gap overlays from the current backfiller tip,
with actor-local startup and UI toggling. Preserve remote annotation IPC and
make the gap-specific path strict and `msgspec`-typed.
## Response summary
Adds a chart-local controller and vectorized SHM detector, starts historical
gap overlays with display state, toggles the focused timeframe through the
existing input loop and exposes the same renderer through a strict Tractor
payload context. Keeps all legacy remote annotation commands and separates
local graphics from each remote context's teardown ownership.
## Files changed
- `piker/ui/_gaps.py` - typed messages, detection and local controller.
- `piker/ui/_remote_ctl.py` - IPC clients and chart-actor endpoints.
- `piker/ui/_display.py` - actor-local startup integration.
- `piker/ui/_interaction.py` - focused `Ctrl+G` toggle.
- `piker/ui/_widget.py` - controller reference.
- `piker/ui/_annotate.py` - safe FQME-aware repositioning.
- `piker/tsp/_annotate.py` - typed annotation-client reference.
- `piker/storage/cli.py` - renamed annotation client context.
- `tests/test_gap_overlays.py` - detector and schema regression.
- `tests/test_ldshm.py` - renamed annotation-client test seams.
- `plans/opencode/chart-local-gap-overlays.md` - architecture and landing plan.
## Human edits
None - the work remains an uncommitted disposable-worktree prototype for
human review and live chart qualification.

View File

@ -0,0 +1,72 @@
---
model: openai/gpt-5.6-sol
service: opencode
timestamp: 2026-08-14T03:27:16Z
git_ref: 2aa66c2c
diff_cmd: git diff HEAD~1..HEAD
---
## Prompt
The user asked what work had been done so far, then authorized autonomous
work while AFK. The target was to scheme and prototype an actor-local gap
annotator from the `backfiller_deep_fixes` tip without mutating existing
branches. The user then clarified twice that the annotation IPC API must
remain available and should become more rigorous and `msgspec`-typed rather
than being removed.
## Generated code
> `git diff HEAD~1..HEAD -- piker/ui/_gaps.py`
Adds tagged gap request/reply structs, render-ready gap specs, vectorized
OHLCV timestamp-gap detection and chart-local overlay ownership.
> `git diff HEAD~1..HEAD -- piker/ui/_remote_ctl.py`
Adds strict Tractor gap IPC, `AnnotClient` stream mapping/locking and
owner-scoped endpoint cleanup while retaining the legacy annotation endpoint.
> `git diff HEAD~1..HEAD -- piker/ui/_display.py`
Starts the local 60-second overlay after display-state registration.
> `git diff HEAD~1..HEAD -- piker/ui/_interaction.py`
Routes `Ctrl+G` from the focused chart's existing async input loop directly
to the local controller.
> `git diff HEAD~1..HEAD -- piker/ui/_widget.py`
Stores the chart actor's local controller reference.
> `git diff HEAD~1..HEAD -- piker/ui/_annotate.py`
Keeps redraw repositioning isolated by FQME and synchronizes arrow geometry.
> `git diff HEAD~1..HEAD -- piker/tsp/_annotate.py piker/storage/cli.py`
Renames the remote annotation control type and context manager to the
client-oriented `AnnotClient` / `open_annot_client` interface.
> `git diff HEAD~1..HEAD -- tests/test_gap_overlays.py`
Adds deterministic detector, directional payload-schema, typed error and
msgpack roundtrip regressions.
> `git diff HEAD~1..HEAD -- tests/test_ldshm.py`
Updates storage CLI test seams for the renamed annotation client context.
## Generated architecture output
> `git diff HEAD~1..HEAD -- plans/opencode/chart-local-gap-overlays.md`
Documents ownership, startup, UI, strict IPC, refresh and branch landing
semantics.
## Verification output
Source resolution points to this worktree. The targeted regression passes,
Ruff reports no changed-file findings, `compileall` succeeds, `git diff
--check` succeeds and Tractor's strict gap payload decoder initializes.

View File

@ -0,0 +1,39 @@
---
model: openai/gpt-5.6-sol
service: opencode
session: unavailable
timestamp: 2026-08-17T22:20:36Z
git_ref: f65df6a1
scope: code
substantive: true
raw_file: 20260817T222036Z_f65df6a1_prompt_io.raw.md
---
## Prompt
Add end-to-end coverage for chart-local gap annotations in the existing
gap-overlay worktree without disturbing concurrent manual backfiller testing
in the root checkout.
## Response summary
Adds real offscreen Qt and real Tractor actor integration coverage. The tests
drive startup registration, PyQtGraph insertion/removal, focused Ctrl-G input,
history-prepend repositioning, duplicate-FQME chart identity, typed endpoint
exchange and cancellation with stale-response recovery. Defects exposed by
those tests are fixed in manager cleanup, Qt geometry notification and PyQt6
event routing.
## Files changed
- `piker/ui/_gaps.py` - correct PyQtGraph ownership and bounds behavior.
- `piker/ui/_annotate.py` - notify Qt before reposition geometry changes.
- `piker/ui/_display.py` - expose deterministic startup registration.
- `piker/ui/_interaction.py` - focus toggles and PyQt6 event registration.
- `tests/test_gap_overlays.py` - real Qt and Tractor integration coverage.
- `plans/opencode/chart-local-gap-overlays.md` - update coverage boundaries.
- `plans/opencode/chart-local-gap-overlays.summary.md` - update test totals.
## Human edits
None - the changes remain uncommitted for human review.

View File

@ -0,0 +1,59 @@
---
model: openai/gpt-5.6-sol
service: opencode
timestamp: 2026-08-17T22:20:36Z
git_ref: f65df6a1
diff_cmd: git diff f65df6a1..HEAD
---
## Prompt
The user asked whether the chart-local gap annotation feature had complete
end-to-end coverage, then requested implementation in the existing gap-overlay
worktree while manual NativeDB/backfiller qualification continued in the root
checkout. The user initially declined a tests-only Prompt-IO entry. Real Qt
and Tractor integration tests then exposed production lifecycle and PyQt6
event-routing defects, making the resulting patch substantive code work and
requiring this full provenance entry.
## Generated code
> `git diff HEAD~1..HEAD -- piker/ui/_gaps.py`
Removes manager-owned graphics through their `PlotItem`, retains owning-plot
registrations, and excludes pixel-sized gap arrows from automatic data bounds.
> `git diff HEAD~1..HEAD -- piker/ui/_annotate.py`
Moves Qt geometry-change notification ahead of rectangle and arrow mutation so
scene spatial indexing follows history-prepend repositioning.
> `git diff HEAD~1..HEAD -- piker/ui/_display.py`
Extracts `_register_gap_overlays()` as the deterministic chart startup seam
which registers display states and renders default historical gap layers.
> `git diff HEAD~1..HEAD -- piker/ui/_interaction.py`
Adds focused-chart toggle routing, updates the gap keyboard path to PyQt6 event
enums, and keeps unrelated cached chart states untouched.
> `git diff HEAD~1..HEAD -- tests/test_gap_overlays.py`
Adds offscreen real-Qt manager, startup, keyboard, cached-chart and reposition
coverage plus a real Tractor actor endpoint/client dialog with deterministic
cancellation and stale-response recovery.
## Generated architecture output
> `git diff HEAD~1..HEAD -- plans/opencode/chart-local-gap-overlays.md plans/opencode/chart-local-gap-overlays.summary.md`
Records the automated integration boundary and remaining broker-backed manual
qualification requirements.
## Verification output
The source-isolated non-actor and related suites pass 24 tests with one actor
case deselected. The isolated actor case passes with one upstream Tractor/Trio
deprecation warning. Ruff, compileall, import resolution and whitespace checks
pass. Independent review reports no remaining P1/P2 findings.

Some files were not shown because too many files have changed in this diff Show More