{
 "present": true,
 "seq": 0,
 "ts": "2026-09-28T00:07:44.442177Z",
 "hash": "fb66b6bf40e9229407cdd62266cfe180994c38eb96f18d5c8bca983323ccd3c0",
 "genesis": {
  "bgc_package": "@bitget-ai/bitget-agent-cli@3.0.0",
  "code_commit": "5c838fdce0e4cde5d8f091174907a2d02ff92a67",
  "contract_version": "1.1.0",
  "created_at": "2026-09-28T00:07:42.667223Z",
  "declared_changes": [
   {
    "change_id": "run2-d1",
    "detail": "Run 1 evaluated triggers only on light snapshots (every 5 minutes), which by design do not read crowd text or the calendar (DESIGN.md §7), and never on the full snapshot built for each decision, so coordinated_cluster, earnings_event and filing_event could not fire. Run 2 keeps the pre-registered cadences (crowd text and the calendar are read on full snapshots only) and evaluates those kinds on every full snapshot: when a candidate set will start a decision, the full snapshot is taken first and its crowd and calendar events are admitted in the same batch, under the same cooldowns and daily cap, so they join that decision instead of waking another. Light snapshots evaluate only Fear & Greed, funding and open interest, so no condition is emitted twice. The policy, the prompt files and every guard limit are unchanged by this change.",
    "evidence": {
     "run1_full_snapshot_triggers_never_evaluated": "22 coordinated_cluster triggers on run 1's 2 decision snapshots (admitted riders under run 2's wiring; 1 more refused by cooldown); 0 extra decisions",
     "source": "validation/run2/run1_trigger_replay.json (policy_v1)"
    },
    "files": [
     "src/sentiment_agent/events/triggers.py",
     "src/sentiment_agent/runtime/loop.py"
    ],
    "kind": "code_fix",
    "new_policy_hash": null,
    "previous_policy_hash": null,
    "title": "Crowd and calendar trigger kinds are evaluated on the full snapshot"
   },
   {
    "change_id": "run2-d2",
    "detail": "A source that errors, times out or answers hollow raises an alarm with the start of its streak; recovery clears it. Each change is a feed_health ledger event (and every decision snapshot's report is logged), carried on the decision card, shown on the public page and in the health file, and every trigger kind it leaves unable to fire is named per instrument with its cause (a failing feed, no usable data, the light-snapshot cadence, or no frozen threshold). Run 1 recorded these failures only inside snapshots: sentiment_index.current hollow in 226 of 226, bitget-mcp-server do_query answering 503 on every call from 2026-09-25 10:14 UTC (and a session-expiry 404 at 08:33), and no upcoming earnings date in the calendar. The policy, the prompt files and every guard limit are unchanged by this change.",
    "evidence": {
     "calendar": "equity_calendar answered; no upcoming report date for any equity",
     "do_query": "error (service reported 503) on every call from 2026-09-25T10:14:31Z",
     "sentiment_index.current": "hollow in 226 of 226 run-1 snapshots",
     "source": "run 1 public/ledger.jsonl through seq 363"
    },
    "files": [
     "src/sentiment_agent/perception/feeds.py",
     "src/sentiment_agent/runtime/loop.py",
     "src/sentiment_agent/site/export.py",
     "src/sentiment_agent/site/render.py",
     "src/sentiment_agent/site/cards.py"
    ],
    "kind": "observability",
    "new_policy_hash": null,
    "previous_policy_hash": null,
    "title": "Feed-health alarms, and a named reason for every blind trigger kind"
   },
   {
    "change_id": "run2-d3",
    "detail": "Crowd positioning (retail and top-trader long/short, taker buy/sell, open interest, funding) is read from bitget-mcp-server, then bitget-signal for a field it left empty; crypto Fear & Greed from both; news from bitget-signal. When both leave a reading empty, run 2 reads the source they themselves wrap: Binance USD-M futures (futures/data/*, fapi/v1/fundingRate; open interest in contracts, the unit bitget-mcp-server returns), alternative.me for Fear & Greed, and four publishers' RSS for news. Every such call is recorded on its own surface (upstream_direct) with the upstream named, after the Bitget calls, which are still made and recorded, so their failure stays a feed-health alarm and the toolkit count still counts only what Bitget answered. A series is taken whole from one source. The policy, the prompt files and every guard limit are unchanged by this change.",
    "evidence": {
     "bitget_mcp_server": "answered 144 of 1356 calls from its first failure (2026-09-25T08:33Z) to seq 546",
     "bitget_signal": "answered 0 of 939 calls over the whole run",
     "readings_missing": "crypto Fear & Greed and BTCUSDT long/short missing from 146 of 164 snapshots since that failure",
     "source": "validation/run2/run1_feed_outage.json, by python scripts/feed_outage.py <run 1 public/> --until-seq 546 --out validation/run2/run1_feed_outage.json"
    },
    "files": [
     "src/sentiment_agent/sources/upstream.py",
     "src/sentiment_agent/sources/toolkit.py",
     "src/sentiment_agent/runtime/wiring.py",
     "src/sentiment_agent/perception/feeds.py",
     "src/sentiment_agent/site/coverage.py",
     "src/sentiment_agent/types.py"
    ],
    "kind": "code_fix",
    "new_policy_hash": null,
    "previous_policy_hash": null,
    "title": "The upstreams Bitget's data services wrap, read directly when both fail"
   },
   {
    "change_id": "run2-d4",
    "detail": "Run 1's fact legend told the model that top_trader_long_short_ratio was 'the same for Bitget's top traders', and described the retail long/short, taker and open-interest change figures as 'live' with no market named. All four are Binance USD-M futures figures: bitget-mcp-server and bitget-signal read them from Binance (sources/bitget_data.py EXCHANGE), and run 2's fallback reads the same endpoints (sources/upstream.py). The legend now names Binance USD-M for each; the funding rate and open interest level, read from Bitget's own live market, keep their Bitget label. Only decision/prompt.py changes: the system prompt, the output schema, the policy and every guard limit are unchanged.",
    "evidence": {
     "decision/prompt.py": "15f825ced789b7fb9c809685fd67546b466de45c3b8cf9846695e839b8715e0d in run 1's genesis, 1ce5b81a4368a3ba0d56a0640b861831473ac20c8f5b980137f5a073e35e85fd after this change",
     "source": "sources/bitget_data.py derivatives(); sources/upstream.py module docstring"
    },
    "files": [
     "src/sentiment_agent/decision/prompt.py"
    ],
    "kind": "prompt_fix",
    "new_policy_hash": null,
    "previous_policy_hash": null,
    "title": "The prompt names the market each crowd-positioning figure comes from"
   },
   {
    "change_id": "run2-d5",
    "detail": "The model may declare that a held position's stated invalidation has fired, and that declaration is the only thing that lets G6 permit an increase or a flip inside the 24 hours after the last increase. Run 1's contract accepted any non-empty invalidation_evidence, so a sentence with no fact behind it lifted the hold. From run 2 the contract returns such an answer to the model unless its evidence names at least one key the request showed as `key = value` (for example NVDAUSDT.funding_z_live), which is what output_schema_v1.md already asks for. The policy, the prompt files and every guard limit are unchanged by this change.",
    "evidence": {
     "rule_already_written": "output_schema_v1.md: invalidation_triggered is true only with invalidation_evidence naming the fact that shows it",
     "test": "tests/decision/test_decision_contract.py::TestADeclaredInvalidationNamesAFact"
    },
    "files": [
     "src/sentiment_agent/decision/contract.py",
     "src/sentiment_agent/decision/agent.py"
    ],
    "kind": "code_fix",
    "new_policy_hash": null,
    "previous_policy_hash": null,
    "title": "A declared invalidation must name a fact the request showed"
   },
   {
    "change_id": "run2-d7",
    "detail": "Replayed on run 1's market, run 2's agent acted in 14 of 15 decisions and 30 of its 31 proposed weights were a full target (5% of equity), whatever the evidence behind them (validation/run2/act_rate.json), and after run2-a7 the prompt's flat 4% stop was no longer true of five names. System prompt v2 keeps v1 and adds: a target's size follows the evidence (one positioning measure past its reference, unconfirmed, is at most half a target; a full target needs two independent measures the same way; crowd text alone never sizes); confidence is the probability of being right at the horizon after fees, and new exposure needs it above even odds, only a little above without confirmation; a magnitude is stated with its facts, and words like extreme only for a fact beyond its reference. The stop rule names the wider stop and smaller cap of the drifting names. The output schema, the policy and every guard limit are unchanged.",
    "evidence": {
     "act_rate": "validation/run2/act_rate.json",
     "decision/prompt.py": "df7f36967adf2e26f454851a5d9e02bc26a0540e1e46cf7e0413cc475a74bd36 after run2-a7, 74c6f697c809c6c4a89e01f8cdcbdda1df4c7c76182f9b5838345dcc2ade6ca0 after this change",
     "decision/prompts/system_v1.md": "67773d85dbebbd0e6b0db5dba48b558ac97e231aa1e37db39b23201da095007e in run 1's genesis; replaced by system_v2.md 3a96252827c4d2872703387dc625e1338817099cafaa6f96a7b6eac39658cf13",
     "replayed_with_v2": "validation/run2/act_rate_prompt_v2.json: acted in 10 of 15 decisions, all 25 proposed weights half a target, the largest net book 10% of equity"
    },
    "files": [
     "src/sentiment_agent/decision/prompts/system_v2.md",
     "src/sentiment_agent/decision/prompt.py"
    ],
    "kind": "prompt_fix",
    "new_policy_hash": null,
    "previous_policy_hash": null,
    "title": "The system prompt sizes by the evidence and states each name's stop"
   },
   {
    "change_id": "run2-d8",
    "detail": "Replayed on run 1's market with system prompt v2 (run2-d7), the agent sized by the evidence but opened 14 of its 25 positions at a stated confidence of 0.38 to 0.48: trades it judged more likely wrong than right, which the fees then make a loss. The prompt's words did not hold that line, so from run 2 the contract returns to the model any target that opens, flips or enlarges a position at a confidence of 0.5 or below (decision/contract.py EVEN_ODDS); a cut or a close is accepted at any confidence. The policy, the prompt files and every guard limit are unchanged by this change.",
    "evidence": {
     "replayed_with_v2": "validation/run2/act_rate_prompt_v2.json",
     "test": "tests/decision/test_decision_contract.py::TestNewExposureNeedsBetterThanEvenOdds"
    },
    "files": [
     "src/sentiment_agent/decision/contract.py"
    ],
    "kind": "code_fix",
    "new_policy_hash": null,
    "previous_policy_hash": null,
    "title": "New exposure needs a confidence above even odds"
   },
   {
    "change_id": "run2-d9",
    "detail": "Neither MCP client ever ended a session: the live runner held one per server for its lifetime, and every one-shot command (probe-toolkit, verify, the preflight) left two open on Bitget's servers until they expired. bitget-mcp-server began refusing new sessions with 'Too many open sessions' on 2026-09-26 (ARGUS measured it, argus/data/skill_runs). From run 2 the client ends its session with the HTTP DELETE the streamable-HTTP transport specifies, and every command ends the sessions its default toolkit opened. The policy, the prompt files and every guard limit are unchanged by this change.",
    "evidence": {
     "specification": "MCP streamable HTTP, revision 2025-06-18: a client that no longer needs a session SHOULD send HTTP DELETE with its Mcp-Session-Id",
     "test": "tests/sources/test_mcp_http.py::test_close_ends_the_session_with_a_delete_once"
    },
    "files": [
     "src/sentiment_agent/sources/mcp_http.py",
     "src/sentiment_agent/sources/bitget_data.py",
     "src/sentiment_agent/sources/signal_skills.py",
     "src/sentiment_agent/sources/toolkit.py",
     "src/sentiment_agent/runtime/wiring.py",
     "src/sentiment_agent/runtime/cli.py"
    ],
    "kind": "code_fix",
    "new_policy_hash": null,
    "previous_policy_hash": null,
    "title": "MCP sessions are ended when a command finishes"
   },
   {
    "change_id": "run2-d6",
    "detail": "The run is scored on the book's equity, and the book counted fills alone: starting equity plus realized minus fees plus unrealized. A funding-extreme strategy holds the side the funding pays or charges, so any funding Demo books would have been missing from the record. From run 2 each reconciliation reads the account's financial records (funds_records --action financial) over the last four days, writes every funding payment not yet in the ledger as a funding event, and the book adds it to equity and to the net P&L of the trade open on its symbol when it settled; trades.csv carries a funding column and scripts/recompute.py rebuilds both. Whether Demo books funding at all is NOT VERIFIED; if it does not, nothing is read and nothing changes. A failed read is recorded on the reconciliation, never a discrepancy. The policy, the prompt files and every guard limit are unchanged by this change.",
    "evidence": {
     "funding_types": "CONTRACT_MAIN_SETTLE_FEE_USER_IN/OUT, MARGIN_SETTLE_FEE_USER_IN/OUT and the RWA user variants, from bitget.com/docs/uta/enum (type-financial-records), read 2026-09-27",
     "run1_funding": "run 1 held no position across a settlement, so its record cannot say whether Demo books funding"
    },
    "files": [
     "src/sentiment_agent/types.py",
     "src/sentiment_agent/execution/bgc.py",
     "src/sentiment_agent/execution/reconcile.py",
     "src/sentiment_agent/execution/simulated.py",
     "src/sentiment_agent/book/book.py",
     "src/sentiment_agent/book/projection.py",
     "src/sentiment_agent/runtime/loop.py",
     "src/sentiment_agent/site/export.py",
     "scripts/recompute.py"
    ],
    "kind": "code_fix",
    "new_policy_hash": null,
    "previous_policy_hash": null,
    "title": "Funding the venue books is in the scored equity"
   },
   {
    "change_id": "run2-a1",
    "detail": "Policy v1 evaluated the live funding z-score trigger for BTCUSDT only. From run2-a1 it is evaluated for all 14 instruments with the same +-2 threshold, 90-settlement lookback, 240-minute cooldown per instrument, 8 event decisions per UTC day, and the same weekend refusal of events on US-session legs while G2 holds them flat.",
    "evidence": {
     "funding_extremes": "live funding z beyond +-2 in 436 equity/index instrument-snapshots of 226 (HOOD 88, NVDA 84, MSTR 58, GOOGL 49, AMZN 46, SNDK 44, TSLA 30, COIN 23, NDX100 14); BTCUSDT 0",
     "replayed_event_decisions": "15 in 19.9 hours on run 1's 226 snapshots under this step alone (7 on 2026-09-24, 8 on 2026-09-25, the daily cap refusing 3 more); 15 also within the token budget at every decision's worst-case bound; run 1 itself: 0",
     "source": "validation/run2/run1_trigger_replay.json, by python scripts/replay_triggers.py <run 1 public/> --until-seq 363 --out validation/run2/run1_trigger_replay.json (policy_v1, run2_a1)"
    },
    "files": [
     "src/sentiment_agent/policy.py",
     "src/sentiment_agent/events/triggers.py"
    ],
    "kind": "policy_amendment",
    "new_policy_hash": "e83d4ab6d35064415cb0496fc9c4d920f7e485742314e73a81a1676ccadf04d0",
    "previous_policy_hash": "55ff779b5d6f2c55082be1b4db6c9fde53865213cade58232f64f372ad73aeaf",
    "title": "funding_zscore covers every universe instrument"
   },
   {
    "change_id": "run2-a2",
    "detail": "The z-score alone fires on a series that sits at zero: an equity perp's live rate was exactly zero in half of run 1's instrument-snapshots, so a single tick off zero scored as an extreme and the trigger fired in 13.8% of them, where a +-2 bound on a normal series fires in 4.6%. From run2-a2 the live rate must also be at least 0.075% per settlement interval in absolute value. The threshold, lookback, cooldown and cap are unchanged.",
    "evidence": {
     "agent_on_the_unfloored_triggers": "run 2's agent acted in 14 of the 15 decisions run2-a1 alone would have woken, every one of them short",
     "funding_extremes_with_level": "43 of the 436 (MSTR 29, SNDK 14)",
     "level_profile": "3,150 instrument-snapshots with a live z-score and rate: beyond +-2 in 13.8%; the absolute rate zero in 51.1%, median 0, 95th percentile 4.5 bp; beyond +-2 with a level of 5, 7.5 and 8 bp: 3.5%, 1.4%, 1.0%",
     "replayed_event_decisions": "3 in 19.9 hours (2026-09-25 00:18 MSTR, 01:28 SNDK, 12:31 MSTR), against 15 under run2-a1 alone",
     "source": "validation/run2/run1_trigger_replay.json, by python scripts/replay_triggers.py <run 1 public/> --until-seq 363 --out validation/run2/run1_trigger_replay.json (funding_level, funding_extremes, policy_v2); validation/run2/act_rate.json"
    },
    "files": [
     "src/sentiment_agent/policy.py",
     "src/sentiment_agent/events/triggers.py"
    ],
    "kind": "policy_amendment",
    "new_policy_hash": "8c895787084794e5ca43bce0ce034c4caa0bce85f04bb48a37498dacdaf5d0fd",
    "previous_policy_hash": "e83d4ab6d35064415cb0496fc9c4d920f7e485742314e73a81a1676ccadf04d0",
    "title": "funding_zscore also needs a funding level of 7.5 bp"
   },
   {
    "change_id": "run2-a3",
    "detail": "Policy v1 capped each name at 5% and the book at 25% gross, and nothing capped one direction or one exposure spread across names. From run2-a3 G3 also caps the net weight (long minus short) at 10% of equity and MSTR, COIN, HOOD and CRCL at 7.5% gross together. What is already held keeps its weight; new exposure is scaled pro rata into what the caps leave.",
    "evidence": {
     "crypto_beta": "COIN, HOOD and MSTR 5% short each at once (15%), 2026-09-25 00:13 UTC; MSTR moved 1.85x BTC over 30 days of hourly candles (correlation +0.83)",
     "one_sided_books": "run 2's agent, replayed on run 1's market under run2-a1, acted in 14 of 15 decisions and every book it proposed was short; the largest was 20% net short (GOOGL, HOOD, MSTR, TSLA at 5% each, 2026-09-25 12:05 UTC); no guard of policy v1 changed any of them",
     "ruled_by_policy_v2": "the same 15 recorded answers, parsed again and ruled by policy v2's kernel with no model call: 7 of the 12 books ruled were cut, and the current contract sends 2 more back before any ruling (run2-d8, added after the answers were recorded); the largest net short from 20% to 10% and the crypto-beta names from 15% to 7.5%",
     "source": "validation/run2/act_rate.json, by python scripts/act_rate.py <run 1 public/> --until-seq 363 --policy run2-a1 --out validation/run2/act_rate.json; validation/run2/rerule_v2.json, by python scripts/rerule.py <run 1 public/> --until-seq 363"
    },
    "files": [
     "src/sentiment_agent/policy.py",
     "src/sentiment_agent/kernel/guards.py",
     "src/sentiment_agent/kernel/kernel.py",
     "src/sentiment_agent/types.py"
    ],
    "kind": "policy_amendment",
    "new_policy_hash": "3756dfea0485a5ade1afb0819aa6151bdb3af545ea29ff239ceb8e5088826493",
    "previous_policy_hash": "8c895787084794e5ca43bce0ce034c4caa0bce85f04bb48a37498dacdaf5d0fd",
    "title": "G3 caps the book's net weight at 10% and the crypto-beta names at 7.5%"
   },
   {
    "change_id": "run2-a4",
    "detail": "Run 2 is scored from 2026-09-28 00:00 UTC to 2026-10-07 00:00 UTC, and the window is in the hashed policy. At its end G2 closes every leg of every asset class and refuses new exposure, the loop stops deciding, and the published metrics count only marks, fills and trades inside it.",
    "evidence": {
     "run1": "no scored span in run 1's genesis; a trade still open at the end of a record has no result, so win rate and trade count depend on when the record is read"
    },
    "files": [
     "src/sentiment_agent/policy.py",
     "src/sentiment_agent/kernel/guards.py",
     "src/sentiment_agent/kernel/kernel.py",
     "src/sentiment_agent/runtime/loop.py",
     "src/sentiment_agent/analysis/metrics.py",
     "src/sentiment_agent/site/export.py",
     "src/sentiment_agent/types.py"
    ],
    "kind": "policy_amendment",
    "new_policy_hash": "3e3182060695bee5cb62d45ae5ea629a15bd6961216934f8f5a494db7af32da9",
    "previous_policy_hash": "3756dfea0485a5ade1afb0819aa6151bdb3af545ea29ff239ceb8e5088826493",
    "title": "A pre-registered scoring window, closed by G2 at its end"
   },
   {
    "change_id": "run2-a5",
    "detail": "Under policy v1, four losing trades in a row put the book in reduce-only until a winning trade closed. Reduce-only refuses every exposure-adding order, so a book that was flat could never open, never close a winner, and never leave reduce-only: the trip was absorbing. From run2-a5 it lapses 24 hours after the last losing close. The drawdown trips, which clear on equity, are unchanged.",
    "evidence": {
     "mechanism": "kernel/breaker.py: consecutive_losses counts closed trades back from the last winner, and in reduce-only G10 refuses every opening, so on a flat book the count cannot fall"
    },
    "files": [
     "src/sentiment_agent/policy.py",
     "src/sentiment_agent/kernel/breaker.py",
     "src/sentiment_agent/book/book.py",
     "src/sentiment_agent/types.py"
    ],
    "kind": "policy_amendment",
    "new_policy_hash": "ee8801b81cf542be8ee98dd69a2bd739a0bae6a2db1ce262ca52bae98e4ac22b",
    "previous_policy_hash": "3e3182060695bee5cb62d45ae5ea629a15bd6961216934f8f5a494db7af32da9",
    "title": "The losing-streak trip lapses 24 hours after the last losing close"
   },
   {
    "change_id": "run2-a6",
    "detail": "Under policy v1 one failed decision (a timeout, a malformed answer, a refused call) flattened every open leg at market. From run2-a6 the first and second failed decisions in a row hold the book under the venue stops G4 placed with each leg and open nothing; the third flattens it as before. A decision that succeeds resets the count.",
    "evidence": {
     "cost": "a flatten is a taker trade on every open leg at 6 bp each (Demo takerFeeRate 0.0006, universe_probe.json), paid on a gateway timeout that says nothing about the thesis"
    },
    "files": [
     "src/sentiment_agent/policy.py",
     "src/sentiment_agent/runtime/loop.py",
     "src/sentiment_agent/types.py"
    ],
    "kind": "policy_amendment",
    "new_policy_hash": "cb46674ba28d9c1f8e8bcf786c5c856073ad148e3807e212bb9f42a5d7d7a745",
    "previous_policy_hash": "ee8801b81cf542be8ee98dd69a2bd739a0bae6a2db1ce262ca52bae98e4ac22b",
    "title": "A model outage flattens the book on the third failed decision in a row"
   },
   {
    "change_id": "run2-a7",
    "detail": "G4 places every stop on the Demo mark, and the Demo price of HOOD, SNDK, MSTR, CRCL and SP500 departs from live by 267 to 383 bps at the 99th percentile, so a 4% stop could be taken out by the venue's drift rather than by the market. From run2-a7 a name's stop is the wider of 4% and twice its measured p99 gap (HOOD 7.66%, SNDK 6.96%, MSTR 6.32%, CRCL 6.17%, SP500 5.33%; every other name keeps 4%), and G3 caps its weight at 5% x 4% / its stop, so any name loses at most 0.2% of equity at its stop. The prompt shows each such name's stop and cap as facts (decision/prompt.py); the system prompt and the output schema are unchanged.",
    "evidence": {
     "decision/prompt.py": "1ce5b81a4368a3ba0d56a0640b861831473ac20c8f5b980137f5a073e35e85fd after run2-d4, df7f36967adf2e26f454851a5d9e02bc26a0540e1e46cf7e0413cc475a74bd36 after this change",
     "demo_live_gap_p99_bps": "HOOD 383.0, SNDK 348.2, MSTR 316.2, CRCL 308.7, SP500 266.5 (policy.UNIVERSE, from validation/demo_venue)"
    },
    "files": [
     "src/sentiment_agent/policy.py",
     "src/sentiment_agent/types.py",
     "src/sentiment_agent/kernel/planner.py",
     "src/sentiment_agent/kernel/guards.py",
     "src/sentiment_agent/kernel/kernel.py",
     "src/sentiment_agent/execution/stops.py",
     "src/sentiment_agent/decision/prompt.py"
    ],
    "kind": "policy_amendment",
    "new_policy_hash": "d719e75950df438ce15db99f82e48d7baf6f06366ed5766be6b981012b2ac380",
    "previous_policy_hash": "cb46674ba28d9c1f8e8bcf786c5c856073ad148e3807e212bb9f42a5d7d7a745",
    "title": "A name's stop clears its Demo-live drift, at the same loss per name"
   },
   {
    "change_id": "run2-a8",
    "detail": "Policy v1 pre-registered what a no-edge book scores over 72 hours, from a simulation outside the kernel. From run2-a8 the envelope is the nine-day coin-flip book ruled, sized and filled by the production kernel, planner and book over 51 rolling windows of Bitget history (median return -3.5 bp, median drawdown -0.45%, median win rate 0.45, median 23 closed trades). G7's 20 bp window budget is kept: the no-edge book paid a median 6.3 and a p95 of 8.2 bp in fees over nine days.",
    "evidence": {
     "envelope": "validation/run2/strategy_replay.json, by python scripts/strategy_replay.py --history <history.json> --marks <history_marks.json> --seeds 50",
     "window": "run 2 scores 216 hours (run2-a4), three times the 72 hours policy v1's envelope measured"
    },
    "files": [
     "src/sentiment_agent/policy.py",
     "scripts/strategy_replay.py",
     "src/sentiment_agent/analysis/armsim.py"
    ],
    "kind": "policy_amendment",
    "new_policy_hash": "eaee6d5a27f608db9923e870fdd23f786925b92a2e11d440c5c7b37aeb93689e",
    "previous_policy_hash": "d719e75950df438ce15db99f82e48d7baf6f06366ed5766be6b981012b2ac380",
    "title": "The expected numbers and the fee budget's basis, measured for nine days"
   }
  ],
  "dependency_lock_hashes": {
   "tools/agent-hub/package-lock.json": "73973309493d9913aaffb5f8024b2454bd27279059ec74eb5485170cbc072eec",
   "uv.lock": "d8dc71b61bc00f45f5ecd96b7e98f3cfbb8f50081d9bbc0b57e4433969fe5c9b"
  },
  "expected_envelope": {
   "closed_trades": "median 23",
   "max_drawdown_pct": "median -0.45, p05 -0.95, worst -1.67",
   "return_on_equity_bps": "median -3.5, p05 -75.9, p95 +93.6",
   "sharpe_ann": "median -0.5, p05 -12.3, p95 +11.1",
   "source": "validation/run2/strategy_replay.json (no-edge coin-flip book: five names a day at half a target, 50 seeds on each of 51 rolling 216 h windows of Bitget history, 2026-07-30 to 2026-09-26, ruled, sized and filled by the production kernel, planner and book)",
   "win_rate": "median 0.45, p05 0.28, p95 0.62"
  },
  "metric_definitions": [
   {
    "formula": "r_t = E_t / E_{t-1} - 1 on the UTC hour grid, E = equity_book",
    "name": "hourly_return",
    "notes": "Same grid and definition as validation/demo_venue/envelope_clean.py",
    "unit": "fraction"
   },
   {
    "formula": "mean(r) / pstdev(r) * sqrt(8760)",
    "name": "sharpe_ann",
    "notes": "Population stdev, as envelope_clean.py. Undefined (None) when pstdev is 0.",
    "unit": "annualised ratio"
   },
   {
    "formula": "sqrt((1 + 0.5 * S_h^2) / n) * sqrt(8760), S_h = mean(r) / pstdev(r)",
    "name": "sharpe_se_ann",
    "notes": "Lo (2002) iid standard error; about 11 at n = 72 with S_h near 0",
    "unit": "annualised ratio"
   },
   {
    "formula": "mean(r) / sqrt(mean(min(r, 0)^2)) * sqrt(8760)",
    "name": "sortino_ann",
    "notes": "",
    "unit": "annualised ratio"
   },
   {
    "formula": "min_t (E_t / max_{s<=t} E_s - 1)",
    "name": "max_drawdown",
    "notes": "",
    "unit": "fraction (non-positive)"
   },
   {
    "formula": "count(net_pnl > 0) / count(closed trades)",
    "name": "win_rate",
    "notes": "A closed trade is one symbol from flat to flat; a flip closes one and opens one. net_pnl includes both fees. None when no trade has closed.",
    "unit": "fraction"
   },
   {
    "formula": "sum(|fill notional|) / mean(E)",
    "name": "turnover",
    "notes": "",
    "unit": "multiple of equity"
   },
   {
    "formula": "moving-block bootstrap of r, block = ceil(n^(1/3)) hours, 10,000 resamples, seed 20260924; 5th and 95th percentiles over the resamples where the statistic is defined, the share where it is not published beside the band; the Sharpe ratio of an all-zero resample is 0",
    "name": "ci90",
    "notes": "Descriptive, not inferential: 72 hourly marks cannot separate skill from luck.",
    "unit": "per metric"
   }
  ],
  "mode": "paper",
  "policy": {
   "breaker": {
    "basis": "See GuardBasis G10 Policy v2 (run2-a5): the losing-streak trip lapses 24 hours after the last losing close. Under v1 it was absorbing: reduce-only cannot open, so a flat book could never close the winner that ends the streak, and four stop-outs would have kept the book flat for the rest of the run.",
    "halt_drawdown": 0.04,
    "losing_streak_cooloff_hours": 24,
    "losing_streak_reduce_only": 4,
    "quote_max_age_seconds": 120,
    "reduce_only_drawdown": 0.025,
    "snapshot_max_age_minutes": 15
   },
   "cluster_caps": [
    {
     "basis": "Strategy holds bitcoin; Coinbase, Robinhood and Circle earn on crypto volume and stablecoins. Measured 2026-09-26 on 30 days of Bitget hourly candles: MSTR 1.85x BTC's move (correlation +0.83, R-squared 69%) and COIN 1.37x (+0.74), against 12% and 11% explained by the Nasdaq-100. Several of them at the 5% per-name cap is one bitcoin bet at 15-20%; the cluster may hold 7.5%, one and a half names (run2-a3).",
     "cap": 0.075,
     "name": "crypto-beta",
     "symbols": [
      "MSTRUSDT",
      "COINUSDT",
      "HOODUSDT",
      "CRCLUSDT"
     ]
    }
   ],
   "daily_kill_pct": 0.015,
   "decision": {
    "basis": "FULL reasoning on the <=4 daily heartbeats, LOW on <=8 event decisions: measured FULL ~4.3k and LOW ~0.8k completion tokens per call (ARGUS llm/qwen.py). Streaming is required for FULL: the gateway closes unstreamed requests at ~120 s. The cap is sized to the budget's own projection (llm/budget.py: one token per prompt byte plus the completion cap), prompt included, for the busiest day the trigger rules allow: recorded dry-run prompt 40,784 B (system 11,898 + user 28,886), x1.25 headroom = 50,980 B; one attempt = 50,980 + 2x16 + 128 + 8,192 = 59,332; a retry adds 8,192 + 32 = 67,556; three attempts = 194,444 per decision; (4 heartbeats + 8 events) x 194,444 = 2,333,328, rounded up to 2,400,000. Real spend is lower (a byte bounds a token from above); the prompt_tokens of the first live calls re-check it before genesis. Owner requests are outside this sum; the heartbeats left in the day are reserved before an event decision is admitted. The cap is the owner's to approve. Policy v2 (run2-a6): the book is flattened after three failed decisions in a row, not the first. Every open leg carries a venue stop (G4), and a flatten is itself a rule-driven trade paying the taker fee on every leg; one gateway timeout is not evidence the thesis failed.",
    "call_timeout_seconds": 600,
    "daily_token_cap": 2400000,
    "max_attempts": 3,
    "max_completion_tokens": 8192,
    "min_horizon_hours": 24,
    "model": "qwen3.8-max",
    "outage_flatten_after": 3,
    "temperature": 0.0,
    "thinking_event": "low",
    "thinking_heartbeat": "full"
   },
   "excluded": [
    "ETHUSDT",
    "SOLUSDT",
    "XRPUSDT",
    "DOGEUSDT"
   ],
   "expected_envelope": {
    "closed_trades": "median 23",
    "max_drawdown_pct": "median -0.45, p05 -0.95, worst -1.67",
    "return_on_equity_bps": "median -3.5, p05 -75.9, p95 +93.6",
    "sharpe_ann": "median -0.5, p05 -12.3, p95 +11.1",
    "source": "validation/run2/strategy_replay.json (no-edge coin-flip book: five names a day at half a target, 50 seeds on each of 51 rolling 216 h windows of Bitget history, 2026-07-30 to 2026-09-26, ruled, sized and filled by the production kernel, planner and book)",
    "win_rate": "median 0.45, p05 0.28, p95 0.62"
   },
   "fee_budget_daily_bps": 7.0,
   "fee_budget_window_bps": 20.0,
   "gross_max": 0.25,
   "grounding_tolerance": 0.02,
   "guard_bases": [
    {
     "basis": "Demo flash prints (BTCUSDT Demo mark 65.6% from its index, 2026-06-29 00:00 UTC); excursion counts and p99 gaps in demo_integrity.json and fade_demo.json; weekend Demo index moves 0.1-1.5 bps/h vs 18-66 bps/h on weekdays (weekend_vol.json)",
     "guard": "G1_venue_integrity",
     "rule": "Refuse or exit an instrument whose Demo mark departs more than 3% from its Demo index, whose Demo-live price gap exceeds its measured p99, or whose Demo index has not moved (mean |1H move| over 3h below 2 bps) while its session should be open."
    },
    {
     "basis": "UTA Demo does not track these perps on weekends. Mostly it freezes: weekend mean |1H move| of the Demo index 0.1-1.5 bps against 5.6-29.1 bps live (weekend_vol.json); MSTR +204 bps live vs +5 bps Demo from Saturday 00:00 to 12:00 UTC on 2026-09-19. Sometimes it jumps on its own: AAPL +80 bps Demo vs +17 bps live on 2026-09-12 (fade_demo2.txt); policy v2 (run2-a4): a trade still open when the record is scored has no result, so the window closes them all",
     "guard": "G2_weekend_freeze",
     "rule": "US-equity and US-index legs flat from Friday 20:00 UTC to Monday 00:00 UTC; pre-flatten from 19:45; no new equity exposure in the 2h before the freeze. From the scoring window's end, 2026-10-07 00:00 UTC, every leg of every class is closed and nothing opens."
    },
    {
     "basis": "Envelope at 25% gross: worst 72h drawdown -2.48% under no edge (envelope_clean.json); the drawdown is the one quantitative number design controls; policy v2 (run2-a3): run 2's agent, replayed on run 1's market, took one side in all 14 of the decisions where it acted, up to 20% net short, with MSTR, COIN and HOOD 5% short each at once (validation/run2/act_rate.json); policy v2 (run2-a7): widening a stop without shrinking the size would raise the loss a stop-out takes, so the cap shrinks in proportion",
     "guard": "G3_size",
     "rule": "At most 5% of equity per name, 25% gross, 10% net (long minus short), and 7.5% gross across the crypto-beta names (MSTR, COIN, HOOD, CRCL). A name with a wider stop has a smaller cap, 5% x 4% / its stop, so every name loses at most 0.2% of equity at its stop."
    },
    {
     "basis": "Envelope simulation uses -4% per position (envelope_clean.py); a stop that lives on the venue survives a crashed agent, one that lives in our loop does not; policy v2 (run2-a7): the stop is placed and triggered on the Demo mark, and the Demo price of HOOD, SNDK, MSTR, CRCL and SP500 departs from live by 267 to 383 bps at the 99th percentile (validation/demo_venue), so a 4% stop could be taken out by the venue's own drift rather than by the market",
     "guard": "G4_stop",
     "rule": "Every exposure-adding order carries a venue stop 4% from entry, triggered on mark. Under run2-a7 a name's stop is the wider of 4% and twice its measured Demo-live p99 gap: HOOD 7.66%, SNDK 6.96%, MSTR 6.32%, CRCL 6.17%, SP500 5.33%."
    },
    {
     "basis": "Envelope simulation uses a -1.5% daily kill (envelope_clean.py)",
     "guard": "G5_daily_kill",
     "rule": "Book down 1.5% from the 00:00 UTC equity flattens everything; halted until the next UTC day."
    },
    {
     "basis": "Round-trip cost 12-16 bps against a measured intraday edge of ~0.00% on these perps; 360+ intraday variants were negative in the author's earlier study (not in this repository)",
     "guard": "G6_turnover",
     "rule": "At most 2 model-initiated orders per name per UTC day; no increase or side flip within 24h of the last increase unless the model declares the stated invalidation fired. Reductions are never blocked (see DESIGN.md §10.4)."
    },
    {
     "basis": "Demo taker fee 6 bps (instruments takerFeeRate 0.0006 on all 14 names, universe_probe.json); envelope expects ~11 round trips at 5% = 6.6 bps of equity per 72h; caps are about 3x that; policy v2 (run2-a8): over the nine-day window the no-edge book paid a median 6.3 and a p95 of 8.2 bps of equity in fees (validation/run2/strategy_replay.json), so the 20 bps window budget is kept",
     "guard": "G7_fee_budget",
     "rule": "No new exposure once fees paid today reach 7 bps of equity, or 20 bps over the window."
    },
    {
     "basis": "Maker fills are adversely selected: a +90% limit-fill simulation replayed at -0.45% in the author's earlier fill-model replay (not in this repository). Demo spreads measured 0.1-6.6 bps (universe_probe.json)",
     "guard": "G8_taker_only",
     "rule": "Market (taker) orders only; refuse an opening when the Demo spread exceeds 20 bps."
    },
    {
     "basis": "An unattributable figure is where an unsupported fact enters a trade. The check is ported from ARGUS agents/grounding.py (MIT); its fabricated-figure test set is re-run in this repository (tests/decision/test_grounding.py) rather than cited",
     "guard": "G9_grounding",
     "rule": "A target whose thesis, invalidation or view states a number that does not resolve to a snapshot fact within 2% may not add exposure."
    },
    {
     "basis": "2.5% is past the worst no-edge 72h drawdown (-2.48%, envelope_clean.json); losing-streak and staleness rules ported from ARGUS risk/circuit.py (MIT); a model outage never leads to a deterministic trade (DESIGN.md §9.5); policy v2 (run2-a5): the losing-streak trip lapses, since a flat book in reduce-only could never close the winner that clears it; policy v2 (run2-a6): a flatten is itself a taker trade on every leg",
     "guard": "G10_breaker",
     "rule": "Drawdown from peak 2.5% -> reduce-only; 4% -> halted; 4 losing trades in a row -> reduce-only until 24h after the last loss; a snapshot older than 15 min or a quote older than 120 s cannot open; three failed decisions in a row flatten the book; before that the legs are held under their venue stops and nothing opens."
    },
    {
     "basis": "Demo instrument limits (e.g. NVDAUSDT maxMarketOrderQty 60, minOrderQty 0.01) in universe_probe.json",
     "guard": "G11_eligibility",
     "rule": "Only the 14 universe symbols, only when Demo lists them online, only at or above minOrderQty and minOrderAmount after rounding to quantityMultiplier; orders above maxMarketOrderQty are split."
    }
   ],
   "mandate": {
    "min_horizon_hours": 24,
    "per_name_max": 0.05,
    "risk_budget_gross": 0.25,
    "text": "You manage a paper book on Bitget's Demo venue. You hold a risk budget of up to 25% of equity gross, at most 5% of equity in any one name, long or short. At every decision you either deploy some of that budget, with a written thesis and invalidation per position, or decline it in writing. Flat with reasons is a valid, counted answer. A risk kernel you do not control may shrink or refuse what you ask for; it can never add to it."
   },
   "mark_index_max_gap": 0.03,
   "max_open_spread_bps": 20.0,
   "max_rebalances_per_name_per_day": 2,
   "metrics": [
    {
     "formula": "r_t = E_t / E_{t-1} - 1 on the UTC hour grid, E = equity_book",
     "name": "hourly_return",
     "notes": "Same grid and definition as validation/demo_venue/envelope_clean.py",
     "unit": "fraction"
    },
    {
     "formula": "mean(r) / pstdev(r) * sqrt(8760)",
     "name": "sharpe_ann",
     "notes": "Population stdev, as envelope_clean.py. Undefined (None) when pstdev is 0.",
     "unit": "annualised ratio"
    },
    {
     "formula": "sqrt((1 + 0.5 * S_h^2) / n) * sqrt(8760), S_h = mean(r) / pstdev(r)",
     "name": "sharpe_se_ann",
     "notes": "Lo (2002) iid standard error; about 11 at n = 72 with S_h near 0",
     "unit": "annualised ratio"
    },
    {
     "formula": "mean(r) / sqrt(mean(min(r, 0)^2)) * sqrt(8760)",
     "name": "sortino_ann",
     "notes": "",
     "unit": "annualised ratio"
    },
    {
     "formula": "min_t (E_t / max_{s<=t} E_s - 1)",
     "name": "max_drawdown",
     "notes": "",
     "unit": "fraction (non-positive)"
    },
    {
     "formula": "count(net_pnl > 0) / count(closed trades)",
     "name": "win_rate",
     "notes": "A closed trade is one symbol from flat to flat; a flip closes one and opens one. net_pnl includes both fees. None when no trade has closed.",
     "unit": "fraction"
    },
    {
     "formula": "sum(|fill notional|) / mean(E)",
     "name": "turnover",
     "notes": "",
     "unit": "multiple of equity"
    },
    {
     "formula": "moving-block bootstrap of r, block = ceil(n^(1/3)) hours, 10,000 resamples, seed 20260924; 5th and 95th percentiles over the resamples where the statistic is defined, the share where it is not published beside the band; the Sharpe ratio of an all-zero resample is 0",
     "name": "ci90",
     "notes": "Descriptive, not inferential: 72 hourly marks cannot separate skill from luck.",
     "unit": "per metric"
    }
   ],
   "min_hold_hours": 24,
   "net_max": 0.1,
   "per_name_max": 0.05,
   "scoring_window": {
    "basis": "Run 2's nine days, from the end of the weekend freeze on Monday 2026-09-28 00:00 UTC to Wednesday 2026-10-07 00:00 UTC, after Tuesday's US close (RUNBOOK section 3), the day before the submission deadline Bitget moved to 8 October (@Bitget_AI, 2026-09-24); the handbook recommends a paper log of two weeks or more. Pre-registered here so the scored span is in the hashed policy and not only in prose, and so every trade closes and counts: at the end G2 closes every leg (run2-a4). Run 1 had no window in its record and its win rate stayed undefined with positions open.",
    "end": "2026-10-07T00:00:00Z",
    "start": "2026-09-28T00:00:00Z"
   },
   "stale_index_min_move_bps_3h": 2.0,
   "stop_gap_multiple": 2.0,
   "stop_loss_pct": 0.04,
   "stop_trigger": "mark",
   "taker_only": true,
   "triggers": {
    "basis": "Heartbeat and event list: DESIGN.md §8. Fear & Greed bands are Bitget's own (bitget-signal skills/sentiment-analyst/SKILL.md: 0-25 extreme fear, 76-100 extreme greed). Coordination constants from ARGUS agents/novelty.py (3 sources inside 2h). Live BTCUSDT funding settles every 8h (instruments fundInterval, re-read at genesis). Policy v2 (run 2, declared change run2-a1): funding_zscore is evaluated for every universe instrument, not the crypto leg alone, with the same +-2 threshold, 90-settlement lookback, 240-minute cooldown and daily cap, and the same weekend refusal for US-session legs. Basis: in run 1's 226 snapshots the live funding z-score of an equity or index perp was beyond +-2 in 436 instrument-snapshots (HOOD 88, NVDA 84, MSTR 58, GOOGL 49, AMZN 46, SNDK 44, TSLA 30, COIN 23, NDX100 14) and never woke the model (validation/run2/run1_trigger_replay.json). Policy v2 (run2-a2): the live rate must also be at least 7.5 bp in absolute value. Half of run 1's live rates were exactly zero, so the z-score alone was beyond +-2 in 13.8% of instrument-snapshots and would have woken 15 event decisions in 19.9 hours; with the level, 43 extremes (MSTR 29, SNDK 14) and 3 decisions.",
    "cooldown_minutes": 240,
    "coordinated_min_sources": 3,
    "coordinated_window_minutes": 120,
    "earnings_lookahead_hours": 24,
    "fear_greed_high": 76,
    "fear_greed_low": 25,
    "funding_abs_min": 0.00075,
    "funding_heartbeat_hours_utc": [
     0,
     8,
     16
    ],
    "funding_z_asset_classes": [
     "crypto",
     "us_equity",
     "us_index"
    ],
    "funding_z_lookback_settlements": 90,
    "funding_z_threshold": 2.0,
    "max_event_decisions_per_day": 8,
    "oi_jump_lookback_days": 30,
    "oi_jump_quantile": 0.99,
    "us_open_local": "09:30"
   },
   "universe": [
    {
     "asset_class": "crypto",
     "basis": "p99 of |Demo close / live close - 1| over the Demo history, 1H candles (validation/demo_venue/demo_integrity.json); 0 mark-vs-index excursions over 3% in the last 30 days",
     "demo_live_gap_p99_bps": 8.3,
     "symbol": "BTCUSDT"
    },
    {
     "asset_class": "us_index",
     "basis": "p99 of |Demo close / live close - 1| over the Demo history, 1H candles (validation/demo_venue/demo_integrity.json); listed on Demo 2026-09-03",
     "demo_live_gap_p99_bps": 266.5,
     "symbol": "SP500USDT"
    },
    {
     "asset_class": "us_index",
     "basis": "p99 of |Demo close / live close - 1| over the Demo history, 1H candles (validation/demo_venue/demo_integrity.json); listed on Demo 2026-09-03",
     "demo_live_gap_p99_bps": 63.2,
     "symbol": "NDX100USDT"
    },
    {
     "asset_class": "us_equity",
     "basis": "p99 of |Demo close / live close - 1| over the Demo history, 1H candles (validation/demo_venue/fade_demo.json (tracking)); 0 mark-vs-index excursions over 3% since Demo listing 2026-08-25",
     "demo_live_gap_p99_bps": 316.2,
     "symbol": "MSTRUSDT"
    },
    {
     "asset_class": "us_equity",
     "basis": "p99 of |Demo close / live close - 1| over the Demo history, 1H candles (validation/demo_venue/fade_demo.json (tracking)); 0 mark-vs-index excursions over 3% since Demo listing 2026-08-25",
     "demo_live_gap_p99_bps": 383.0,
     "symbol": "HOODUSDT"
    },
    {
     "asset_class": "us_equity",
     "basis": "p99 of |Demo close / live close - 1| over the Demo history, 1H candles (validation/demo_venue/fade_demo.json (tracking)); 0 mark-vs-index excursions over 3% since Demo listing 2026-08-25",
     "demo_live_gap_p99_bps": 308.7,
     "symbol": "CRCLUSDT"
    },
    {
     "asset_class": "us_equity",
     "basis": "p99 of |Demo close / live close - 1| over the Demo history, 1H candles (validation/demo_venue/fade_demo.json (tracking)); 0 mark-vs-index excursions over 3% since Demo listing 2026-08-25",
     "demo_live_gap_p99_bps": 348.2,
     "symbol": "SNDKUSDT"
    },
    {
     "asset_class": "us_equity",
     "basis": "p99 of |Demo close / live close - 1| over the Demo history, 1H candles (validation/demo_venue/fade_demo.json (tracking)); 0 mark-vs-index excursions over 3% since Demo listing 2026-08-25",
     "demo_live_gap_p99_bps": 165.0,
     "symbol": "COINUSDT"
    },
    {
     "asset_class": "us_equity",
     "basis": "p99 of |Demo close / live close - 1| over the Demo history, 1H candles (validation/demo_venue/fade_demo.json (tracking)); 0 mark-vs-index excursions over 3% since Demo listing 2026-08-25",
     "demo_live_gap_p99_bps": 113.7,
     "symbol": "TSLAUSDT"
    },
    {
     "asset_class": "us_equity",
     "basis": "p99 of |Demo close / live close - 1| over the Demo history, 1H candles (validation/demo_venue/fade_demo.json (tracking)); 0 mark-vs-index excursions over 3% since Demo listing 2026-08-25",
     "demo_live_gap_p99_bps": 104.7,
     "symbol": "GOOGLUSDT"
    },
    {
     "asset_class": "us_equity",
     "basis": "p99 of |Demo close / live close - 1| over the Demo history, 1H candles (validation/demo_venue/fade_demo.json (tracking)); 0 mark-vs-index excursions over 3% since Demo listing 2026-08-25",
     "demo_live_gap_p99_bps": 67.2,
     "symbol": "METAUSDT"
    },
    {
     "asset_class": "us_equity",
     "basis": "p99 of |Demo close / live close - 1| over the Demo history, 1H candles (validation/demo_venue/fade_demo.json (tracking)); 0 mark-vs-index excursions over 3% since Demo listing 2026-08-25",
     "demo_live_gap_p99_bps": 129.9,
     "symbol": "NVDAUSDT"
    },
    {
     "asset_class": "us_equity",
     "basis": "p99 of |Demo close / live close - 1| over the Demo history, 1H candles (validation/demo_venue/fade_demo.json (tracking)); 0 mark-vs-index excursions over 3% since Demo listing 2026-08-25",
     "demo_live_gap_p99_bps": 92.0,
     "symbol": "AMZNUSDT"
    },
    {
     "asset_class": "us_equity",
     "basis": "p99 of |Demo close / live close - 1| over the Demo history, 1H candles (validation/demo_venue/fade_demo.json (tracking)); 0 mark-vs-index excursions over 3% since Demo listing 2026-08-25",
     "demo_live_gap_p99_bps": 82.3,
     "symbol": "AAPLUSDT"
    }
   ],
   "version": "policy-v2",
   "weekend": {
    "basis": "Same window as the envelope simulation's tradable() (envelope_clean.py); the Demo freeze itself is in weekend_vol.json",
    "freeze_hour": 20,
    "freeze_weekday": 4,
    "no_open_buffer_hours": 2.0,
    "preflatten_minutes": 15,
    "reopen_hour": 0,
    "reopen_weekday": 0
   }
  },
  "policy_hash": "eaee6d5a27f608db9923e870fdd23f786925b92a2e11d440c5c7b37aeb93689e",
  "predecessor": {
   "code_commit": "3059fcf473927093af35d9af4c999c6d135b331a",
   "genesis_hash": "ebbf607a8fe199bccc0f612ededf2a6902e4884a38d21fbfea498dff7da453a5",
   "policy_hash": "55ff779b5d6f2c55082be1b4db6c9fde53865213cade58232f64f372ad73aeaf",
   "policy_version": "policy-v1",
   "window": "2026-09-24T17:06Z to 2026-09-27T17:06Z (72 hours)"
  },
  "project": "t2-sentiment-agent",
  "prompt_hashes": {
   "decision/prompt.py": "74c6f697c809c6c4a89e01f8cdcbdda1df4c7c76182f9b5838345dcc2ade6ca0",
   "decision/prompts/output_schema_v1.md": "9675a8b18c2ee0a1371d95ae13b9c7dda2284473f687376a38f4a78768aa373a",
   "decision/prompts/system_v2.md": "3a96252827c4d2872703387dc625e1338817099cafaa6f96a7b6eac39658cf13"
  },
  "qwen_model": "qwen3.8-max",
  "statement": "Pre-registration of the t2-sentiment-agent paper run on Bitget UTA Demo, written before its first paper order. It fixes the policy (every guard, limit and threshold, each with its measured basis), the prompt files, the universe, the metric definitions, the expected no-edge envelope, the code commit, the dependency locks, the Agent Hub CLI package and the Qwen model. Any change after this event is a logged, owner-confirmed amendment in this ledger, and thresholds are not tuned inside the window. Results are reported against the envelope with n and a block-bootstrap interval, labelled descriptive, not inferential.",
  "universe": [
   "BTCUSDT",
   "SP500USDT",
   "NDX100USDT",
   "MSTRUSDT",
   "HOODUSDT",
   "CRCLUSDT",
   "SNDKUSDT",
   "COINUSDT",
   "TSLAUSDT",
   "GOOGLUSDT",
   "METAUSDT",
   "NVDAUSDT",
   "AMZNUSDT",
   "AAPLUSDT"
  ]
 },
 "anchors": [
  {
   "seq": 1,
   "record": {
    "detail": "not anchored: ots stamp exited 1: if '/' in name or '\\\\' in name: | ^^^^^^^^^^^ | TypeError: argument of type 'NoneType' is not iterable",
    "ots_blob": null,
    "status": "failed",
    "submitted_at": "2026-09-28T00:07:44.456237Z",
    "target_hash": "fb66b6bf40e9229407cdd62266cfe180994c38eb96f18d5c8bca983323ccd3c0",
    "target_seq": 0
   }
  },
  {
   "seq": 1371,
   "record": {
    "detail": "pending in 4 calendar(s): https://alice.btc.calendar.opentimestamps.org, https://btc.calendar.catallaxy.com, https://finney.calendar.eternitywall.com, https://bob.btc.calendar.opentimestamps.org. A Bitcoin attestation follows once a block confirms the calendars' aggregate (typically hours); ots upgrade collects it. Check with: ots verify -d d6be2cdf58af3974d4ebfdcb6842a5a0638c3d57a22732ab359e8ea9895ba750 <proof>.ots",
    "ots_blob": {
     "media_type": "application/vnd.opentimestamps.v1",
     "sha256": "798fb13b535a0a115b6bc3bb44695b470b2f6c021ac28b3a36946c6bafac027d",
     "size": 735
    },
    "status": "submitted",
    "submitted_at": "2026-09-29T00:11:55.319940Z",
    "target_hash": "d6be2cdf58af3974d4ebfdcb6842a5a0638c3d57a22732ab359e8ea9895ba750",
    "target_seq": 1370
   }
  },
  {
   "seq": 1475,
   "record": {
    "detail": "complete as of 2026-09-29T06:12:17.421992Z: the proof carries a Bitcoin attestation at block 969072, 969093, 969098. It is not verified against Bitcoin here; check with: ots verify -d d6be2cdf58af3974d4ebfdcb6842a5a0638c3d57a22732ab359e8ea9895ba750 <proof>.ots",
    "ots_blob": {
     "media_type": "application/vnd.opentimestamps.v1",
     "sha256": "4048166eb861a4f7151143e5c5abbf0454ae8855197ad879628814a25f2c316c",
     "size": 3880
    },
    "status": "upgraded",
    "submitted_at": "2026-09-29T00:11:55.319940Z",
    "target_hash": "d6be2cdf58af3974d4ebfdcb6842a5a0638c3d57a22732ab359e8ea9895ba750",
    "target_seq": 1370
   }
  },
  {
   "seq": 1971,
   "record": {
    "detail": "pending in 4 calendar(s): https://btc.calendar.catallaxy.com, https://finney.calendar.eternitywall.com, https://bob.btc.calendar.opentimestamps.org, https://alice.btc.calendar.opentimestamps.org. A Bitcoin attestation follows once a block confirms the calendars' aggregate (typically hours); ots upgrade collects it. Check with: ots verify -d 147561dbda117b01f962269621eacc767f6717ff1d725f2d1aa98a659496c6d6 <proof>.ots",
    "ots_blob": {
     "media_type": "application/vnd.opentimestamps.v1",
     "sha256": "33e3ea0b97aa216d87c79164feae5e431ef5ea7e057e12003118245f87f7f688",
     "size": 665
    },
    "status": "submitted",
    "submitted_at": "2026-09-30T00:10:36.694393Z",
    "target_hash": "147561dbda117b01f962269621eacc767f6717ff1d725f2d1aa98a659496c6d6",
    "target_seq": 1970
   }
  },
  {
   "seq": 2095,
   "record": {
    "detail": "complete as of 2026-09-30T06:17:47.709822Z: the proof carries a Bitcoin attestation at block 969230, 969244, 969251. It is not verified against Bitcoin here; check with: ots verify -d 147561dbda117b01f962269621eacc767f6717ff1d725f2d1aa98a659496c6d6 <proof>.ots",
    "ots_blob": {
     "media_type": "application/vnd.opentimestamps.v1",
     "sha256": "0b4a92d61cdc257cded44390082b8decf36c2c94fba13ff9fe63b97378b37ed8",
     "size": 3809
    },
    "status": "upgraded",
    "submitted_at": "2026-09-30T00:10:36.694393Z",
    "target_hash": "147561dbda117b01f962269621eacc767f6717ff1d725f2d1aa98a659496c6d6",
    "target_seq": 1970
   }
  },
  {
   "seq": 2550,
   "record": {
    "detail": "pending in 4 calendar(s): https://alice.btc.calendar.opentimestamps.org, https://finney.calendar.eternitywall.com, https://btc.calendar.catallaxy.com, https://bob.btc.calendar.opentimestamps.org. A Bitcoin attestation follows once a block confirms the calendars' aggregate (typically hours); ots upgrade collects it. Check with: ots verify -d 906db8fe83870a7c34949b0c59d363a8edac1f80cfc96aa5d918aff2dda50a8c <proof>.ots",
    "ots_blob": {
     "media_type": "application/vnd.opentimestamps.v1",
     "sha256": "0a31313b4b8b18e410e2d22b01b92ce6fc9957076856bcbf9b90d60d0b78f542",
     "size": 840
    },
    "status": "submitted",
    "submitted_at": "2026-10-01T00:10:32.054924Z",
    "target_hash": "906db8fe83870a7c34949b0c59d363a8edac1f80cfc96aa5d918aff2dda50a8c",
    "target_seq": 2549
   }
  },
  {
   "seq": 2700,
   "record": {
    "detail": "complete as of 2026-10-01T06:19:29.694874Z: the proof carries a Bitcoin attestation at block 969373, 969375, 969398. It is not verified against Bitcoin here; check with: ots verify -d 906db8fe83870a7c34949b0c59d363a8edac1f80cfc96aa5d918aff2dda50a8c <proof>.ots",
    "ots_blob": {
     "media_type": "application/vnd.opentimestamps.v1",
     "sha256": "9640c794da98dbfa869ccdcf7df084f4d53ddad53d3e681ad80f881a115d1091",
     "size": 3950
    },
    "status": "upgraded",
    "submitted_at": "2026-10-01T00:10:32.054924Z",
    "target_hash": "906db8fe83870a7c34949b0c59d363a8edac1f80cfc96aa5d918aff2dda50a8c",
    "target_seq": 2549
   }
  },
  {
   "seq": 3102,
   "record": {
    "detail": "pending in 4 calendar(s): https://bob.btc.calendar.opentimestamps.org, https://alice.btc.calendar.opentimestamps.org, https://btc.calendar.catallaxy.com, https://finney.calendar.eternitywall.com. A Bitcoin attestation follows once a block confirms the calendars' aggregate (typically hours); ots upgrade collects it. Check with: ots verify -d ee90ea08cc0f4c834d0e301789e751c936c827900e4c71ccbbc5c89043e2b035 <proof>.ots",
    "ots_blob": {
     "media_type": "application/vnd.opentimestamps.v1",
     "sha256": "b5a7a66e48d0af113ffa245c05514fd3907ec8df58eca9756dbc9a6a00de3f5e",
     "size": 770
    },
    "status": "submitted",
    "submitted_at": "2026-10-02T00:17:15.745815Z",
    "target_hash": "ee90ea08cc0f4c834d0e301789e751c936c827900e4c71ccbbc5c89043e2b035",
    "target_seq": 3101
   }
  },
  {
   "seq": 3272,
   "record": {
    "detail": "complete as of 2026-10-02T06:20:57.959186Z: the proof carries a Bitcoin attestation at block 969508, 969510. It is not verified against Bitcoin here; check with: ots verify -d ee90ea08cc0f4c834d0e301789e751c936c827900e4c71ccbbc5c89043e2b035 <proof>.ots",
    "ots_blob": {
     "media_type": "application/vnd.opentimestamps.v1",
     "sha256": "60866f2fdf3b54a712896e644e8c92d12a9a15164808dcff5b1fa2231075ad86",
     "size": 2808
    },
    "status": "upgraded",
    "submitted_at": "2026-10-02T00:17:15.745815Z",
    "target_hash": "ee90ea08cc0f4c834d0e301789e751c936c827900e4c71ccbbc5c89043e2b035",
    "target_seq": 3101
   }
  },
  {
   "seq": 3655,
   "record": {
    "detail": "pending in 4 calendar(s): https://btc.calendar.catallaxy.com, https://bob.btc.calendar.opentimestamps.org, https://finney.calendar.eternitywall.com, https://alice.btc.calendar.opentimestamps.org. A Bitcoin attestation follows once a block confirms the calendars' aggregate (typically hours); ots upgrade collects it. Check with: ots verify -d a2ecdf1866e446b27b4b148c52dbc867e9c80d489b173f545a4469065b8e7f2b <proof>.ots",
    "ots_blob": {
     "media_type": "application/vnd.opentimestamps.v1",
     "sha256": "61ed95664ed2582495c6f70a94295c4f7229f1b07189243af2f912e3da60028d",
     "size": 875
    },
    "status": "submitted",
    "submitted_at": "2026-10-03T00:11:18.164146Z",
    "target_hash": "a2ecdf1866e446b27b4b148c52dbc867e9c80d489b173f545a4469065b8e7f2b",
    "target_seq": 3654
   }
  },
  {
   "seq": 3763,
   "record": {
    "detail": "complete as of 2026-10-03T06:25:37.724923Z: the proof carries a Bitcoin attestation at block 969650, 969658, 969668. It is not verified against Bitcoin here; check with: ots verify -d a2ecdf1866e446b27b4b148c52dbc867e9c80d489b173f545a4469065b8e7f2b <proof>.ots",
    "ots_blob": {
     "media_type": "application/vnd.opentimestamps.v1",
     "sha256": "363fcf3e0b883d1963d408fca841c79b8f31b1376ce35d2d31c9f4acb65c806a",
     "size": 4126
    },
    "status": "upgraded",
    "submitted_at": "2026-10-03T00:11:18.164146Z",
    "target_hash": "a2ecdf1866e446b27b4b148c52dbc867e9c80d489b173f545a4469065b8e7f2b",
    "target_seq": 3654
   }
  },
  {
   "seq": 4068,
   "record": {
    "detail": "pending in 4 calendar(s): https://bob.btc.calendar.opentimestamps.org, https://finney.calendar.eternitywall.com, https://btc.calendar.catallaxy.com, https://alice.btc.calendar.opentimestamps.org. A Bitcoin attestation follows once a block confirms the calendars' aggregate (typically hours); ots upgrade collects it. Check with: ots verify -d 3e80dbc4fb9a07b3de2e5524b66d8151752dea30e115c888b755e3ba83aa29b2 <proof>.ots",
    "ots_blob": {
     "media_type": "application/vnd.opentimestamps.v1",
     "sha256": "38e42c6b64e2cd51819963d4cfef4929d67b21adcaf68a0250ef3319a5821657",
     "size": 805
    },
    "status": "submitted",
    "submitted_at": "2026-10-04T00:20:19.942798Z",
    "target_hash": "3e80dbc4fb9a07b3de2e5524b66d8151752dea30e115c888b755e3ba83aa29b2",
    "target_seq": 4067
   }
  },
  {
   "seq": 4171,
   "record": {
    "detail": "complete as of 2026-10-04T06:34:25.207673Z: the proof carries a Bitcoin attestation at block 969783, 969789. It is not verified against Bitcoin here; check with: ots verify -d 3e80dbc4fb9a07b3de2e5524b66d8151752dea30e115c888b755e3ba83aa29b2 <proof>.ots",
    "ots_blob": {
     "media_type": "application/vnd.opentimestamps.v1",
     "sha256": "1c00dbdc8bd8efba327d7dd89c839baa1dd2c7e97a8e05d26aa29c683da6c324",
     "size": 2844
    },
    "status": "upgraded",
    "submitted_at": "2026-10-04T00:20:19.942798Z",
    "target_hash": "3e80dbc4fb9a07b3de2e5524b66d8151752dea30e115c888b755e3ba83aa29b2",
    "target_seq": 4067
   }
  },
  {
   "seq": 4464,
   "record": {
    "detail": "pending in 4 calendar(s): https://finney.calendar.eternitywall.com, https://alice.btc.calendar.opentimestamps.org, https://bob.btc.calendar.opentimestamps.org, https://btc.calendar.catallaxy.com. A Bitcoin attestation follows once a block confirms the calendars' aggregate (typically hours); ots upgrade collects it. Check with: ots verify -d 62d455606038b3a832f6075af6049b16c9e881d0cfac97ddc7c8c96dbd37ab0b <proof>.ots",
    "ots_blob": {
     "media_type": "application/vnd.opentimestamps.v1",
     "sha256": "377dd2ad458374d1cb4227357707ff2951063fa949735649c57385dd8fba1f21",
     "size": 700
    },
    "status": "submitted",
    "submitted_at": "2026-10-05T00:22:02.055767Z",
    "target_hash": "62d455606038b3a832f6075af6049b16c9e881d0cfac97ddc7c8c96dbd37ab0b",
    "target_seq": 4463
   }
  },
  {
   "seq": 4543,
   "record": {
    "detail": "complete as of 2026-10-05T05:15:17.403727Z: the proof carries a Bitcoin attestation at block 969926, 969929, 969942. It is not verified against Bitcoin here; check with: ots verify -d 62d455606038b3a832f6075af6049b16c9e881d0cfac97ddc7c8c96dbd37ab0b <proof>.ots",
    "ots_blob": {
     "media_type": "application/vnd.opentimestamps.v1",
     "sha256": "edabaf7aafff685bced99a73510dc20f50c29819d0833a96d62aa120d14f516c",
     "size": 3706
    },
    "status": "upgraded",
    "submitted_at": "2026-10-05T00:22:02.055767Z",
    "target_hash": "62d455606038b3a832f6075af6049b16c9e881d0cfac97ddc7c8c96dbd37ab0b",
    "target_seq": 4463
   }
  },
  {
   "seq": 4651,
   "record": {
    "detail": "pending in 4 calendar(s): https://bob.btc.calendar.opentimestamps.org, https://btc.calendar.catallaxy.com, https://finney.calendar.eternitywall.com, https://alice.btc.calendar.opentimestamps.org. A Bitcoin attestation follows once a block confirms the calendars' aggregate (typically hours); ots upgrade collects it. Check with: ots verify -d ca1374f0366ff737ff2be0bad296049b4dbef3a16b5ebef6e2af41f3a4287d28 <proof>.ots",
    "ots_blob": {
     "media_type": "application/vnd.opentimestamps.v1",
     "sha256": "fe8619acc84466f3359089dc9adf214106a79e75024b0a661e838811cfbef0fa",
     "size": 875
    },
    "status": "submitted",
    "submitted_at": "2026-10-06T15:04:13.196365Z",
    "target_hash": "ca1374f0366ff737ff2be0bad296049b4dbef3a16b5ebef6e2af41f3a4287d28",
    "target_seq": 4650
   }
  },
  {
   "seq": 4756,
   "record": {
    "detail": "complete as of 2026-10-06T21:06:48.484835Z: the proof carries a Bitcoin attestation at block 970191, 970194, 970214. It is not verified against Bitcoin here; check with: ots verify -d ca1374f0366ff737ff2be0bad296049b4dbef3a16b5ebef6e2af41f3a4287d28 <proof>.ots",
    "ots_blob": {
     "media_type": "application/vnd.opentimestamps.v1",
     "sha256": "f67e45c7ca6b09346d91fb582cd3e59f41f1c371d128b098161899e45abccc10",
     "size": 3986
    },
    "status": "upgraded",
    "submitted_at": "2026-10-06T15:04:13.196365Z",
    "target_hash": "ca1374f0366ff737ff2be0bad296049b4dbef3a16b5ebef6e2af41f3a4287d28",
    "target_seq": 4650
   }
  },
  {
   "seq": 4830,
   "record": {
    "detail": "pending in 4 calendar(s): https://btc.calendar.catallaxy.com, https://finney.calendar.eternitywall.com, https://alice.btc.calendar.opentimestamps.org, https://bob.btc.calendar.opentimestamps.org. A Bitcoin attestation follows once a block confirms the calendars' aggregate (typically hours); ots upgrade collects it. Check with: ots verify -d 1d7e02da74dfe0f353d36543d99b605f97923719c915b4b5cf5cf6aafdc3f7e2 <proof>.ots",
    "ots_blob": {
     "media_type": "application/vnd.opentimestamps.v1",
     "sha256": "df7fac319fa270c846569c801a8e245d20870ad64b0fd3bc71a309499d2a4b5f",
     "size": 840
    },
    "status": "submitted",
    "submitted_at": "2026-10-07T00:39:07.457666Z",
    "target_hash": "1d7e02da74dfe0f353d36543d99b605f97923719c915b4b5cf5cf6aafdc3f7e2",
    "target_seq": 4829
   }
  },
  {
   "seq": 4870,
   "record": {
    "detail": "complete as of 2026-10-07T03:01:05.200927Z: the proof carries a Bitcoin attestation at block 970264, 970266. It is not verified against Bitcoin here; check with: ots verify -d 1d7e02da74dfe0f353d36543d99b605f97923719c915b4b5cf5cf6aafdc3f7e2 <proof>.ots",
    "ots_blob": {
     "media_type": "application/vnd.opentimestamps.v1",
     "sha256": "6165326cf37a8efd03c2d7441f97d733bc26bd6c3f82dda950169c7925cfc218",
     "size": 2845
    },
    "status": "upgraded",
    "submitted_at": "2026-10-07T00:39:07.457666Z",
    "target_hash": "1d7e02da74dfe0f353d36543d99b605f97923719c915b4b5cf5cf6aafdc3f7e2",
    "target_seq": 4829
   }
  },
  {
   "seq": 5188,
   "record": {
    "detail": "pending in 4 calendar(s): https://btc.calendar.catallaxy.com, https://finney.calendar.eternitywall.com, https://alice.btc.calendar.opentimestamps.org, https://bob.btc.calendar.opentimestamps.org. A Bitcoin attestation follows once a block confirms the calendars' aggregate (typically hours); ots upgrade collects it. Check with: ots verify -d 906b3b6ba1d9d0d3b77d102794344ef9fd136712bafe7cabf8491bdcb721b627 <proof>.ots",
    "ots_blob": {
     "media_type": "application/vnd.opentimestamps.v1",
     "sha256": "8588a9e9623db7a96871df7aea7a59410d1aa1f6afe41be76ad1800214775012",
     "size": 840
    },
    "status": "submitted",
    "submitted_at": "2026-10-08T00:16:29.531572Z",
    "target_hash": "906b3b6ba1d9d0d3b77d102794344ef9fd136712bafe7cabf8491bdcb721b627",
    "target_seq": 5187
   }
  }
 ],
 "genesis_anchors": [
  {
   "seq": 1,
   "record": {
    "detail": "not anchored: ots stamp exited 1: if '/' in name or '\\\\' in name: | ^^^^^^^^^^^ | TypeError: argument of type 'NoneType' is not iterable",
    "ots_blob": null,
    "status": "failed",
    "submitted_at": "2026-09-28T00:07:44.456237Z",
    "target_hash": "fb66b6bf40e9229407cdd62266cfe180994c38eb96f18d5c8bca983323ccd3c0",
    "target_seq": 0
   }
  }
 ],
 "amendments": [],
 "x_post_text": "t2-sentiment-agent: a Market Sentiment Agent paper-trading on Bitget Demo. Qwen decides, a reduce-only risk kernel gates, Agent Hub executes.\n\nGenesis hash:\nfb66b6bf40e9229407cdd62266cfe180994c38eb96f18d5c8bca983323ccd3c0\n\n#BitgetHackathon @Bitget_AI\nhttps://x.com/Bitget_AI/status/2100519318824055159",
 "x_post": null
}
