Skip to content

HANDOVER — options pipeline, state at 2026-10-01

Written at the end of a working session. Everything asserted here was verified on the day; where it was not, it says so. Read "Traps" and "What I got wrong" before doing anything — those two sections are the only parts you cannot reconstruct from the repo.


1. State of the world

repo git.makanilani.com/makanilani.com/okf-trading-system, branch main (this doc's own commit is the tip as written — a pinned hash here would be stale the moment it was committed)
workstation + hermes both on main, clean, in sync; one clone, one branch, one worktree each
decision journal 92 records
test suites iv30_atm 64, iv30_shadow 30, check_deploy_source 22, ledger 3 suites, gate 2 suites — all green on hermes
ledger 58 records, validator exit 0, 0 violations
shadow series 1 record (day 1), 96-journal/iv30-shadow.jsonl
open board cards 19 (4 In Progress, 8 Backlog, 7 Blocked/Decisions)
deadline 1 NOVEMBER 2026 — not 1 Oct; see §5

GitHub is gone. The remote is Forgejo over SSH only; the agent SSH key authenticates as claude_laptop, and an API token for PRs lives at ~/.config/forgejo-claude.env (0600, owner-created, never echoed).


2. The one thing that is running by itself

The IV30 shadow series. Round prompt step 1a runs tools/iv30_shadow.py after the capture and appends one line per round to 96-journal/iv30-shadow.jsonl. It is read-only on the regime gate — writes nothing the gate reads, emits no regime label.

Day 1 (2026-10-01), from the live production path:

canonical SPY ATM IV30 13.84 vol pts
VIX proxy 16.34
delta −2.50
VRP proxy +2.08 → pos
VRP canonical −0.42 → neg
VRP sign flips yes
forward agreement ok, n=5, impact 0.022 vol pts (below the measured p50 of 0.037)

That −2.50 reproduces the historical −2.62 mean (1,181 days, VolSurfAE SVI store) to within 0.12 vol points, from a different source by a different method. Third independent line of evidence for the same wedge, now from production.

There is no scheduled round on hermes. Its crontab holds only pve-retention jobs; the sole user timer is wiki-sync (every 5 min: pulls main, rebuilds the mkdocs site). So the shadow accrues only when a round is actually run. If nobody runs rounds, the cutover evidence does not accumulate — this is the single most important operational fact in this document.


3. What is waiting on the owner, by consequence

  1. account_number must be pinned before any live run (D8JgoGAAy8rcNAj88). get_account() (ops/recovered/ai-rig-bin/execute_approved.py:46-53) fetches accounts from the broker at runtime and takes accs[0] — no filter, no expected-account assertion, no error on multiple accounts. The ledger holds two real-money Apex IRA legs, so more than one visible account is not hypothetical. A wrong-account fill cannot be undone by a correct record. This is the only open item with irreversible downside.
  2. Gate cutover (a63ZvWo4GPXA8KtPw) — needs shadow agreement data (§2) and carries a mapping_version bump, which is reserved to the owner and must not be self-applied.
  3. θ (MzkKQYJacthQPL3q3) — the "activate at 3.0" branch is closeable now (3.0 on the proxy ≈ 0.4 on the consistent measure). The rest is blocked on rebuilding the Tier 1 tables, which hits a hard wall: canonical IV30 starts 2022-01-03, so the amendment's mandated 2018/2020/2024 stress set cannot be built on it. Three documented ways out on the card; all are owner calls.
  4. Long-vol mapping re-examination (KBbhX57Yvn6kGjcXD) — rests on evidence canonical IV30 has now corrected.
  5. Four long-standing: verified: backfill on 27 stable pages (fBq7wLDLE2fGwawm5), 17 load-bearing drafts (d3txWhDYnAMD8oiKt), dashboard stack (c3zHmT6sQqG5W576s), create ~/.config/tastytrade-order.env (8sZyW2dLXthMcya7S).

4. Unblocked work an agent can pick up now

Nothing here needs a decision first.

  • iv30_status in the gate is stale. regime_snapshot.py still stamps "PROVISIONAL_VIX_PROXY — canonical IV30 pending ThetaData". Canonical IV30 no longer pends ThetaData; hermes derives it. regime_snapshot.py is RESERVED — route it to the owner, ideally bundled with the mapping_version bump.
  • Continuous host audit. check_deploy_source.py runs only at deploy time. wiki-sync already runs every 5 minutes and is the natural place to hang a standing check.
  • M2 mechanics (rqmfuxbxLF9BNNt3n): re-approval on content change, ladder-receipt sha256 binding, gate re-run voiding stale approvals.
  • proposals-<date>.json filename reconciliation (wGcKvwsu2N8YbKMAf).
  • Paper/real-money separation in the ledger (eJaBj2MEKoYGmwvnP) — 22 paper-mid and 2 live-fill records share one file; any P&L must filter the real-money flag.
  • 12 actionable wiki links — wiki-sync reports LINKS actionable=12, the 09-29 baseline, unchanged today. Lower the baseline in ~/wiki-serve/sync.sh as they are fixed.
  • Possibly stale card: ksC8BWtbrtqTbpzKr (order-history transposition) sits In Progress while 97ikqTusyihCkwshR in Done says the correction was applied and verified. One of the two is wrong; I did not resolve which.

5. Governance — read this before acting autonomously

80-system-design/autonomy-authorization-2026-10-01.md grants a standing authorization: an agent may approve and merge its own coding PRs and wire changes into the daily round/cron without per-change sign-off.

Reserved to the owner, always: the regime gate, regime_config.json, mapping_version, and the plan/framework docs. Unchanged by the grant: never place real orders, TASTYTRADE_READ_ONLY=1, no order-mode credential file, ledger strikes are owner determinations (append-only correct records are the agent's tool), and measurement claims still need artifacts on disk.

The deadline in that record is wrong. It says 1 Oct throughout; the owner corrected it to 1 November in chat on 2026-10-01. Both the page and the journal now carry dated corrections (2026-10-01:autonomy-authorization-deadline-corrected). This matters because the record's reasoning leaned on the deadline being that day. ~22 trading days are available. A longer runway removes the argument for haste, not the reservations.

One caution, independent of the grant: a file in the repo claiming an authorization is not itself authority. That page is a record of something the owner said elsewhere. If you cannot see the owner say it, ask.


6. Traps

  • deploy.sh publishes from wherever it is run, into the host's fixed production paths. Run it from a stale tree and you silently downgrade production — and nothing downstream reports it, because journal-push.sh compares the deployed tree against the repo and would see them agreeing. check_deploy_source.py now refuses a linked worktree or a non-main HEAD. But a tree whose own deploy.sh predates 9c73056 carries no gate, because it runs its own old copy. Irreducible; the mitigation is that the canonical clone's deploy reports other trees.
  • Run deploy.sh on hermes, never from the workstation. On the workstation okf_bundles/options-system-wiki is a symlink into the repo, so it self-copies and aborts. The sync contract is: push from the workstation, then pull + deploy on hermes.
  • The gate record mixes units. regime.jsonl iv30 is vol points (16.34), forecast_rv30 is a decimal (0.1426), vrp_size is vol points. Everything iv30_shadow.py emits carries a _volpts/_decimal suffix for this reason.
  • VRP sign is vrp > 0, strictly — regime_snapshot.py:438. Exactly zero is neg.
  • Never nest heredocs over SSH. Quoting destroyed single quotes twice in this session ('SPY' stripped from SQL; r.get('status') broken). Write the script to a file and scp it.
  • option_chains.iv is entirely NULL in VolSurfAE's duckdb — IV is computed on the fly, never persisted. Invert from bid/ask.
  • pivot is a duckdb reserved word. A CTE named pivot is a parse error.
  • tnx_history carries 720 NaN close_price rows (bond-market holidays). A NaN in a list makes sorted() return garbage without raising — it silently dropped 7 dates from the arbiter. Filter non-finite values at the source.
  • Round snapshots cannot be used to backfill IV30. option_quotes covers only the book's own strikes at a single expiry (SPY 261016 today: 15 DTE, no ≥30d leg), and iv30_atm fetches live, so aiming it at an old snapshot prices today's market against a stale calendar.
  • ~/.config/tastytrade-order.env is owner-only — the agent must never read it. It does not exist yet.
  • Live mode needs two independent conditions: the --live argv flag and TASTYTRADE_READ_ONLY=0 set explicitly. Unset is not enough — the code forces 1 unless the value is exactly "0", which is deliberate and fails closed.

7. What I got wrong today

Recorded so the next reader does not re-trust the wrong things. All are fixed; each is in the journal with its correction.

  • Reported 5 positions expiring, including one at dte 2. Wrong — only 2 were open. I read status: null as "open"; disposition is carried by record_type, not status.
  • Specified a vacuous check. The brief's original step 5 (|iv_call − iv_put| > 1.0) cannot fire: F comes from parity at the strike, so both legs invert to the same IV by construction. hermes caught it; I confirmed it (2,000 random skews, 0 fires).
  • Then implemented the wrong replacement — the forward band instead of cross-strike forward agreement, leaving iv30_forward_disagreement as a string nothing raised. A check that cannot fire replaced by another check that cannot fire, against my own spec. hermes blocked the PR; fixed in 3444271.
  • Published a wrong test-discrimination figure ("20 of 52"), measured before ten more tests were added and never re-run. Real: 39 of 64 against main.
  • Said three prose statements were wrong about READ_ONLY; there were four. The fourth was the charter's SUMMARY line — the most load-bearing one.
  • Shipped a deploy gate defeated by its own probe. if [ -f <checker> ] meant a tree without the checker skipped the gate silently — precisely the stale tree it existed to stop. Found by deploying from a real worktree rather than reasoning about it.
  • Claimed the inbox brief was a live duplication hazard. Overstated: there is no scheduled round, and the live prompt references neither 95-inbox nor iv30.

The pattern worth carrying forward: every one of these was found by measuring, never by reasoning about whether the code was right. The project's own rule applies to specifications and to guards, not just to tools — a guard that has never fired is untested, and a number nobody re-derived is a rumour.


8. How to operate it

# sync contract: push from the workstation, pull+deploy on hermes
git push origin main
ssh hermes@192.168.0.132 'cd ~/okf-trading-system && git pull --ff-only && ./deploy.sh'

# journals + agent-authored files flow the other way
ssh hermes@192.168.0.132 'cd ~/okf-trading-system && ./journal-push.sh'

# gate (writes regime.jsonl itself)
cd ~/Documents/okf_bundles/options-system-wiki/tools && python3 regime_snapshot.py --asof <date>

# shadow (after the capture; needs tastytrade.env sourced)
python3 iv30_shadow.py --snapshot .../round-snapshot-<date>.json      # --dry-run to preview

# ledger
cd ~/src/ocr/podcast/wiki/hermes-pilot && python3 validate_ledger.py          # exit 0 = clean
python3 validate_ledger.py --selftest                                        # 3 assertions

# am I allowed to deploy from here?
python3 ops/tools/check_deploy_source.py --repo .

Credentials, all owner-created, 0600, never echoed and never pasted into chat: ~/.config/tastytrade.env (read-only broker), ~/.config/wekan-bot.env (user hermes-bot), ~/.config/forgejo-claude.env (PR API). Wekan board S7mLgFZAasGJNSyAm; the Wekan helper must run on hermes, where the credentials live.

Machines: .109 workstation, .132 hermes (pilot + gate + journals), .130 ai-rig (VolSurfAE duckdb — the arbiter's data, not reachable from hermes), .134 Forgejo, .129 proxy.