Compare commits

..

8 Commits

Author SHA1 Message Date
Gud Boi 7c8c1c4cb6 Merge pull request 'moar_ai_skillz' (#93) from moar_ai_skillz into main
Reviewed-on: #93
2026-10-07 07:06:04 +00:00
Gud Boi 45d0f003fc Move Piker skills to `.agents` discovery
- keep repository-owned skill sources provider-neutral
- retain Claude discovery through portable relative links
- drop tracked local settings and the superseded style guide

Prompt-IO: ai/prompt-io/opencode/20260908T043134Z_f148fbcc_prompt_io.md

(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
2026-09-08 00:35:01 -04:00
Gud Boi f148fbcc23 Add neutral `.ai` workflow config
Select the provider-neutral backend and port repo-owned commit-msg
and test-harness refs into `.ai`.

Also,
- ignore local `.agents` links and generated runtime state.
- retain legacy Claude archives for recovery.
- record Prompt-IO provenance for the migration.

Prompt-IO: ai/prompt-io/opencode/20260907T214000Z_9115d24_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-09-07 19:07:00 -04:00
Gud Boi 00e7ca45f6 Refresh `ai.skillz` provider deployment
Move managed worktree ignores to the provider-neutral `/wkts/` root
and remove obsolete `commit-msg` session state.

Add `code-nav-refs` links and the remaining OpenCode command shims so
both provider deployments validate against the current manifest.

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-23 19:59:15 -04:00
Gud Boi 4880f0d3c6 Deploy cross-provider AI skills
Replace the tracked global `commit-msg` symlink with ignored local
deployments for the complete Claude and OpenCode skill sets.

Add OpenCode command shims for review, commit, test, PR, Taken, and
session-cleaning workflows while preserving repository-local runtime
artifacts.

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-12 14:37:44 -04:00
Gud Boi 7b7ecb6747 Add `skills/commit-msg/conf.toml` to `.gitignore`
(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-27 12:09:45 -04:00
Gud Boi 899e3ed992 Symlink `commit-msg` SKILL.md to global skill
Replace the repo-local 84-line `SKILL.md` with a
symlink to `~/.claude/skills/commit-msg/SKILL.md`
so the skill definition is maintained in one place
across all repos.

(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-27 12:09:45 -04:00
Gud Boi e9cf1d7379 Move commit-msg output to `msgs/` subdir
Relocate generated commit message files from
`.claude/` root into `.claude/skills/commit-msg/msgs/`
and switch timestamp format to filesystem-safe UTC
(`%Y%m%dT%H%M%SZ`).

Also,
- add `msgs/` dir and `git_commit_msg_LATEST.md` to
  `.gitignore`.

(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:18:05 -04:00
299 changed files with 3827 additions and 39535 deletions

View File

@ -1,174 +0,0 @@
---
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

@ -1,210 +0,0 @@
# 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

@ -1,146 +0,0 @@
# 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

@ -1,84 +0,0 @@
# 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

@ -1,78 +0,0 @@
---
name: piker-fsp-expert
description: Piker financial signal processing expertise. Apply when changing or debugging FSP definitions, Cascade execution, derived shared-memory arrays, history/realtime handoff, backfill synchronization, FSP graph composition, online financial algorithms, or FSP chart lifecycle and scaling.
---
# Piker FSP Expertise
Treat financial signal processing as causal, revision-aware processing of
event-time market data, not as generic batch array manipulation.
## Start Here
1. Identify the source `Flume`, timeframe, event clock, and writer actor.
2. State whether the operation is historical bootstrap, backfill revision,
current-sample mutation, or sample-step append.
3. Record source and destination absolute SHM bounds before reasoning from
lengths or `ShmArray.index`.
4. Separate compute, SHM publication, IPC wakeup, and graphics costs before
optimizing.
5. Preserve causality, warm-up behavior, missing-data semantics, and output
schema in every implementation and test.
Read [architecture.md](architecture.md) before modifying runtime code. Read
[signal-processing.md](signal-processing.md) before adding an algorithm. Use
[roadmap-and-research.md](roadmap-and-research.md) for design and dependency
work, and [test-map.md](test-map.md) for verification.
## Core Contracts
- `piker.data` owns provider ingestion, source `Flume`s, history publication,
quote fan-out, OHLC mutation, and `samplerd` step events.
- `piker.fsp` owns operator declarations, historical computation, realtime
state updates, and destination SHM publication.
- `piker.ui._fsp.FspAdmin` currently owns FSP worker selection, destination
allocation, cascade lifetime, and chart attachment.
- One logical writer owns each destination FSP array.
- Output event time and absolute indices derive from the source series, not
task scheduling or quote arrival order.
- A source prepend changes historical provenance even when the current sample
index is unchanged. Recompute or patch the affected destination range.
- One-step destination lag is normal between source sampling and destination
append. A destination lead or unexplained bound shift is not.
- Backfill notifications are source and timeframe specific even though the
current sampler transport broadcasts them globally.
- Only graphics backed by a rebuilt destination should refresh. Never use an
FSP update as an unconditional whole-chart redraw request.
## Editing Workflow
1. Trace the operator from `@fsp` declaration through `cascade()`,
`connect_streams()`, destination SHM, `FspAdmin`, and every consuming
`Viz`.
2. Write an event-order table covering source writes, bound publication,
notifications, cancellation, recomputation, and destination publication.
3. Add deterministic tests for history and realtime phases independently.
4. Add a controlled interleaving test for every race fix.
5. Profile representative visible ranges and feed rates before adding a
dependency or native boundary.
## Companion Skills
- Use `piker-conc-expert` for Tractor contexts, actor topology, cancellation,
service ownership, and cross-actor state.
- Use `timeseries-optimization` for NumPy/Polars lookup and vectorization.
- Use `piker-profiling` for distributed performance measurements.
- Use `pyqtgraph-optimization` for renderer and graphics batching work.
- Use `piker-clearing-expert` when a derived signal reaches EMS or accounting.
## Do Not
- Do not hide a historical mismatch merely to stop a resync log.
- Do not infer a complete synchronization state from array length alone.
- Do not run Python loops over historical numeric arrays without evidence.
- Do not apply regular-grid DSP blindly to irregular tick arrival times.
- Do not introduce an external stream runtime that duplicates Tractor before
measuring an isolated protocol or kernel prototype.
- Do not route strategy signals directly around EMS order-state, risk, or
accounting contracts.

View File

@ -1,117 +0,0 @@
# FSP Runtime Architecture
## Current Topology
```text
provider backend
-> datad feed bus
-> source Flume
-> rt ShmArray (typically 1 second)
-> hist ShmArray (typically 60 seconds)
-> samplerd sample and backfill events
chart actor
-> FspAdmin
-> chart.fsp_N worker actor
-> cascade()
-> connect_streams()
-> destination Flume / ShmArray
-> targeted Viz updates
```
The source path is implemented mainly by `piker.data.feed`,
`piker.data.flows`, `piker.data._sampling`, and `piker.tsp._history`. The FSP
path is implemented by `piker.fsp._api`, `piker.fsp._engine`, and operator
modules such as `_volume` and `_momo`. Worker and graphics lifecycle currently
live in `piker.ui._fsp` and `piker.ui._display`.
## Operator Contract
`@fsp` wraps an async generator. Its first yield is historical output computed
from the source array. Later yields are `(field, value)` realtime mutations.
`connect_streams()` converts the first yield into the destination structured
array, aligns time and absolute bounds, then updates the current destination
row from realtime quote events.
`samplerd` owns row advancement. At a new source sample, the cascade copies or
zeros the previous destination row and aligns destination timestamps to the
source. Quote-time mutations and sample-time appends are distinct operations.
## SHM Synchronization
Treat each array as an absolute half-open range:
```text
[first, last)
```
For a normal source history prepend:
```text
before: src=[10000, 12000) dst=[10000, 12000)
after: src=[ 8000, 12000) dst=[10000, 12000)
```
The current step is unchanged, but destination history is missing 2,000 rows.
The destination must be recomputed or incrementally repaired.
For a normal one-step source lead:
```text
src=[8000, 12001) dst=[8000, 12000)
```
The cascade should append exactly one destination row, not recompute history.
Use `_first.value` and `_last.value` for synchronization. `ShmArray.index` is
the last bound modulo capacity and is not a complete absolute position.
Snapshot bounds once per check; shared first and last counters do not form a
transactional pair.
## Backfill Event Ordering
1. `start_backfill()` receives an older provider frame.
2. `shm_push_in_between()` writes the frame and publishes the earlier first
bound.
3. `notify_backfill()` asks `samplerd` to broadcast the market and timeframe.
4. Relevant cascades treat the event as a history revision. A prepend changes
bounds; an in-place gap repair may not.
5. Each cascade cancels its current compute task and waits for completion.
6. The historical phase recomputes against the newest readable source range.
7. Destination bounds are published and the UI receives an `fsp_update`.
8. Only `Viz` objects backed by that destination token redraw.
Multiple provider frames may arrive while a recomputation runs. The cascade
must reject mixed-bound bootstrap results, replay in-place revisions, recheck
bounds after restart, and converge on the newest source range.
## Actor-Local State
`Fsp._flow_registry`, attached SHM handles, caches, and nurseries are local to
each actor. Never assume worker siblings share warmed registries or Python
objects. Graph dependencies currently rely on startup order, as demonstrated
by `flow_rates` starting only after `dolla_vlm`.
## Cross-Subsystem Boundaries
- Data ingest should normalize identity and event-time semantics before an FSP
consumes a stream.
- UI renderers should consume revision/range information without controlling
computation correctness.
- Clearing may consume a derived signal, but EMS remains responsible for order
intent, status, routing, fills, positions, and accounting.
- FSP output exposed as a feed needs the same fan-out, identity, lifecycle,
and backpressure contracts as provider feeds.
## Architectural Gaps
The current protocol infers history revisions from shared bounds. A future
revision message should carry at least:
```text
(fqme, timeframe, revision, first, last, start_ts, end_ts)
```
Destination publication should identify the source revision used. This allows
coalescing, range repair, stale-result rejection, and deterministic graph
dependency startup without turning every prepend into a global wakeup.

View File

@ -1,104 +0,0 @@
# Roadmap and Research
Issue states change. Re-read each tracker before planning work and preserve
human ownership of issue and checklist state.
## Guiding Roadmap
- [piker #270](https://github.com/pikers/piker/issues/270): typed,
stream-composable FSP graph and DSL direction.
- [piker #107](https://github.com/pikers/piker/issues/107): cross-actor shared
memory tick/ring-buffer direction.
- [piker #216](https://github.com/pikers/piker/issues/216): exposing FSP output
through feed-like interfaces.
- [piker #325](https://github.com/pikers/piker/issues/325): faster streaming
y-range computation and overlap with FSP reductions.
- [piker #536](https://github.com/pikers/piker/issues/536): Arrow/Parquet data
ingest and columnar boundaries.
- [piker #98](https://github.com/pikers/piker/issues/98): older realtime feed
architecture discussion. Treat it as likely superseded; compare every idea
against current `Flume`, feed-bus, history, and sampler code.
- [Tractor #339](https://github.com/goodboy/tractor/issues/339): shared-memory
array and localhost IPC evolution.
- [hotbaud issues](https://github.com/guilledk/hotbaud/issues): experimental
high-throughput local transport and ring-buffer work.
## Near-Term Protocol Research
Prototype an explicit source revision envelope and measure it with two FSPs
and two overlaid feeds. The prototype should prove source-specific wakeups,
coalescing of consecutive prepends, stale-result rejection, and targeted UI
range updates. Keep the protocol independent of the final IPC transport.
Characterize historical repair strategies:
1. Full replay from the new first bound.
2. Prefix-only calculation for pointwise operators.
3. Replay from an operator checkpoint for stateful recurrences.
4. Bounded overlap repair for finite-window operators.
An FSP declaration eventually needs repair metadata describing locality,
state checkpointability, warm-up/overlap, and whether old inputs can alter all
later outputs.
## Candidate Libraries
Candidates are experiments, not dependency recommendations.
### Bounded Near-Term Prototypes
- [Bottleneck](https://github.com/pydata/bottleneck): compare rolling min/max,
rank, and moving-window kernels against NumPy/Numba for chart y-ranges and
finite-window FSPs.
- [River](https://github.com/online-ml/river): study stateful online estimator
APIs, drift detectors, and reference semantics. Do not assume its object
model belongs in the hottest tick path.
- [nanoarrow](https://github.com/apache/arrow-nanoarrow): evaluate lightweight
Arrow C data interfaces at native/process boundaries already motivated by
`pyarrow` and piker #536.
- [DuckDB](https://github.com/duckdb/duckdb): use for historical Parquet query,
replay slicing, and validation, outside realtime tick execution.
### Watchlist
- [hotbaud](https://github.com/guilledk/hotbaud): track API and correctness
maturity for localhost SHM/ring transport.
- [iceoryx2](https://github.com/eclipse-iceoryx/iceoryx2): study zero-copy
pub/sub ownership and lifecycle, but avoid competing with Tractor without a
narrow adapter benchmark.
- [DataFusion Python](https://github.com/apache/datafusion-python): Arrow-native
historical query and expression execution if existing Polars paths cannot
meet a demonstrated need.
- [Feldera](https://github.com/feldera/feldera): study DBSP/incremental-view
semantics for revision-aware graphs; its service/SQL runtime is not a direct
piker integration target.
- [Bytewax](https://github.com/bytewax/bytewax) and
[Arroyo](https://github.com/ArroyoSystems/arroyo): study dataflow semantics,
watermarking, and recovery, not as replacements for the actor tree.
GPU stacks such as JAX and CuPy are poor defaults for low-batch per-tick work
because transfer, compilation, and scheduling can dominate. Reconsider only
for measured large-batch research or many-market matrix workloads.
## Benchmark Design
Report separate distributions for:
- provider decode and normalization;
- source SHM write and bound publication;
- historical kernel and realtime recurrence;
- actor notification and stream fan-out;
- destination publication;
- visible-range formatting and Qt paint.
Use p50, p95, p99, maximum, allocation rate, and resident-memory growth. Test
steady state, burst load, backfill overlap, cancellation, and slow consumers.
Throughput without event-time correctness and bounded teardown is not a win.
## Medium-Term Sequence
First establish revisioned range messages and operator repair semantics. Then
decouple FSP orchestration from chart ownership and expose destination flows
through feed-like subscriptions. Next benchmark localhost transport adapters
behind the same protocol. Only after those contracts stabilize should graph
placement, fusion, worker scaling, and remote partitioning be automated.

View File

@ -1,103 +0,0 @@
# Financial Signal Processing
## Model the Clock First
Market inputs can be quote events, trades, book changes, fixed-time bars, or
revised historical bars. Name both event time and processing time. For
irregular events, either use time-aware recurrences or define the resampling
and interpolation policy explicitly.
Never let backfilled information leak into a value presented as historically
tradable. Distinguish corrected analysis history from the signal values that a
live strategy could have observed.
## Useful Technique Families
### Online Moments and Robust Statistics
Use Welford/Chan recurrences for stable online moments, compensated summation
for long accumulations, and mergeable summaries when partitioning actors.
Rolling median, quantiles, MAD, and winsorized measures are useful under heavy
tails, but exact rolling order statistics need more state than moments.
### Time-Aware Exponential Filters
For irregular event intervals, derive decay from elapsed time:
```text
alpha(dt) = 1 - exp(-dt / tau)
y_t = y_prev + alpha(dt) * (x_t - y_prev)
```
Record initialization, session reset, gap, and backfill behavior. A fixed
sample alpha is valid only after an explicit regularization policy.
### State-Space Models
Kalman and robust state-space filters fit latent price, spread, volatility,
and lead/lag estimates. Prefer square-root or Joseph-form covariance updates
when conditioning is poor. State revisions after backfill require replay from
a checkpoint or a bounded smoother; silently prepending observations without
replaying state is incorrect.
### Point Processes and Microstructure
Trade and quote arrivals are events, not merely sampled amplitudes. Consider
intensity, duration, signed order flow, imbalance, spread, queue change, and
self-excitation models. Validate against provider-specific aggregation and
duplicate/out-of-order behavior before interpreting a statistic.
### Volatility and Covariance
Realized variance, bipower variation, EW covariance, and range estimators can
be updated online. Asynchronous cross-market covariance needs synchronization
or estimators designed for non-synchronous observations; naive row alignment
creates lead/lag and Epps-effect artifacts.
### Spectral and Multiscale Methods
FFT methods assume a regular grid and a window. Declare detrending, tapering,
overlap, normalization, and latency. For tick data, resample deliberately or
use irregular-time methods. Wavelets and multiresolution filters can separate
horizons, but boundary handling and causal delay must be visible to strategy
code.
### Change and Anomaly Detection
CUSUM, Page-Hinkley, sequential likelihood ratios, and robust z-scores are
cheap online tools. Calibrate false alarms under dependence and regime shifts;
do not treat IID thresholds as market guarantees.
## Numerical Implementation Order
1. Establish a scalar reference with explicit state and semantics.
2. Vectorize historical bootstrap with NumPy.
3. Keep realtime updates as O(1) recurrences where possible.
4. Use Numba for measured numeric kernels with stable dtypes and no Python
object traffic.
5. Use Polars or Arrow for columnar historical transforms and interchange,
not automatically for each tick.
6. Partition by independent market/operator state before adding threads or a
new distributed runtime.
## Existing Stack
- NumPy 2.x: canonical dense and structured-array kernels.
- Numba: compiled CPU loops and recurrences that do not vectorize cleanly.
- Polars: parallel/lazy columnar history preparation and validation.
- PyArrow: columnar interchange, storage, and ingest boundaries.
- Tractor/Trio: structured distributed execution and lifecycle.
- PyQtGraph: visible-range rendering, not a compute scheduler.
## Algorithm Acceptance Criteria
Every new FSP should define:
- input fields, clock, ordering, duplicate, gap, and revision policy;
- output dtype, units, identity, and parameterization;
- warm-up length and initialization bias;
- causal latency and strategy-visible publication point;
- historical/realtime equivalence tolerance;
- reset and session-boundary behavior;
- complexity, state size, and representative throughput;
- replay behavior when historical source data changes.

View File

@ -1,52 +0,0 @@
# FSP Test Map
## Operator Tests
- Historical output schema, dtype, units, and source timestamp alignment.
- Realtime update equivalence with the final row of a batch recomputation.
- Warm-up, NaN, zero, gap, duplicate, and out-of-order behavior.
- Session reset and elapsed-time behavior.
- Parameter and source identity isolation.
## Engine Tests
- Equal absolute source/destination bounds.
- Normal one-step destination lag.
- Destination lead and lag greater than one step.
- Source prepend by one, two, and a full provider frame.
- In-place gap repair with unchanged source bounds.
- Equal lengths with shifted absolute bounds.
- Source change during historical recomputation and convergence afterward.
- Cancellation while the quote stream, sample stream, or compute generator is
blocked.
- Foreign FQME and timeframe backfill broadcasts.
## Graph and Actor Tests
- Dependency startup before downstream operator lookup.
- Actor-local flow registry isolation.
- One destination writer and multiple readers.
- Consumer cancellation without orphaned cascade or worker tasks.
- Worker failure propagation and SHM cleanup.
- Feed-like fan-out with a slow or disconnected consumer.
## UI Tests
- An `fsp_update` redraws every field visualization sharing its destination
token and no unrelated market, OHLC, or FSP visualization.
- A prepend outside the visible range does not move or re-range the live view.
- A revised value inside the visible range invalidates only required formatter
and graphics caches.
- Hidden charts defer rendering without stopping compute correctness.
## Performance Tests
- Historical bootstrap across realistic SHM capacities.
- O(1) realtime recurrence cost under bursty quotes.
- Consecutive provider-frame prepends with two or more cascades.
- Multi-market sampler broadcasts and targeted wakeups.
- Visible-range redraw cost at several units-per-pixel levels.
Prefer deterministic local arrays and controlled Trio task interleavings.
Use live providers only as a separate integration layer because credentials,
rate limits, sessions, and changing history make poor regression oracles.

View File

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

View File

@ -5,11 +5,7 @@ description: >
ethos. Apply when communicating with piker devs,
writing commit messages, code review comments, or
any collaborative interaction.
compatibility: >
Harness-independent communication guidance.
metadata:
author: goodboy
version: "1.0"
user-invocable: false
---
# 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()`,
`ClassName`, `field_name`
- File paths: `piker/ui/_remote_ctl.py`
- Commands: `git status`, `piker store shm`
- Commands: `git status`, `piker store ldshm`
**Explain like you're pair programming:**
```

View File

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

View File

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

View File

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

View File

@ -1,12 +0,0 @@
{
"permissions": {
"allow": [
"Bash(chmod:*)",
"Bash(/tmp/piker_commits.txt)",
"Bash(python:*)",
"Bash(ls:*)"
],
"deny": [],
"ask": []
}
}

View File

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

View File

@ -1,155 +0,0 @@
---
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

@ -1,100 +0,0 @@
# 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

@ -1,116 +0,0 @@
# 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

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

View File

@ -0,0 +1 @@
../../.agents/skills/piker-profiling

View File

@ -0,0 +1 @@
../../.agents/skills/piker-slang

View File

@ -0,0 +1 @@
../../.agents/skills/pyqtgraph-optimization

View File

@ -0,0 +1 @@
../../.agents/skills/timeseries-optimization

354
.gitignore vendored
View File

@ -107,14 +107,11 @@ ENV/
.git/
# any commit-msg gen tmp files
.claude/*_commit_*.md
.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
.claude/*_commit_*.md
.claude/*_commit*.toml
# nix develop --profile .nixdev
.nixdev*
@ -137,61 +134,6 @@ gitea/
# LLM conversations that should remain private
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
@ -200,17 +142,7 @@ docs/conversations/
/.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
@ -222,6 +154,121 @@ claude_wkts
/.opencode/skills/open-wkt
# END ai.skillz: direct:symlink:opencode:open-wkt
# 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: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
.ai/code-review/reports/
# END ai.skillz: runtime:code-review
# BEGIN ai.skillz: direct:symlink:claude:code-review
/.claude/skills/code-review
# END ai.skillz: direct:symlink:claude:code-review
# BEGIN ai.skillz: direct:symlink:opencode:code-review
/.opencode/skills/code-review
# END ai.skillz: direct:symlink:opencode:code-review
# BEGIN ai.skillz: runtime:code-review-changes
.ai/state/review/
.claude/review_context.md
.claude/review_regression.md
.claude/review_replies/
# 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
.ai/state/commit-msg/
.ai/state/migrations/
.ai/commit-msg/conf.toml
.claude/skills/commit-msg/msgs/
.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:commit-plan
/.claude/skills/commit-plan
# END ai.skillz: direct:symlink:claude:commit-plan
# BEGIN ai.skillz: direct:symlink:opencode:commit-plan
/.opencode/skills/commit-plan
# END ai.skillz: direct:symlink:opencode:commit-plan
# 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: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: direct:symlink:claude:git-mgmt
/.claude/skills/git-mgmt
# END ai.skillz: direct:symlink:claude:git-mgmt
# BEGIN ai.skillz: direct:symlink:opencode:git-mgmt
/.opencode/skills/git-mgmt
# END ai.skillz: direct:symlink:opencode:git-mgmt
# BEGIN ai.skillz: direct:symlink:claude:harness-perf
/.claude/skills/harness-perf
# END ai.skillz: direct:symlink:claude:harness-perf
# BEGIN ai.skillz: direct:symlink:opencode:harness-perf
/.opencode/skills/harness-perf
# END ai.skillz: direct:symlink:opencode:harness-perf
# 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: direct:symlink:claude:opencode-cleaning
/.claude/skills/opencode-cleaning
# END ai.skillz: direct:symlink:claude:opencode-cleaning
# BEGIN ai.skillz: direct:symlink:opencode:opencode-cleaning
/.opencode/skills/opencode-cleaning
# END ai.skillz: direct:symlink:opencode:opencode-cleaning
# BEGIN ai.skillz: direct:symlink:claude:plan-io
/.claude/skills/plan-io
# END ai.skillz: direct:symlink:claude:plan-io
@ -231,6 +278,8 @@ claude_wkts
# END ai.skillz: direct:symlink:opencode:plan-io
# BEGIN ai.skillz: runtime:pr-msg
.ai/state/pr-msg/
.ai/pr-msg/conf.toml
.claude/skills/pr-msg/msgs/
.claude/skills/pr-msg/pr_msg_LATEST.md
# END ai.skillz: runtime:pr-msg
@ -263,14 +312,6 @@ claude_wkts
/.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
@ -291,18 +332,163 @@ claude_wkts
/.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: direct:symlink:opencode:command:run-tests
/.opencode/commands/run-tests.md
# END ai.skillz: direct:symlink:opencode:command:run-tests
# 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:code-review
/.opencode/commands/code-review.md
# END ai.skillz: direct:symlink:opencode:command:code-review
# BEGIN ai.skillz: direct:symlink:opencode:command:code-review-changes
/.opencode/commands/code-review-changes.md
# END ai.skillz: direct:symlink:opencode:command:code-review-changes
# 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:commit-plan
/.opencode/commands/commit-plan.md
# END ai.skillz: direct:symlink:opencode:command:commit-plan
# BEGIN ai.skillz: direct:symlink:opencode:command:opencode-cleaning
/.opencode/commands/opencode-cleaning.md
# END ai.skillz: direct:symlink:opencode:command:opencode-cleaning
# BEGIN ai.skillz: direct:symlink:opencode:command:pr-msg
/.opencode/commands/pr-msg.md
# END ai.skillz: direct:symlink:opencode:command:pr-msg
# BEGIN ai.skillz: direct:symlink:opencode:command:taken-export
/.opencode/commands/taken-export.md
# END ai.skillz: direct:symlink:opencode:command:taken-export
# BEGIN ai.skillz: direct:symlink:claude:code-nav-refs
/.claude/skills/code-nav-refs
# END ai.skillz: direct:symlink:claude:code-nav-refs
# BEGIN ai.skillz: direct:symlink:opencode:code-nav-refs
/.opencode/skills/code-nav-refs
# END ai.skillz: direct:symlink:opencode:code-nav-refs
# BEGIN ai.skillz: direct:symlink:opencode:command:gish
/.opencode/commands/gish.md
# END ai.skillz: direct:symlink:opencode:command:gish
# BEGIN ai.skillz: direct:symlink:opencode:command:resolve-conflicts
/.opencode/commands/resolve-conflicts.md
# END ai.skillz: direct:symlink:opencode:command:resolve-conflicts
# BEGIN ai.skillz: direct:symlink:opencode:command:git-mgmt
/.opencode/commands/git-mgmt.md
# END ai.skillz: direct:symlink:opencode:command:git-mgmt
# BEGIN ai.skillz: direct:symlink:opencode:command:open-wkt
/.opencode/commands/open-wkt.md
# END ai.skillz: direct:symlink:opencode:command:open-wkt
# BEGIN ai.skillz: direct:symlink:opencode:command:close-wkt
/.opencode/commands/close-wkt.md
# END ai.skillz: direct:symlink:opencode:command:close-wkt
# BEGIN ai.skillz: direct:symlink:opencode:command:dep-supersede-scan
/.opencode/commands/dep-supersede-scan.md
# END ai.skillz: direct:symlink:opencode:command:dep-supersede-scan
# BEGIN ai.skillz: direct:symlink:opencode:command:harness-perf
/.opencode/commands/harness-perf.md
# END ai.skillz: direct:symlink:opencode:command:harness-perf
# BEGIN ai.skillz: direct:symlink:opencode:command:yt-url-lookup
/.opencode/commands/yt-url-lookup.md
# END ai.skillz: direct:symlink:opencode:command:yt-url-lookup
# BEGIN ai.skillz: direct:symlink:agents:gish
/.agents/skills/gish
# END ai.skillz: direct:symlink:agents:gish
# BEGIN ai.skillz: direct:symlink:agents:resolve-conflicts
/.agents/skills/resolve-conflicts
# END ai.skillz: direct:symlink:agents:resolve-conflicts
# BEGIN ai.skillz: direct:symlink:agents:git-mgmt
/.agents/skills/git-mgmt
# END ai.skillz: direct:symlink:agents:git-mgmt
# BEGIN ai.skillz: direct:symlink:agents:open-wkt
/.agents/skills/open-wkt
# END ai.skillz: direct:symlink:agents:open-wkt
# BEGIN ai.skillz: direct:symlink:agents:run-tests
/.agents/skills/run-tests
# END ai.skillz: direct:symlink:agents:run-tests
# BEGIN ai.skillz: direct:symlink:agents:close-wkt
/.agents/skills/close-wkt
# END ai.skillz: direct:symlink:agents:close-wkt
# BEGIN ai.skillz: direct:symlink:agents:code-review
/.agents/skills/code-review
# END ai.skillz: direct:symlink:agents:code-review
# BEGIN ai.skillz: direct:symlink:agents:code-nav-refs
/.agents/skills/code-nav-refs
# END ai.skillz: direct:symlink:agents:code-nav-refs
# BEGIN ai.skillz: direct:symlink:agents:code-review-changes
/.agents/skills/code-review-changes
# END ai.skillz: direct:symlink:agents:code-review-changes
# BEGIN ai.skillz: direct:symlink:agents:commit-msg
/.agents/skills/commit-msg
# END ai.skillz: direct:symlink:agents:commit-msg
# BEGIN ai.skillz: direct:symlink:agents:commit-plan
/.agents/skills/commit-plan
# END ai.skillz: direct:symlink:agents:commit-plan
# BEGIN ai.skillz: direct:symlink:agents:dep-supersede-scan
/.agents/skills/dep-supersede-scan
# END ai.skillz: direct:symlink:agents:dep-supersede-scan
# BEGIN ai.skillz: direct:symlink:agents:harness-perf
/.agents/skills/harness-perf
# END ai.skillz: direct:symlink:agents:harness-perf
# BEGIN ai.skillz: direct:symlink:agents:inter-skill-review
/.agents/skills/inter-skill-review
# END ai.skillz: direct:symlink:agents:inter-skill-review
# BEGIN ai.skillz: direct:symlink:agents:opencode-cleaning
/.agents/skills/opencode-cleaning
# END ai.skillz: direct:symlink:agents:opencode-cleaning
# BEGIN ai.skillz: direct:symlink:agents:plan-io
/.agents/skills/plan-io
# END ai.skillz: direct:symlink:agents:plan-io
# BEGIN ai.skillz: direct:symlink:agents:pr-msg
/.agents/skills/pr-msg
# END ai.skillz: direct:symlink:agents:pr-msg
# BEGIN ai.skillz: direct:symlink:agents:prompt-io
/.agents/skills/prompt-io
# END ai.skillz: direct:symlink:agents:prompt-io
# BEGIN ai.skillz: direct:symlink:agents:py-codestyle
/.agents/skills/py-codestyle
# END ai.skillz: direct:symlink:agents:py-codestyle
# BEGIN ai.skillz: direct:symlink:agents:taken-export
/.agents/skills/taken-export
# END ai.skillz: direct:symlink:agents:taken-export
# BEGIN ai.skillz: direct:symlink:agents:yt-url-lookup
/.agents/skills/yt-url-lookup
# END ai.skillz: direct:symlink:agents:yt-url-lookup
# BEGIN ai.skillz: workflow-state
/.ai/state/
/.ai/commit-msg/conf.toml
/.ai/pr-msg/conf.toml
# END ai.skillz: workflow-state

View File

@ -17,21 +17,6 @@ track new integration ideas and proposals in
| Tool | Directory | Status |
|------|-----------|--------|
| [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
@ -47,7 +32,7 @@ ai/
├── README.md # <- you are here
├── claude-code/
│ └── README.md
├── opencode/
├── opencode/ # future
│ └── README.md
└── <your-tool>/
└── README.md
@ -60,7 +45,6 @@ ai/
- Each integration doc should describe **what**
the skill does, **how** to invoke it, and any
**output** artifacts it produces
- Keep docs concise; link to the shared skill source files
- Keep docs concise; link to the actual skill
source files (under `.claude/skills/`, etc.)
rather than duplicating content
- Keep skill bodies harness-neutral; isolate slash-command
syntax and permissions in each harness integration

View File

@ -1,18 +1,15 @@
# Claude Code Integration
[Claude Code](https://github.com/anthropics/claude-code)
integration for piker's shared coding-harness skills.
skills and workflows for piker development.
## Skills
| Skill | Invocable | Description |
|-------|-----------|-------------|
| [`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-slang` | auto | Communication style + slang guide |
| `py-codestyle` | auto | Python source conventions |
| `pyqtgraph-optimization` | auto | Batch rendering patterns |
| `timeseries-optimization` | auto | NumPy/Polars perf patterns |
@ -20,12 +17,8 @@ Skills marked **auto** are background knowledge
applied automatically when Claude detects relevance.
Only `commit-msg` is user-invoked via slash command.
New portable skill source files live under
`.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.
Skill source files live under
`.claude/skills/<skill-name>/SKILL.md`.
---
@ -91,7 +84,8 @@ description context. Examples:
**Footer** (always):
```
(this patch was generated in some part by
`claude-code` using `<model>` (`<provider>`))
[`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
```
### Output Files
@ -100,8 +94,7 @@ After generation, the commit message is written to:
```
.claude/
├── skills/commit-msg/msgs/
│ └── <timestamp>_<hash>_commit_msg.md
├── <timestamp>_<hash>_commit_msg.md # archived
└── git_commit_msg_LATEST.md # latest
```
@ -109,10 +102,18 @@ Where `<timestamp>` is ISO-8601 with seconds and
`<hash>` is the first 7 chars of the current
`HEAD` commit.
Use the latest file with an editor review before commit:
Use the latest file to feed into `git commit`:
```bash
git commit --edit --file .claude/git_commit_msg_LATEST.md
git commit -F .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
@ -127,32 +128,56 @@ Add `MktPair.fqme` property for symbol resolution
Factor `.claude/skills/` into proper subdirs
Deats,
- use portable Agent Skills frontmatter
- keep harness commands as thin delegates
- share supporting references across harnesses
- `commit_msg/` -> `commit-msg/` w/ enhanced
frontmatter
- all background skills set `user-invocable: false`
- content split into supporting files
(this patch was generated in some part by
`claude-code` using `<model>` (`<provider>`))
[`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
```
### Shared Frontmatter
### Frontmatter Reference
Skills use the Agent Skills fields understood by both
Claude Code and OpenCode:
The skill's `SKILL.md` uses these Claude Code
frontmatter fields:
```yaml
---
name: commit-msg
description: >
Generate piker-style git commit messages. Use when...
compatibility: Requires git.
metadata:
author: goodboy
version: "1.0"
Generate piker-style git commit messages...
argument-hint: "[optional-scope-or-description]"
disable-model-invocation: true
allowed-tools:
- Bash(git *)
- Read
- Grep
- Glob
- Write
---
```
Live git context is gathered by the skill at execution
time. Claude-only dynamic interpolation and tool-policy
frontmatter are intentionally excluded so another harness
can execute the same workflow.
| Field | Purpose |
|-------|---------|
| `argument-hint` | Shows hint in autocomplete |
| `disable-model-invocation` | Only user can trigger via `/commit-msg` |
| `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

@ -1,143 +0,0 @@
# 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

@ -1,55 +0,0 @@
# 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

@ -1,23 +0,0 @@
# AI Prompt I/O Log
This directory tracks prompt inputs and model outputs for
AI-assisted development across harnesses.
## Policy
Prompt logging follows the
[NLNet generative AI policy][nlnet-ai]. Substantive contributions
record the original harness, provider/model when known, timestamps,
the prompting sequence, and an unedited `.raw.md` partner.
[nlnet-ai]: https://nlnet.nl/foundation/policies/generativeAI/
## Usage
Entries are created by the `prompt-io` skill or its commit-message
integration. Harness-specific records live in their corresponding
subdirectories. Code records can reference the associated Git diff
instead of duplicating source code.
Human contributors retain responsibility for review and acceptance.
AI-generated content is not presented as human-authored work.

View File

@ -1,59 +0,0 @@
---
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

@ -1,46 +0,0 @@
---
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

@ -1,53 +0,0 @@
---
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

@ -1,56 +0,0 @@
---
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

@ -1,54 +0,0 @@
---
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

@ -1,46 +0,0 @@
---
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

@ -1,47 +0,0 @@
---
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

@ -1,60 +0,0 @@
---
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

@ -1,47 +0,0 @@
---
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

@ -1,43 +0,0 @@
---
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

@ -1,48 +0,0 @@
---
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

@ -1,54 +0,0 @@
---
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

@ -1,43 +0,0 @@
---
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

@ -1,41 +0,0 @@
---
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

@ -1,66 +0,0 @@
---
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

@ -1,51 +0,0 @@
---
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

@ -1,60 +0,0 @@
---
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

@ -1,57 +0,0 @@
---
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

@ -1,34 +0,0 @@
# 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

@ -1,36 +0,0 @@
---
model: gpt-6 (exact variant not exposed)
service: codex
session: fsp-session-recovery-takeover-20260915
timestamp: 2026-09-15T15:52:29Z
git_ref: fadab3d2
scope: code
substantive: true
raw_file: 20260915T155229Z_fadab3d2_prompt_io.raw.md
---
## Prompt
Recover the interrupted OpenCode FSP session and take over its ongoing
work. The recovered child instruction records human authorization for
the minimal EMA omitted-default fix followed by a complete commit plan.
## Response summary
Removed the eager Numba signature that rejected Python's omitted
default arguments. Preserved nopython/nogil compilation and numerical
behavior. Added nine numerical regressions and verified all 38 focused
FSP cases. Rebuilt planning artifacts separately from production code.
## Files changed
- `piker/fsp/_momo.py`: drop the eager signature and unused type imports.
- `tests/test_fsp_momo.py`: omitted/explicit argument and seed coverage.
## Human edits
The human identified the omitted-default limitation in the earlier
session, authorized the minimal lazy-specialization approach, and
requested this takeover after OpenCode failed. Existing source changes
and older OpenCode provenance were retained. The new fix remains
unstaged and awaits human review; no acceptance is inferred.

View File

@ -1,50 +0,0 @@
---
model: gpt-6 (exact variant not exposed)
service: codex
timestamp: 2026-09-15T15:52:29Z
git_ref: fadab3d2
diff_cmd: git diff HEAD~1..HEAD
---
## Prompt
> i'm thinking maybe you can just take over that work and we can
> abandon that oc dialog for now?
Recovered child-session instructions describe the earlier human
authorization to remove EMA's eager Numba signature while retaining
`jit(nopython=True, nogil=True)`, verify defaults, and finish the commit
plan without staging or committing. This is recovered context, not a
verbatim quote of the earlier human authorization.
## Response
> `git diff HEAD~1..HEAD -- piker/fsp/_momo.py`
Removed EMA's eager Numba signature and now-unused Numba type imports.
Retained nopython/nogil compilation, the numerical kernel, historical
comments, and pre-existing staged and unstaged edits.
> `git diff HEAD~1..HEAD -- tests/test_fsp_momo.py`
Added production-dispatcher regressions for omitted positional and
keyword arguments, explicit None and numeric parameters, and the
previously corrected one-sample continuation. Expected outputs are
hand-computed recurrences, rather than the Python implementation.
Before the fix, the first case failed with:
```text
TypeError: No matching definition for argument type(s)
array(float64, 1d, C), omitted(default=None), omitted(default=None)
```
After the fix, the focused run outside the socket-restricted sandbox
reported:
```text
38 passed in 0.66s
```
Ruff passed for the modified momentum module and new regression file.
Combined-worktree results do not attest to isolated commit boundaries.

View File

@ -1,64 +0,0 @@
---
model: gpt-6 (exact variant not exposed)
service: codex
timestamp: 2026-09-23T16:46:56.830897+00:00
git_ref: efa72980
session: 01a0a5b9-f872-7ca3-ab8a-d1cffc14e895
scope: code
substantive: true
raw_file: 20260923T164656Z_efa72980_prompt_io.raw.md
---
## Prompt
Investigate existing branch fixes before repairing IB day-skipping.
Create an isolated worktree on ib_connectivity_harden and add a test
that checks actual persisted data, leaving live gap repair for later.
## Response summary
Preserve IB request boundaries on HMDS no-data responses. Retain the
existing earliest-history classification and bounded reset handling.
Separate requested and published boundaries during backfill so the
first response after an empty interval does not lose its end bar.
Prove supplied bars survive both SHM publication and a fresh parquet
load while a simulated venue closure remains empty.
## Files changed
- `piker/brokers/ib/feed.py`: complete HMDS no-data requests unchanged.
- `piker/tsp/_history.py`: filter overlap against published history.
- `tests/test_ib_history.py`: six empty-response boundary cases.
- `tests/test_history_backfill.py`: two SHM/parquet integration cases.
## Human edits
The human identified the live gap, required prior-branch discovery,
selected ib_connectivity_harden as the base, and requested stronger
end-to-end evidence. They corrected overstated reproduction claims,
insisted on plain explanations, and pointed out that valid venue
closures must remain gaps. Existing data repair was explicitly deferred.
No direct human source edits have been observed; changes await review.
## Review follow-up
The human's tuicr review asked whether None was part of the backfill
protocol and requested plain explanations of the two dates and overlap
filter. Clarified the IB-internal empty-response sentinel and its
translation into NoData/DataUnavailable before reaching the backfiller.
Renamed last_start_dt to request_end_dt and published_start_dt to
oldest_shm_dt. Added a concrete empty-interval example at the filter and
explained that only successful SHM writes move oldest_shm_dt.
These changes clarify naming and documentation without changing the
previously tested request or publication behavior.
## History-filter review follow-up
The human flagged the unused append_until_dt argument and confusing
append/prepend terminology in diff_history(). The helper had one
caller, using only prepend_until_dt. Removed the helper and inlined
its inclusive lower-time cutoff beside the SHM duplicate filter.
The human's nearby TODO comment is resolved by this removal; the
replacement comment describes the retained timestamps directly.
The storage comparison remains >= backfill_until_dt; the SHM
comparison remains < oldest_shm_dt.

View File

@ -1,87 +0,0 @@
---
model: gpt-6 (exact variant not exposed)
service: codex
timestamp: 2026-09-23T16:46:56.830897+00:00
git_ref: efa72980
diff_cmd: git diff efa72980 -- piker/brokers/ib/feed.py piker/tsp/_history.py tests/test_ib_history.py tests/test_history_backfill.py
---
## Prompt
The human requested the IB request-boundary repair in a new worktree
based on ib_connectivity_harden and a test exercising real persistence.
The existing MNQ parquet gap is outside this repair scope.
## Code output
> `git diff efa72980 -- piker/brokers/ib/feed.py`
> `git diff efa72980 -- piker/tsp/_history.py`
> `git diff efa72980 -- tests/test_ib_history.py`
> `git diff efa72980 -- tests/test_history_backfill.py`
get_bars() now completes HMDS no-data responses without changing the
requested date. open_history_client() retains its NoData and earliest
history classification. start_backfill() tracks the published boundary
separately from the request cursor after empty intervals.
## Unedited progress output
The new test failed on the original code as intended: the
temporary parquet contained only 4 bars instead of 2,004.
The day-skip caused 2,000 available bars to be omitted.
The test also includes a simulated venue closure that must remain
a gap. It checks that we fetch available bars without inventing
data for an empty interval.
The IB fix now preserves all 2,004 bars in parquet. The SHM
assertion exposed a second bug: after stepping over an empty
interval, the backfiller drops the last bar of the next response
as though it were already published.
I’ll separate the request date from the published-data boundary
so the same test can verify that both memory and disk retain
every available bar.
## Validation
Using the existing fsp_backfill_sync/py313 interpreter, with cwd in
ib_history_request_bounds and the Piker import checked against that cwd:
- tests/test_history_backfill.py: 11 passed in 1.92s.
- tests/test_ib_history.py: 17 passed in 0.36s.
- Ruff on all four changed Python files: all checks passed.
- git diff --check: passed.
The integration test uses simulated IB bars responses, real IB history
adapter and backfill logic, real SHM, and temporary NativeDB parquet.
It checks a fresh disk load. It does not test a live gateway, actor RPC,
or Qt rendering, and does not prove the origin of the user's live gap.
## Unedited review response
`None` is internal to the IB adapter. The backfiller receives
`NoData`, or `DataUnavailable` when the requested date has reached
the known beginning of history. I’ll make that distinction explicit.
The two dates answer different questions: “where should the next
history query end?” and “what is the oldest bar already in SHM?”
An empty reply changes only the first. I’ll rename them to
`request_end_dt` and `oldest_shm_dt` and explain the filtering with
a concrete example.
> `git diff efa72980 -- piker/brokers/ib/feed.py piker/tsp/_history.py`
## History-filter review follow-up
The human flagged the unused append_until_dt argument and confusing
append/prepend terminology in diff_history(). The helper had one
caller, using only prepend_until_dt. Removed the helper and inlined
its inclusive lower-time cutoff beside the SHM duplicate filter.
The human's nearby TODO comment is resolved by this removal; the
replacement comment describes the retained timestamps directly.
The storage comparison remains >= backfill_until_dt; the SHM
comparison remains < oldest_shm_dt.

View File

@ -1,51 +0,0 @@
---
model: gpt-6 (exact variant not exposed)
service: codex
timestamp: 2026-09-24T16:57:57.342784+00:00
git_ref: 1da9b760
scope: code
substantive: true
raw_file: 20260924T165757Z_1da9b760_prompt_io.raw.md
---
## Prompt
Document and test the existing null-segment repair, including its
flat-bar fallback, while distinguishing packed timestamp gaps.
## Response summary
Documented SHM-only repair, provider placement assumptions, fallback
fields, notifications, and concurrency limits. Added boundary and
no-history coverage and fixed epoch-dependent comparison tolerance
which could synthesize a duplicate timestamp at the right margin.
## Files changed
- piker/tsp/_history.py: helper contract and absolute tolerance.
- tests/test_history_backfill.py: four additional parameter/case runs.
## Human edits
The human identified cancellation as a source of stored gaps,
correctly described packed indexes and insertion costs, pointed to
maybe_fill_null_segments(), and requested immediate documentation
and tests. No direct source edits observed during this change;
the patch remains uncommitted for review.
## Close-copy factoring follow-up
The human requested stronger internal types, a reusable close-copy
helper, searchable comments for copying close and synthesizing
stamps, and research into historical venue closure queries.
Extracted _copy_close_to_null_rows(), preserving non-OHLC fields and
rejecting occupied slots before mutation. Added explicit callback,
array, segment-index and date annotations. Preserved the existing
provenance TODO and suggested avoiding volume sentinels.
Four helper cases cover successful fill and unchanged rejected
inputs. Backfill suite: 19 passed in 2.01s; Ruff and diff check pass.
IB documents historical SCHEDULE requests separately from contract
hours. The installed ib_async exposes reqHistoricalScheduleAsync.
No live schedule request or venue behavior change was performed.
Source: https://interactivebrokers.github.io/tws-api/historical_bars.html

View File

@ -1,48 +0,0 @@
---
model: gpt-6 (exact variant not exposed)
service: codex
timestamp: 2026-09-24T16:57:57.342784+00:00
git_ref: 1da9b760
diff_cmd: git diff 1da9b760 -- piker/tsp/_history.py tests/test_history_backfill.py
---
## Prompt
The human requested documentation and tests for
maybe_fill_null_segments(), identifying its flat OHLC fallback
and asking how packed SHM could accept later recovered bars.
## Code output
> `git diff 1da9b760 -- piker/tsp/_history.py`
> `git diff 1da9b760 -- tests/test_history_backfill.py`
Document the existing slot-based repair and its limitations.
Extend fallback coverage to NoData and DataUnavailable; verify
the observed packed MNQ gap remains unchanged. Reproduce the
relative-tolerance failure at a modern epoch, then disable relative
tolerance in the synthetic timestamp boundary check.
## Validation
Before the fix: modern-epoch boundary case failed, zero-epoch
case passed. The overfull synthetic run duplicated the right bar.
After the fix: 15 backfill tests passed in 1.99s.
## Close-copy factoring follow-up
The human requested stronger internal types, a reusable close-copy
helper, searchable comments for copying close and synthesizing
stamps, and research into historical venue closure queries.
Extracted _copy_close_to_null_rows(), preserving non-OHLC fields and
rejecting occupied slots before mutation. Added explicit callback,
array, segment-index and date annotations. Preserved the existing
provenance TODO and suggested avoiding volume sentinels.
Four helper cases cover successful fill and unchanged rejected
inputs. Backfill suite: 19 passed in 2.01s; Ruff and diff check pass.
IB documents historical SCHEDULE requests separately from contract
hours. The installed ib_async exposes reqHistoricalScheduleAsync.
No live schedule request or venue behavior change was performed.
Source: https://interactivebrokers.github.io/tws-api/historical_bars.html

View File

@ -1,91 +0,0 @@
---
model: gpt-6 (exact variant not exposed)
service: codex
timestamp: 2026-09-24T18:54:56.893490+00:00
git_ref: 1da9b760
scope: code
substantive: true
raw_file: 20260924T185456Z_1da9b760_schedule_prompt_io.raw.md
---
## Prompt
The human requested: first freeze a commit-plan for the existing
close-copy/null-repair changes, then implement ib_async historical
schedule support with tests, use it for datad.ib history-buffer gap
spacing, and plan a common on-demand segment interface for CLI and
chart selection. Avoid reindexing in this first pass and record
fallback insertion only as a TODO.
## Response summary
Implemented historical schedule normalization, bounded startup
reservations, explicit actor-local synthetic ranges, and two read
RPC functions. Added deterministic adapter tests and real SHM/parquet
startup coverage. Wrote the segment-backfill interface plan.
This entry covers the new schedule work only. The earlier frozen
null-repair plan has its own provenance. The shared _history.py file
contains changes from both scopes until the earlier commit is made.
## Human edits
The human identified cancellation during backfill as the reproducible
trigger, explained the packed-index insertion tradeoff, requested
reuse of venue closure knowledge, and chose placeholders to avoid
expensive rewrites. They required explicit placeholder annotation,
proposed later graphics compression, and asked to defer insertion
fallbacks. They also requested the existing null-repair commit plan
before the new schedule work. These are human-directed architecture
and scope choices; no acceptance or task completion state is inferred.
## Validation and limits
55 targeted tests passed; Ruff and diff checks passed. No live IB
qualification was performed. Reservations are synthetic, not recovered
observations. Repair execution, persistence of unfinished requests,
chart compression, FSP handling, export filtering, and request-level
IB cancellation remain follow-up work documented in the plan.
## Files changed
- `piker/brokers/ib/venues.py`
- `piker/brokers/ib/api.py`
- `piker/brokers/ib/feed.py`
- `piker/tsp/_reserve.py`
- `piker/tsp/_history.py`
- `tests/providers/test_ib_schedule.py`
- `tests/providers/test_history_reservations.py`
- `docs/history_segment_backfill.rst`
## Pre-commit module review
The human requested placing historical schedule normalization in the
existing venues.py module, naming the allocation module _reserve.py,
and adding the missing project license headers. Applied those moves,
updated imports and test patch targets, and added the standard AGPL
headers to the new allocation module and both new test modules.
## Local tuicr review follow-up
The human supplied eleven comments requesting a configurable newest
gap count, caller and metadata references, explanations of guards
and provider-specific limits, multiline boolean style, capacity
fallback rationale, an explanation of numpy split boundaries, and
clearer test cases, fixture types, and module docstrings.
Added newest_gaps=32, with zero disabling queries and negative values
rejected. Documented tsdb_backfill ownership and the local/RPC range
accessors. Explained recoverable overflow and half-open synthetic
range grouping. Typed tmp_path as pathlib.Path and monkeypatch as
pytest.MonkeyPatch. Added four request-budget cases.
Verification: reservation tests 11 passed; schedule tests 7 passed;
Ruff and git diff --check passed. Changes remain unstaged for review.
No forge review or CI state was queried; this was local feedback.
## Provider-test layout review
The human requested grouping both new history/schedule test modules
under tests/providers. Moved both files without changing test logic;
they retain the shared tests/conftest.py fixtures.

View File

@ -1,100 +0,0 @@
---
model: gpt-6 (exact variant not exposed)
service: codex
timestamp: 2026-09-24T18:54:56.893490+00:00
git_ref: 1da9b760
diff_cmd: git diff HEAD~1..HEAD
---
> `git diff HEAD~1..HEAD -- piker/brokers/ib/_schedule.py`
> `git diff HEAD~1..HEAD -- piker/brokers/ib/api.py`
> `git diff HEAD~1..HEAD -- piker/brokers/ib/feed.py`
> `git diff HEAD~1..HEAD -- piker/tsp/_reservations.py`
> `git diff HEAD~1..HEAD -- piker/tsp/_history.py`
> `git diff HEAD~1..HEAD -- tests/test_ib_schedule.py`
> `git diff HEAD~1..HEAD -- tests/test_history_reservations.py`
> `git diff HEAD~1..HEAD -- docs/history_segment_backfill.rst`
## Generated output
Added Client.history_schedule() over the installed ib_async API,
normalizing dated sessions to UTC with explicit response coverage.
Reject ambiguous DST times, reversed sessions, and overlapping
sessions. Match the existing extended-hours bars policy.
Added startup-only schedule reservations to tsdb_backfill before
prepending older stored history. Reserve open-session sample slots
only where the reply covers the full candidate gap. Return synthetic
UTC ranges separately from OHLCV; copy preceding close into flat
placeholders without writing those placeholders to parquet.
Unknown coverage and capacity failures retain packed time gaps.
Added datad.ib schedule and reservation metadata RPC functions and
a proposed shared CLI/chart segment request protocol. The repair
queue, durable unresolved intervals, chart compression, FSP handling,
and coordinated insertion remain deferred. No live data was repaired.
Verification is complete: 55 targeted tests pass, including the
new schedule and real SHM/parquet tests. I also added checks for
reversed session bounds and the 25-hour day at the autumn DST
change.
The segment API plan uses UTC ranges for both CLI and chart
requests, a provider-owned queue, durable unfinished intervals,
and replacement of reserved slots without reindexing. Live IB
schedule coverage still needs qualification.
## Limits
Only stored-history gaps are reserved before prepend; published rows
are never shifted. Newest 32 gaps, at most 31 days each, are queried
serially with a 10-second schedule timeout. This can delay history
publication. Underlying IB request cancellation is still a TODO.
Manual whole-SHM export requires synthetic-range exclusion before
it can preserve provider-only storage. The chart currently displays
flat placeholders, and synthetic-aware FSP treatment is deferred.
Tests: history backfill 19, IB history 17, method proxy 5, schedule 7,
reservations 7. Sandbox local-socket failures were rerun with approval.
Ruff and git diff --check passed. No live broker requests were made.
## Module-layout review response
Schedule normalization now lives in piker.brokers.ib.venues beside
the existing session helpers. The allocation module is tsp._reserve.
Added the project AGPL headers to the new module and test files.
Earlier diff pointers record the original generated paths; use
the following paths for the reviewed layout:
> `git diff HEAD~1..HEAD -- piker/brokers/ib/venues.py`
> `git diff HEAD~1..HEAD -- piker/tsp/_reserve.py`
## Local review response
The fixes pass all 18 tests in the two affected files.
The new limit tests cover zero, negative, one-gap, and two-gap
budgets, including newest-first selection.
Capacity overflow remains a warning: no partial reservation is
published for that gap, and the original observations remain
available. The comments now explain that fallback explicitly.
Diff pointers above include the review edits to tsp._reserve, both
test modules, and the interface document. This review did not query
a forge or publish replies.
## Provider-test paths
Moved both new test modules under tests/providers as requested.
> `git diff HEAD~1..HEAD -- tests/providers/test_ib_schedule.py`
> `git diff HEAD~1..HEAD -- tests/providers/test_history_reservations.py`

View File

@ -1,45 +0,0 @@
---
model: gpt-6
service: codex
timestamp: 2026-09-24T23:24:05.525363+00:00
git_ref: 441358e2
scope: code
substantive: true
raw_file: 20260924T232405Z_441358e2_schedule_coverage_prompt_io.raw.md
---
## Prompt
User: "do it", approving the proposed bounded wider-window retry
for incomplete schedule coverage and its MNQ regression test.
Previous live qualification showed a one-day request began after
the gap's left edge; a wider query covered it and excluded the
one-hour venue closure.
## Response summary
Added bounded backwards widening under one timeout, preserving actual
coverage and fixed end bounds. Added captured-response allocation,
unknown-coverage, and retry-timeout tests, and updated API documentation.
39 selected tests and Ruff passed. Live data was not changed.
## Files changed
- piker/brokers/ib/api.py — bounded schedule coverage retries.
- tests/providers/test_ib_schedule.py — captured MNQ and retry regressions.
- docs/history_segment_backfill.rst — retry contract and limits.
## Human edits
The user established schedule-driven allocation, explicit unknown
coverage, and avoiding reindexing as the design constraints, supplied
the MNQ gap, and authorized this focused coverage fix. No direct human
source edits occurred during this patch; review is pending.
## Follow-up review
The user requested factoring and documenting the repeated 86400
conversion. Added piker.calc.SECONDS_PER_DAY, documented elapsed
versus calendar/session days once, and reused it in the IB query
and reservation span check. Named the client retry limit locally.
Schedule tests: 11 passed; Ruff passed after this adjustment.

View File

@ -1,66 +0,0 @@
---
model: gpt-6
service: codex
timestamp: 2026-09-24T23:24:05.525363+00:00
git_ref: 441358e2
diff_cmd: git diff HEAD~1..HEAD
---
> `git diff HEAD~1..HEAD -- piker/brokers/ib/api.py`
> `git diff HEAD~1..HEAD -- tests/providers/test_ib_schedule.py`
> `git diff HEAD~1..HEAD -- docs/history_segment_backfill.rst`
Client.history_schedule() widens incomplete left coverage by doubling
numDays up to 31 while preserving endDateTime and actual returned
coverage. All attempts share an asyncio timeout. Missing right coverage
returns unchanged because extending the lookback cannot repair it.
Tests replay the captured MNQ schedules through the real client and
reservation allocator, check bounded incomplete coverage and retry
cancellation under one timeout. Documentation describes these limits.
## User-facing output
The captured MNQ case now passes through the real client method
and allocator: the request widens from one day to two, then
reserves 51,824 slots while leaving the venue closure compressed.
The tests also cover permanently incomplete replies and a timeout
during the retry. I’m running the related regression suites now.
All 39 selected tests passed, and Ruff passed. The reservation
suite needed a retry outside the sandbox because Trio’s local
wakeup socket was blocked.
The fix and regression tests are ready for review. I’m recording
the prompt provenance; live SHM and parquet remain unchanged.
## Validation commands and results
Worktree-local py313 in the existing Nix environment; import piker
resolved inside this worktree.
- py313/bin/python -m pytest -p no:xonsh -q -x --tb=short --no-header tests/providers/test_ib_schedule.py tests/providers/test_history_reservations.py
Sandbox: 11 passed then PermissionError creating Trio wakeup socket.
Escalated rerun: 22 passed in 1.02s; fake IB, disposable test SHM.
- py313/bin/python -m pytest -p no:xonsh -q -x --tb=short --no-header tests/test_ib_history.py
17 passed in 0.69s.
- ruff check piker/brokers/ib/api.py tests/providers/test_ib_schedule.py
All checks passed (existing deprecated configuration warning).
- git diff --check: passed.
No live service restart, live history repair, staging, or commit.
## Follow-up user review and generated changes
User: "feels like this same magic # from the prior commit should be
factored and commented once? `num_days: int = max(1, ceil(seconds /
86400))` other then that all looks decent"
> `git diff HEAD~1..HEAD -- piker/calc.py piker/tsp/_reserve.py`
Added SECONDS_PER_DAY for both consumers, with one comment explaining
fixed elapsed days versus calendar days and trading sessions. Named
the client's 31-day retry limit locally. After this change the
schedule suite passed 11 tests and Ruff passed all four Python files.

View File

@ -1,118 +0,0 @@
---
created_at: 2026-10-05T04:11:32.732262+00:00
generated_by:
harness: codex
provider: OpenAI
git_ref: eed58566
scope: code
substantive: true
raw_file: 20261005T041132Z_eed58566_trend_b4fe4d_prompt_io.raw.md
---
## Prompt
The user requested an autonomous search of GitHub issues and local
notes, code, documentation and comments for Trend Calculus research,
then a rigorous Piker-native implementation in the FSP subsystem.
They asked to prepare multiple high-frequency timeframe regimes for
the JEV System-1 provider service, with work bounded by 00:18 Eastern.
They authorized same-caliber subagents and requested, as a bonus,
multiple-timeframe reversal markers and live rectangle annotations.
The user supplied these implementation resources:
- https://docs.typesafe.ai/models
- https://docs.typesafe.ai/sdk/python
- https://docs.typesafe.ai/agent-skill
They offered a Pine implementation but had not supplied it. They
said the API key was still being set up and instructed us to continue
offline. They clarified the workspace concern, disabled fast mode,
and supplied repository rules: preserve human-owned task states,
no autonomous commits or pushes, 69-column prose, Xonsh snippets,
and the Python style skill.
## Response summary
Implemented a bounded Numba recurrence and a four-order native FSP
with provisional-tail isolation, skipped-step recovery, indexed
publication, quiet sampler wakeups, sparse events and history replay.
Added complete aligned multiple-timeframe aggregation, immutable
provenance-rich snapshots, bounded HTTPX/Trio JEV requests and a
paced worker that rejects stale or invalidated results. Added native
feed/service wiring and opt-in Qt markers and regime rectangles,
including actual 1/5/15-second layers and model receipt/expiry rules.
Research identifies pinned Lua/Scala sources, issue discussions,
local notes and the manuscript, with explicit semantic differences.
Original Lua fixtures and an independent materialized-window oracle
check 21,870 exhaustive histories, 48 random recursive streams and
62 original Lua reversals. No complete Pine source was recovered.
The TypeSafe agent skill was installed and applied. No live API key
was read, no model request was sent, no broker session was opened,
and no Git history or human-owned task state was changed.
## Verification
The combined targeted suite passed 252 tests, with one unrelated
remote gap-dialog actor case deselected. The one warning is an
existing Tractor dependency's deprecated Trio Event truth test.
The new SHM test includes an actual separate read-only child actor
after in-place replay. This does not prove concurrent cross-process
write atomicity. Ruff, diff whitespace and 69-column checks passed.
The combined suite covered test_trend, test_trend_reference,
test_trend_shm, test_trend_regimes, test_jev, test_regime_feed,
test_trend_graphics, test_trend_mtf_graphics, test_fsp_sync,
test_fsp_momo and test_gap_overlays. The standard py313 environment
and pytest with the xonsh plugin disabled were used. Socket/SHM
execution required approved sandbox escalation.
The synthetic offscreen chart preview was generated and inspected.
The reproducible benchmark measured roughly 8.586 million batch
rows/second and 6.963 microseconds median scalar update on this host;
these exclude rendering, transport and provider inference. Native
subsecond sampling remains unsupported by the existing clock.
Live provider quality and the user's Pine parity remain unverified.
## Files changed
- `piker/fsp/__init__.py`
- `piker/fsp/_api.py`
- `piker/fsp/_engine.py`
- `piker/ui/_display.py`
- `piker/ui/_fsp.py`
- `piker/fsp/_trend.py`
- `piker/fsp/_trend_fsp.py`
- `piker/fsp/_regime.py`
- `piker/fsp/_jev.py`
- `piker/fsp/_regime_feed.py`
- `piker/ui/_trend.py`
- `piker/ui/_trend_mtf.py`
- `tests/test_trend.py`
- `tests/test_trend_shm.py`
- `tests/test_trend_reference.py`
- `tests/test_trend_regimes.py`
- `tests/test_jev.py`
- `tests/test_regime_feed.py`
- `tests/test_trend_graphics.py`
- `tests/test_trend_mtf_graphics.py`
- `tests/_inputs/trend_calculus_lua.json`
- `examples/benchmark_trend.py`
- `examples/trend_regimes.py`
- `examples/trend_graphics.py`
- `docs/trend_calculus.rst`
- `ai/prompt-io/README.md`
## Human edits
The human supplied the objective, deadline, research resources,
provider choice, optional skill installation, graphics extension,
parallel-agent authorization and offline credential constraint.
Those directions materially determined the implementation and scope.
No direct manual source edits were observed during this work. The
patch is pending human review; acceptance is not inferred. Research,
implementation, review and tests were performed by the root Codex
agent with three same-caliber delegated agents. Existing unrelated
working-tree artifacts were preserved.

View File

@ -1,106 +0,0 @@
---
created_at: 2026-10-05T04:11:32.732262+00:00
generated_by:
harness: codex
provider: OpenAI
git_ref: eed58566
diff_cmd: git diff eed58566 --
---
# Captured output and code references
This record captures selected unedited progress responses and the
code artifact pointers before human source edits. It is not a full
transcript. Exact model and session identifiers were unavailable.
The working branch is trend_calc_on_jev; no commit was authorized.
While files remain untracked, use the no-index references below.
After inclusion in branch history, use the base-to-branch diff.
## Unedited progress excerpts
The cross-process shared-memory test passes, including replay
after an in-place history repair. The JEV feed also preserves
the shared quote policy and catches up cleanly when new bars
arrive during a history copy.
I’m running the combined checks and reviewing the chart preview.
Live JEV verification remains pending your key setup.
The combined offline suite passes: 252 tests, including the
independent algorithm oracle, JEV mock transport, Qt overlays,
existing FSP checks, and the cross-process SHM test.
The chart preview shows separate 1-, 5-, and 15-second reversal
markers and regime shading. I’m checking the final diff and
recording provenance; the work remains uncommitted.
## Unedited verification output
```text
252 passed, 1 deselected, 1 warning in 3.79s
All checks passed!
```
## Code artifact pointers
> `git diff eed58566 -- piker/fsp/__init__.py`
> `git diff eed58566 -- piker/fsp/_api.py`
> `git diff eed58566 -- piker/fsp/_engine.py`
> `git diff eed58566 -- piker/ui/_display.py`
> `git diff eed58566 -- piker/ui/_fsp.py`
> `git diff --no-index /dev/null piker/fsp/_trend.py`
> `git diff --no-index /dev/null piker/fsp/_trend_fsp.py`
> `git diff --no-index /dev/null piker/fsp/_regime.py`
> `git diff --no-index /dev/null piker/fsp/_jev.py`
> `git diff --no-index /dev/null piker/fsp/_regime_feed.py`
> `git diff --no-index /dev/null piker/ui/_trend.py`
> `git diff --no-index /dev/null piker/ui/_trend_mtf.py`
> `git diff --no-index /dev/null tests/test_trend.py`
> `git diff --no-index /dev/null tests/test_trend_shm.py`
> `git diff --no-index /dev/null tests/test_trend_reference.py`
> `git diff --no-index /dev/null tests/test_trend_regimes.py`
> `git diff --no-index /dev/null tests/test_jev.py`
> `git diff --no-index /dev/null tests/test_regime_feed.py`
> `git diff --no-index /dev/null tests/test_trend_graphics.py`
> `git diff --no-index /dev/null tests/test_trend_mtf_graphics.py`
> `git diff --no-index /dev/null tests/_inputs/trend_calculus_lua.json`
> `git diff --no-index /dev/null examples/benchmark_trend.py`
> `git diff --no-index /dev/null examples/trend_regimes.py`
> `git diff --no-index /dev/null examples/trend_graphics.py`
> `git diff --no-index /dev/null docs/trend_calculus.rst`
> `git diff --no-index /dev/null ai/prompt-io/README.md`
The implementation adds the bounded recursive TrendCalculator,
causal history/preview FSP publication, explicit indexed FspSample
updates, coalesced quiet-step wakeups and sparse-event clearing.
MultiTimeframeTrend forms complete aligned buckets; the Jev worker
uses bounded evidence, single-flight requests and freshness checks.
Qt layers render confirmed pivots and bounded regime rectangles.
Independent reference, mock transport and shared-memory tests cover
mathematical and integration behavior. The docs preserve sources,
semantic differences, measured performance and remaining limits.

View File

@ -1,42 +0,0 @@
---
created_at: 2026-10-05T05:02:18.111444+00:00
generated_by:
harness: codex
provider: OpenAI
git_ref: eed58566
scope: code
substantive: true
raw_file: 20261005T050218Z_eed58566_trend_sampler_5d9102_prompt_io.raw.md
---
## Prompt
Continuation of the user's autonomous, deadline-bounded request for
rigorous native Trend Calculus, live regime service preparation and
graphics integration. This supplements the earlier trend record.
## Response summary
Final delegated integration review reproduced duplicate timestamps
in the existing native sampler: seeded [1000,1001] became
[1000,1001,1001], causing the causal calculator to reject the source.
Added a narrow guard for native periods at or below one second and
a controlled-clock regression covering repeated, backward and newer
epochs. Wakeup broadcasts remain unchanged. No refactor or native
subsecond support is claimed. The root reviewed the change and ran
the complete targeted suite: 253 passed, one unrelated remote gap
case deselected, one existing Tractor/Trio deprecation warning.
## Files changed
- `piker/data/_sampling.py`
- `tests/test_trend_sampling.py`
- `docs/trend_calculus.rst`
## Human edits
The original human requirement for reliable native integration
motivated this correction. No additional direct human source edit
was observed. The root and delegated Codex agent implemented and
reviewed it under that scope. Human review and acceptance remain
pending; no commits, broker sessions or model requests were made.

View File

@ -1,38 +0,0 @@
---
created_at: 2026-10-05T05:02:18.111444+00:00
generated_by:
harness: codex
provider: OpenAI
git_ref: eed58566
diff_cmd: git diff eed58566 --
---
# Final integration correction
## Unedited progress output
The sampler fix is verified. The final combined suite passes
253 tests, including repeated and backward clock cases.
Implementation and tests are now frozen; I’m saving the review
patch and final provenance record.
## Unedited verification result
```text
253 passed, 1 deselected, 1 warning in 3.86s
```
## Code pointers
> `git diff eed58566 -- piker/data/_sampling.py`
> `git diff --no-index /dev/null tests/test_trend_sampling.py`
> `git diff --no-index /dev/null docs/trend_calculus.rst`
The actual Sampler.increment_ohlc_buffer could append a duplicate
rounded epoch after realtime startup, or when the clock repeated
or moved backward. The fix retains the existing mutable bar until
a newer epoch exists, preserving broadcasts. The regression drives
the real sampler with Trio MockClock and verifies that its complete
output remains valid input for the strict causal calculator.

View File

@ -1,79 +0,0 @@
---
created_at: 2026-10-05T05:40:14.294712+00:00
generated_by:
harness: codex
provider: OpenAI
git_ref: eed58566
scope: code
substantive: true
raw_file: 20261005T054014Z_eed58566_trend_style_e3216f_prompt_io.raw.md
---
## Prompt
The human explicitly requested a complete py-codestyle pass and
proper license headers for every new code module after identifying
that the previous pass was incomplete. They retained autonomous
work and parallel-agent authorization, offline provider constraints,
and human ownership of task states and commits.
## Response summary
Read the repository-deployed py-codestyle skill and shared runtime
review lessons. Root and three agents audited all changed Python
modules, tests and runnable examples. Added complete project AGPL
notices and SPDX identifiers to all 19 new modules, preserving the
Morgan/ByteSumo algorithm attribution. Corrected annotations,
helper contracts, expression layout, named interpolation operands,
and owner-qualified regression documentation. Used narrow F842
suppressions where Ruff cannot connect declarations to isolated
comprehension scopes.
## Verification
Ruff passes across all changed Python modules. All new source and
added old-file lines meet the 69-column rule with no whitespace-only
lines. Every new function and class has the required multiline
contract; function parameters and returns are annotated. The final
offline suite reports 253 passed, one unrelated actor case
excluded, and one existing Tractor deprecation warning. Native SHM
publication, cross-process replay visibility, quiet sampler steps,
Numba reference parity, Qt overlays and mocked Jev protocols pass.
No live model quality, separate provider-service wiring, Pine parity
or cross-process write atomicity is claimed.
## Human edits and follow-ups
The human's style correction materially drove these changes.
No direct manual source edits were observed; acceptance is pending.
The human deferred SDK AnyIO/Trio work until the HTTP prototype is
validated and requested a later Taken-store handoff. Existing task
states, unrelated artifacts, Git index and Git history are preserved.
## Files changed
- `piker/fsp/__init__.py`
- `piker/fsp/_api.py`
- `piker/fsp/_engine.py`
- `piker/ui/_display.py`
- `piker/ui/_fsp.py`
- `piker/data/_sampling.py`
- `piker/fsp/_trend.py`
- `piker/fsp/_trend_fsp.py`
- `piker/fsp/_regime.py`
- `piker/fsp/_jev.py`
- `piker/fsp/_regime_feed.py`
- `piker/ui/_trend.py`
- `piker/ui/_trend_mtf.py`
- `tests/test_trend.py`
- `tests/test_trend_shm.py`
- `tests/test_trend_reference.py`
- `tests/test_trend_regimes.py`
- `tests/test_jev.py`
- `tests/test_regime_feed.py`
- `tests/test_trend_graphics.py`
- `tests/test_trend_mtf_graphics.py`
- `tests/test_trend_sampling.py`
- `examples/benchmark_trend.py`
- `examples/trend_regimes.py`
- `examples/trend_graphics.py`

View File

@ -1,98 +0,0 @@
---
created_at: 2026-10-05T05:40:14.294712+00:00
generated_by:
harness: codex
provider: OpenAI
git_ref: eed58566
scope: code
substantive: true
---
## Human instruction (verbatim)
ok since i'm seeing this alot i want to make sure you did a proper /py-codestyle pass and have proper license headers throughout all new code-modules.
## Captured assistant output (verbatim)
The earlier pass wasn’t complete: the tests and examples lacked
license notices, and several modules had only abbreviated ones.
All 19 new Python files now have full AGPL notices and SPDX
identifiers, with the algorithm’s source attribution preserved.
I’m checking the remaining style rules too—annotations,
docstrings, expression layout, and regression explanations.
## Final regression output (verbatim)
```text
253 passed, 1 deselected, 1 warning in 7.93s
```
The warning is the existing Tractor dependency's Trio Event
truth-test deprecation; the unrelated remote gap-dialog actor test
is excluded. The first sandboxed attempt stopped on Trio socket
permissions and was rerun with approved escalation. No broker or
Jev API request was made.
## Code pointers
The associated boundary diffs capture full source changes. The
whole-file commit package includes untracked modules, which a bare
working-tree git diff does not include. Human editing and committing
remain pending; these pointers become valid after the complete
package is committed.
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- piker/fsp/__init__.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- piker/fsp/_api.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- piker/fsp/_engine.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- piker/ui/_display.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- piker/ui/_fsp.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- piker/data/_sampling.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- piker/fsp/_trend.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- piker/fsp/_trend_fsp.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- piker/fsp/_regime.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- piker/fsp/_jev.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- piker/fsp/_regime_feed.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- piker/ui/_trend.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- piker/ui/_trend_mtf.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- tests/test_trend.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- tests/test_trend_shm.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- tests/test_trend_reference.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- tests/test_trend_regimes.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- tests/test_jev.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- tests/test_regime_feed.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- tests/test_trend_graphics.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- tests/test_trend_mtf_graphics.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- tests/test_trend_sampling.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- examples/benchmark_trend.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- examples/trend_regimes.py`
> `git diff trend_calc_on_jev~7..trend_calc_on_jev -- examples/trend_graphics.py`
The style pass added full license headers, annotations, multiline
helper docstrings, explicit expression layout and owner-qualified
regression proof descriptions. All recurrence behavior and source
credits are preserved; callback constructor defaults are explicit.

View File

@ -1,10 +0,0 @@
# AI Prompt I/O Log — codex
This directory tracks prompts and outputs for AI-assisted development
using Codex. Substantive contributions include model identification,
prompt context, output records, and human contribution accounting.
Code output uses Git diff references; non-code output is retained in
the paired `.raw.md` file. Human contributors remain responsible for
review and acceptance. Entries follow the
[NLNet generative AI policy](https://nlnet.nl/foundation/policies/generativeAI/).

View File

@ -1,64 +0,0 @@
---
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

@ -1,33 +0,0 @@
---
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

@ -1,41 +0,0 @@
---
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

@ -1,26 +0,0 @@
---
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

@ -1,46 +0,0 @@
---
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

@ -1,40 +0,0 @@
---
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

@ -1,36 +0,0 @@
---
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

@ -1,25 +0,0 @@
---
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

@ -1,39 +0,0 @@
---
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

@ -1,30 +0,0 @@
---
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

@ -1,43 +0,0 @@
---
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

@ -1,43 +0,0 @@
---
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

@ -1,32 +0,0 @@
---
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

@ -1,32 +0,0 @@
---
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

@ -1,40 +0,0 @@
---
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

@ -1,30 +0,0 @@
---
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

@ -1,42 +0,0 @@
---
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

@ -1,34 +0,0 @@
---
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

@ -1,41 +0,0 @@
---
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

@ -1,25 +0,0 @@
---
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

@ -1,42 +0,0 @@
---
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

@ -1,32 +0,0 @@
---
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

@ -1,40 +0,0 @@
---
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

@ -1,27 +0,0 @@
---
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

@ -1,39 +0,0 @@
---
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

@ -1,27 +0,0 @@
---
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

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