# AlphaBot V2 0.2.0 — complete unredacted Python source

Four runtime files and two reproducible test suites. All runtime files must remain together. MIT licensed; see plugin LICENSE. 60 local tests passed. Live gateway integration and browser-render verification remain unverified. Synthetic demonstration results are not historical or live market results.

## scripts/audit_ledger.py

```python
# SPDX-License-Identifier: MIT
"""Local Decimal journal. Entry fees are capitalized in inventory, never expensed twice."""
from decimal import Decimal, localcontext, getcontext
from datetime import datetime, timezone
import hashlib
import json

getcontext().prec = 50
D = Decimal
ZERO = D('0')


def dec(value):
    result = D(str(value))
    if not result.is_finite():
        raise ValueError('nonfinite ledger value')
    return result


def fixed(value):
    if isinstance(value, datetime):
        return value.astimezone(timezone.utc).isoformat(timespec='microseconds')
    if isinstance(value, D):
        return format(value, 'f')
    if isinstance(value, dict):
        return {str(k): fixed(v) for k, v in value.items()}
    if isinstance(value, (list, tuple)):
        return [fixed(v) for v in value]
    return value


def canonical(value):
    return json.dumps(fixed(value), sort_keys=True, separators=(',', ':'), allow_nan=False)


def digest(previous, body):
    return hashlib.sha256((previous + '\n' + canonical(body)).encode()).hexdigest()


def totals(positions):
    return (sum((dec(p['cost']) for p in positions), ZERO),
            sum((dec(p['quantity']) * dec(p['price']) for p in positions), ZERO))


class Ledger:
    def __init__(self, db, config):
        self.db, self.config = db, config
        self.threshold = dec(config.get('target_deviation_pct', '0.0001'))
        self.decay = dec(config.get('lesson_risk_decay', '0.95'))
        if self.threshold != D('0.0001') or not ZERO < self.decay < 1:
            raise ValueError('invalid lesson threshold/decay')
        db.execute('PRAGMA foreign_keys=ON')
        db.executescript('''
        CREATE TABLE IF NOT EXISTS frames(
          id INTEGER PRIMARY KEY,at TEXT NOT NULL,kind TEXT NOT NULL,action TEXT NOT NULL,
          body TEXT NOT NULL,previous_hash TEXT NOT NULL,hash TEXT NOT NULL UNIQUE);
        CREATE INDEX IF NOT EXISTS frames_at ON frames(at,id);
        CREATE TABLE IF NOT EXISTS journal_lines(
          frame_id INTEGER NOT NULL,seq INTEGER NOT NULL,account TEXT NOT NULL,
          debit TEXT NOT NULL,credit TEXT NOT NULL,memo TEXT NOT NULL,
          PRIMARY KEY(frame_id,seq),FOREIGN KEY(frame_id) REFERENCES frames(id));
        CREATE TABLE IF NOT EXISTS execution_receipts(
          execution_id TEXT PRIMARY KEY,frame_id INTEGER NOT NULL,body TEXT NOT NULL,
          FOREIGN KEY(frame_id) REFERENCES frames(id));
        CREATE TABLE IF NOT EXISTS lessons(
          frame_id INTEGER NOT NULL,seq INTEGER NOT NULL,body TEXT NOT NULL,
          PRIMARY KEY(frame_id,seq),FOREIGN KEY(frame_id) REFERENCES frames(id));
        ''')
        for table in ('frames', 'journal_lines', 'lessons', 'execution_receipts'):
            for operation in ('UPDATE', 'DELETE'):
                db.execute(f"CREATE TRIGGER IF NOT EXISTS immutable_{table}_{operation} "
                           f"BEFORE {operation} ON {table} BEGIN "
                           "SELECT RAISE(ABORT,'append-only audit table'); END")
        db.commit()

    def count(self):
        return self.db.execute('SELECT count(*) FROM lessons').fetchone()[0]

    def multiplier(self):
        with localcontext() as ctx:
            ctx.prec = 50
            return self.decay ** self.count()

    def record(self, at, symbol, price, before, after, action, reasons, context,
               executions=(), kind='PRICE', failures=()):
        """One atomic frame+postings+lessons transaction; no credential fields accepted."""
        when = datetime.fromisoformat(at.replace('Z', '+00:00'))
        if when.tzinfo is None:
            raise ValueError('frame timestamp requires timezone')
        at = when.astimezone(timezone.utc).isoformat(timespec='microseconds')
        last = self.db.execute('SELECT id,body,hash FROM frames ORDER BY id DESC LIMIT 1').fetchone()
        previous_body = json.loads(last[1]) if last else None
        previous_hash = last[2] if last else '0' * 64
        prior = previous_body['after'] if previous_body else before
        lines, lessons = [], []
        count = self.count()
        prior_lesson = self.db.execute('SELECT body FROM lessons ORDER BY frame_id DESC,seq DESC LIMIT 1').fetchone()
        parent_lesson_hash = json.loads(prior_lesson[0]).get('lesson_hash') if prior_lesson else None
        def post(account, signed_debit, memo):
            amount = dec(signed_debit)
            lines.append({'account': account, 'debit': max(amount, ZERO),
                          'credit': max(-amount, ZERO), 'memo': memo})
        def lesson(code, stage, expected, actual, details):
            nonlocal parent_lesson_hash
            item = {'code': code, 'stage': stage, 'expected': expected, 'actual': actual,
                'details': details, 'prior_lesson_count': count + len(lessons),
                'parent_frame_id': last[0] if last else None,
                'causality': 'observed execution/reconciliation path; root cause not inferred',
                'parent_lesson_hash': parent_lesson_hash, 'risk_decay': self.decay,
                'risk_formula': 'decay**cumulative_lesson_count',
                'risk_multiplier_before': self.decay ** (count + len(lessons)),
                'risk_multiplier_after': self.decay ** (count + len(lessons) + 1)}
            item['lesson_hash'] = hashlib.sha256(canonical(item).encode()).hexdigest()
            parent_lesson_hash = item['lesson_hash']
            lessons.append(item)
        cash0, cash1 = dec(prior['cash']), dec(after['cash'])
        cost0, value0 = totals(prior['positions'])
        cost1, value1 = totals(after['positions'])
        if not last:
            baseline_equity = cash0 + value0
            post('Assets:Cash', cash0, 'Opening instrumented baseline; no earlier history reconstructed')
            post('Assets:Inventory', cost0, 'Opening fee-inclusive acquisition cost')
            post('Assets:ValuationAdjustment', value0 - cost0, 'Opening market valuation')
            post('Equity:Opening', -baseline_equity, 'Opening net assets')
        expected_cash, expected_cost = cash0, cost0
        fee_drag = slip_drag = ZERO
        for execution in executions:
            side = execution['side']
            quantity, fill, fee = (dec(execution[k]) for k in ('quantity', 'price', 'fee'))
            reference = dec(execution['reference_price'])
            if quantity <= 0 or fill <= 0 or fee < 0 or reference <= 0:
                raise ValueError('invalid execution metrics')
            notional = quantity * fill
            fee_drag += fee
            slip_drag += abs(fill - reference) * quantity
            if side == 'BUY':
                debit = notional + fee
                post('Assets:Inventory', debit, 'BUY: notional plus capitalized entry fee')
                post('Assets:Cash', -debit, 'BUY: cash debit including fee')
                expected_cash -= debit
                expected_cost += debit
            elif side == 'SELL':
                released = dec(execution['released_cost'])
                if released < 0:
                    raise ValueError('negative released acquisition cost')
                net = notional - fee
                post('Assets:Cash', net, 'SELL: net cash proceeds')
                post('Assets:Inventory', -released, 'SELL: derecognize fee-inclusive acquisition cost')
                post('Income:RealizedPnL', -(net - released), 'SELL: net proceeds minus released cost')
                expected_cash += net
                expected_cost -= released
            else:
                raise ValueError('unsupported execution side')
            target = reference * dec(self.config.get('execution_target_multiplier', '1'))
            if target <= 0:
                raise ValueError('nonpositive execution target')
            deviation = abs(fill - target) / target * 100
            if deviation >= self.threshold:
                lesson('TARGET_DEVIATION', 'execution_fill_vs_reference', target, fill,
                       {'execution_id': execution['id'], 'side': side,
                        'formula': 'abs(fill-target)/target*100', 'deviation_pct': deviation,
                        'threshold_pct': self.threshold, 'source': execution['source']})
        mismatch_cash, mismatch_cost = cash1 - expected_cash, cost1 - expected_cost
        unresolved = bool(mismatch_cash or mismatch_cost)
        if unresolved:
            # Explicit suspense is not a fabricated trade or income classification.
            post('Assets:Cash', mismatch_cash, 'UNCLASSIFIED cash reconciliation difference')
            post('Assets:Inventory', mismatch_cost, 'UNCLASSIFIED acquisition-cost difference')
            post('Equity:UnclassifiedSuspense', -(mismatch_cash + mismatch_cost),
                 'UNRESOLVED: trading must halt; administrator reconciliation required')
            lesson('UNCLASSIFIED_MOVEMENT', 'account_reconciliation',
                   {'cash': expected_cash, 'cost': expected_cost},
                   {'cash': cash1, 'cost': cost1}, 'Unexplained balance changes; no economic cause inferred')
        valuation_delta = (value1 - cost1) - (value0 - cost0)
        post('Assets:ValuationAdjustment', valuation_delta, 'Mark holdings to recorded slice prices')
        post('Income:UnrealizedPnL', -valuation_delta, 'Offset mark-to-market valuation movement')
        for failure in failures:
            lesson(failure['code'], failure['stage'], failure.get('expected'),
                   failure.get('actual'), failure.get('details'))
        debit_total = sum((line['debit'] for line in lines), ZERO)
        credit_total = sum((line['credit'] for line in lines), ZERO)
        if debit_total != credit_total:
            raise ValueError('unbalanced journal; refusing frame commit')
        n = count + len(lessons)
        with localcontext() as ctx:
            ctx.prec = 50
            multiplier_after = self.decay ** n
        body = fixed({'at': at, 'symbol': symbol, 'price': dec(price), 'kind': kind,
            'action': action, 'reasons': list(reasons), 'context': context,
            'before': before, 'after': after, 'executions': list(executions),
            'fee_drag': fee_drag, 'slippage_drag': slip_drag,
            'fee_drag_cumulative': dec(previous_body['fee_drag_cumulative']) + fee_drag if last else fee_drag,
            'lesson_count_before': count, 'lesson_count': n,
            'risk_multiplier_before': self.multiplier(), 'risk_multiplier_after': multiplier_after,
            'journal': lines, 'lessons': lessons, 'debit_total': debit_total,
            'credit_total': credit_total, 'unresolved_reconciliation': unresolved,
            'opening_scope': None if last else 'first_instrumented_snapshot_only'})
        frame_hash = digest(previous_hash, body)
        owns_transaction = not self.db.in_transaction
        if owns_transaction:
            self.db.execute('BEGIN IMMEDIATE')
        try:
            cursor = self.db.execute('INSERT INTO frames(at,kind,action,body,previous_hash,hash) VALUES(?,?,?,?,?,?)',
                (at, kind, action, canonical(body), previous_hash, frame_hash))
            frame_id = cursor.lastrowid
            for index, line in enumerate(body['journal']):
                self.db.execute('INSERT INTO journal_lines VALUES(?,?,?,?,?,?)',
                    (frame_id, index, line['account'], line['debit'], line['credit'], line['memo']))
            for item in body['executions']:
                self.db.execute('INSERT INTO execution_receipts VALUES(?,?,?)',
                    (item['id'], frame_id, canonical(item)))
            for index, item in enumerate(body['lessons']):
                self.db.execute('INSERT INTO lessons VALUES(?,?,?)', (frame_id, index, canonical(item)))
            if owns_transaction:
                self.db.commit()
        except BaseException:
            if owns_transaction:
                self.db.rollback()
            raise
        return {'frame_id': frame_id, 'hash': frame_hash, 'body': body,
                'unresolved_reconciliation': unresolved}
```

## scripts/audit_viewer.py

```python
#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
"""Read-only AlphaBot snapshots: local JSON or static HTML/SVG, no server or network."""
import argparse
from datetime import datetime, timezone, timedelta
from decimal import Decimal as D
import html
import hashlib
import json
from pathlib import Path
import sqlite3
import sys
from audit_ledger import canonical, dec, digest, fixed, totals


def timestamp(text, end=False):
    value = datetime.fromisoformat(text.replace('Z', '+00:00'))
    if len(text) == 10:
        value = value.replace(tzinfo=timezone.utc)
        if end:
            value += timedelta(days=1, microseconds=-1)
    if value.tzinfo is None:
        raise ValueError('timezone required')
    return value.astimezone(timezone.utc).isoformat(timespec='microseconds')


def open_readonly(path):
    path = Path(path).expanduser().resolve(strict=True)
    db = sqlite3.connect(path.as_uri() + '?mode=ro', uri=True)
    db.execute('PRAGMA query_only=ON')
    db.execute('BEGIN')  # One consistent WAL snapshot for the entire report.
    return db


def verify(db, end_id):
    previous, frames, lessons = '0' * 64, 0, 0
    parent_lesson_hash = None
    balances, debits, credits = {}, D('0'), D('0')
    for fid, at, kind, action, raw, prev, checksum in db.execute(
            'SELECT id,at,kind,action,body,previous_hash,hash FROM frames WHERE id<=? ORDER BY id', (end_id,)):
        body = json.loads(raw)
        if previous != prev or digest(prev, body) != checksum:
            raise ValueError(f'audit hash chain mismatch at frame {fid}')
        if (body['at'], body['kind'], body['action']) != (at, kind, action):
            raise ValueError(f'frame index/body mismatch at frame {fid}')
        stored = [{'account': a, 'debit': d, 'credit': c, 'memo': m} for a,d,c,m in db.execute(
            'SELECT account,debit,credit,memo FROM journal_lines WHERE frame_id=? ORDER BY seq', (fid,))]
        if canonical(stored) != canonical(body['journal']):
            raise ValueError(f'journal/body mismatch at frame {fid}')
        ds = sum((dec(x['debit']) for x in stored), D('0'))
        cs = sum((dec(x['credit']) for x in stored), D('0'))
        if ds != cs or ds != dec(body['debit_total']) or cs != dec(body['credit_total']):
            raise ValueError(f'unbalanced journal at frame {fid}')
        for item in stored:
            d,c = dec(item['debit']),dec(item['credit'])
            if d < 0 or c < 0 or (d and c):
                raise ValueError('invalid debit/credit line')
            balances[item['account']] = balances.get(item['account'],D('0')) + d - c
        stored_lessons = [json.loads(r[0]) for r in db.execute(
            'SELECT body FROM lessons WHERE frame_id=? ORDER BY seq', (fid,))]
        if canonical(stored_lessons) != canonical(body['lessons']):
            raise ValueError(f'lesson/body mismatch at frame {fid}')
        stored_fills = {r[0]: json.loads(r[1]) for r in db.execute(
            'SELECT execution_id,body FROM execution_receipts WHERE frame_id=?', (fid,))}
        if stored_fills != {x['id']:x for x in body['executions']}:
            raise ValueError(f'fill receipt/body mismatch at frame {fid}')
        if body['lesson_count_before'] != lessons:
            raise ValueError('lesson count continuity mismatch')
        for item in body['lessons']:
            check = dict(item)
            checksum_lesson = check.pop('lesson_hash')
            if check['parent_lesson_hash'] != parent_lesson_hash or hashlib.sha256(canonical(check).encode()).hexdigest() != checksum_lesson:
                raise ValueError('lesson hash ancestry mismatch')
            parent_lesson_hash = checksum_lesson
        lessons += len(body['lessons'])
        if body['lesson_count'] != lessons:
            raise ValueError('lesson count mismatch')
        after = body['after']
        cost, market = totals(after['positions'])
        if dec(after['cash']) + market != dec(after['equity']) or cost != dec(after['asset_cost']):
            raise ValueError(f'position/equity reconciliation mismatch at frame {fid}')
        expected_assets = {'Assets:Cash':dec(after['cash']), 'Assets:Inventory':cost,
                           'Assets:ValuationAdjustment':market-cost}
        if any(balances.get(k,D('0')) != v for k,v in expected_assets.items()):
            raise ValueError(f'journal/position balance mismatch at frame {fid}')
        debits += ds; credits += cs; previous=checksum; frames += 1
    if not frames or sum(balances.values(),D('0')) != 0:
        raise ValueError('empty or unbalanced trial balance')
    return fixed({'verified_frames':frames,'verified_lessons':lessons,'last_hash':previous,
                  'total_debits':debits,'total_credits':credits,'balances':balances,
                  'scope':'append-only hash chain; not externally anchored or tamper-proof against database owner'})


def selection(db, args):
    end_id = None
    if args.milestone:
        target=dec(args.milestone)
        if target <= 0:raise ValueError('positive milestone required')
        for fid,raw in db.execute('SELECT id,body FROM frames ORDER BY id'):
            if dec(json.loads(raw)['after']['peak']) >= target:
                end_id=fid;break
        if end_id is None:raise ValueError('milestone never reached')
    elif args.date:
        row=db.execute('SELECT id FROM frames WHERE at<=? ORDER BY at DESC,id DESC LIMIT 1',
                       (timestamp(args.date,True),)).fetchone()
        end_id=row[0] if row else None
    elif args.until:
        row=db.execute('SELECT id FROM frames WHERE at<=? ORDER BY at DESC,id DESC LIMIT 1',
                       (timestamp(args.until,True),)).fetchone()
        end_id=row[0] if row else None
    else:
        row=db.execute('SELECT max(id) FROM frames').fetchone();end_id=row[0]
    if end_id is None:raise ValueError('no recorded snapshot at or before requested time')
    end=db.execute('SELECT at,body FROM frames WHERE id=?',(end_id,)).fetchone()
    start=timestamp(args.since) if args.since else '0001-01-01T00:00:00.000000+00:00'
    if start>end[0]:raise ValueError('window begins after selected snapshot')
    where='id<=? AND at>=? AND at<=?'
    return end_id,start,end,where,(end_id,start,end[0])


def esc(value):
    return html.escape(str(value),quote=True)


def report_html(db, meta, where, params, max_points):
    count=db.execute('SELECT count(*) FROM frames WHERE '+where,params).fetchone()[0]
    stride=max(1,(count+max_points-1)//max_points)
    points=[]; recent=[]
    for i,(fid,raw) in enumerate(db.execute('SELECT id,body FROM frames WHERE '+where+' ORDER BY at,id',params)):
        body=json.loads(raw)
        if i%stride==0 or i==count-1:points.append((fid,body))
        recent.append((fid,body))
        if len(recent)>200:recent.pop(0)
    after=meta['snapshot']['after']; equity=dec(after['equity'])
    positions=[]
    for p in after['positions']:
        value=dec(p['quantity'])*dec(p['price'])
        weight=value/equity*100 if equity else D('0')
        positions.append('<tr>'+''.join('<td>'+esc(x)+'</td>' for x in
            (p['symbol'],p['quantity'],p['price'],p['cost'],format(value,'f'),format(weight,'f')+'%'))+'</tr>')
    trial=''.join('<tr><td>'+esc(k)+'</td><td>'+esc(v)+'</td></tr>' for k,v in meta['integrity']['balances'].items())
    rows=''.join('<tr>'+''.join('<td>'+esc(x)+'</td>' for x in
        (fid,b['at'],b['kind'],b['action'],b['after']['cash'],b['after']['equity'],b['after']['floor'],
         b['fee_drag'],b['lesson_count'],'; '.join(b['reasons'])))+'</tr>' for fid,b in recent)
    lesson_rows=[]
    for _,b in recent:
        for item in b['lessons']:lesson_rows.append('<pre>'+esc(json.dumps(item,indent=2))+'</pre>')
    chart=''
    if points:
        values=[dec(b['after'][k]) for _,b in points for k in ('equity','floor','cash')]
        low,high=min(values),max(values)
        span=high-low or D('1')
        for key,color in [('equity','#56dfb6'),('floor','#ffbb66'),('cash','#80b5ff')]:
            coordinates=[]
            for i,(_,b) in enumerate(points):
                x=D(55)+D(i)*D(880)/max(1,len(points)-1)
                y=D(265)-(dec(b['after'][key])-low)/span*D(220)
                coordinates.append(f'{x:.3f},{y:.3f}')
            chart+=f'<polyline fill="none" stroke="{color}" stroke-width="2" points="'+ ' '.join(coordinates)+'"/>'
        chart=f'<svg viewBox="0 0 1000 310" role="img" aria-label="Portfolio equity, cash and cash protection floor"><text x="8" y="25" fill="#e9edf4">USD {esc(format(high,"f"))}</text>'+chart+f'<text x="8" y="298" fill="#e9edf4">USD {esc(format(low,"f"))}</text></svg>'
    return '''<!doctype html><html lang="en"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src 'none'; base-uri 'none'; form-action 'none'">
<title>AlphaBot Audit Snapshot</title><style>
body{font:16px system-ui;background:#0e1520;color:#e9edf4;margin:0;padding:24px;line-height:1.5}
main{max-width:1400px;margin:auto}h1{margin-bottom:4px}h2{margin-top:32px}
a{color:#80b5ff}.muted{color:#b9c4d4}.cards{display:flex;flex-wrap:wrap;gap:12px}.card{background:#1b2737;padding:16px;min-width:170px;border-radius:8px}
strong{display:block;font-size:22px;overflow-wrap:anywhere}.scroll{overflow:auto}table{width:100%;border-collapse:collapse;font-size:14px}
th,td{text-align:left;padding:9px;border-bottom:1px solid #3a485b;vertical-align:top}th{background:#1b2737}
pre{white-space:pre-wrap;overflow-wrap:anywhere;background:#1b2737;padding:14px}svg{width:100%;min-height:200px}.warning{border-left:4px solid #ffbb66;padding:12px}
</style></head><body><main>''' + '<h1>AlphaBot · audit snapshot</h1><p class="muted">Read-only · local · USD · fixed-point Decimal</p><p><b>'+esc(meta['snapshot']['context'].get('data_label','Unlabeled historical record'))+'</b></p>'+\
        '<p>Recorded as of '+esc(meta['snapshot']['at'])+'; requested window '+esc(meta['since'])+' to '+esc(meta['until'])+'</p>'+\
        '<div class="cards">'+''.join('<div class="card">'+esc(k)+'<strong>'+esc(after[k])+'</strong></div>' for k in ('equity','cash','floor','peak'))+'</div>'+\
        '<p class="warning">Observed data, not a regulatory certification. Live readiness is unverified. Hash chain verified for '+esc(meta['integrity']['verified_frames'])+' frames; no external anchor.</p>'+\
        '<h2>Performance and protection</h2><p>Equity: green · Floor: amber · Cash: blue. '+esc(count)+' recorded window frames; '+esc(len(points))+' plotted points, downsampled by sequence with endpoints retained.</p>'+chart+\
        '<h2>All positions at snapshot</h2><div class="scroll"><table><thead><tr><th>Symbol</th><th>Quantity</th><th>Unit price</th><th>Acquisition cost</th><th>Market value</th><th>Equity share</th></tr></thead><tbody>'+''.join(positions)+\
        '</tbody></table></div><p>Cash share: '+esc(format(dec(after['cash'])/equity*100 if equity else D(0),'f'))+'%.</p>'+\
        '<h2>Trial balance through selected frame</h2><div class="scroll"><table><tr><th>Account</th><th>Signed debit balance</th></tr>'+trial+'</table></div>'+\
        '<h2>Decision frames</h2><p>Showing the latest '+esc(len(recent))+' of '+esc(count)+' window frames. JSON export includes every selected frame and all postings.</p><div class="scroll"><table><tr>'+\
        ''.join('<th>'+x+'</th>' for x in ['ID','Time','Kind','Action','Cash','Equity','Floor','Fee drag','Lessons','Mathematical reason'])+'</tr>'+rows+'</table></div>'+\
        '<h2>LESSON paths in displayed frames</h2>'+(''.join(lesson_rows) or '<p>No lessons in displayed frames.</p>')+\
        '<h2>Selected decision context</h2><pre>'+esc(json.dumps(meta['snapshot']['context'],indent=2))+'</pre></main></body></html>'


def main():
    p=argparse.ArgumentParser(description=__doc__)
    p.add_argument('--project',required=True)
    p.add_argument('--mode',choices=('backtest','live'),default='backtest')
    group=p.add_mutually_exclusive_group()
    group.add_argument('--date');group.add_argument('--milestone')
    p.add_argument('--from',dest='since');p.add_argument('--until')
    p.add_argument('--format',choices=('json','html'),default='json')
    p.add_argument('--output');p.add_argument('--max-points',type=int,default=1000)
    args=p.parse_args()
    if args.max_points<2:p.error('--max-points must be at least 2')
    if (args.date or args.milestone) and args.until:p.error('--until cannot combine with --date/--milestone')
    db=None;out=None
    try:
        source=Path(args.project).expanduser().resolve(strict=True)/f'{args.mode}.sqlite3'
        if args.output:
            destination=Path(args.output).expanduser().resolve()
            if destination in (source,Path(str(source)+'-wal'),Path(str(source)+'-shm')):
                raise ValueError('report output cannot overwrite database or WAL files')
        db=open_readonly(source)
        end_id,start,end,where,params=selection(db,args)
        integrity=verify(db,end_id)
        meta={'as_of_frame':end_id,'since':start,'until':end[0],
              'snapshot':json.loads(end[1]),'integrity':integrity}
        out=open(args.output,'x',encoding='utf-8') if args.output else sys.stdout
        if args.format=='html':out.write(report_html(db,meta,where,params,args.max_points))
        else:
            out.write('{"metadata":'+json.dumps(meta)+',"frames":[')
            first=True
            for fid,raw in db.execute('SELECT id,body FROM frames WHERE '+where+' ORDER BY at,id',params):
                if not first:out.write(',')
                out.write('{"frame_id":'+str(fid)+',"body":'+raw+'}');first=False
            out.write(']}\n')
        return 0
    except (OSError,ValueError,KeyError,TypeError,sqlite3.Error) as exc:
        print(json.dumps({'event':'VIEW_REJECTED','kind':type(exc).__name__}),file=sys.stderr)
        return 2
    finally:
        if out is not None and out is not sys.stdout:out.close()
        if db:db.close()


if __name__=='__main__':
    raise SystemExit(main())
```

## scripts/gauntlet_v2.py

```python
#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
"""AlphaBot V2: stdlib CLI, streamed prices, SQLite macro/events and durable risk state.
Live mode speaks the documented AlphaBot gateway contract, never a broker directly.
"""
import argparse
from datetime import datetime, timezone
from decimal import Decimal, InvalidOperation
import fcntl
import getpass
import hashlib
import http.cookiejar
import json
import os
from pathlib import Path
import platform
import resource
import re
import signal
import sqlite3
import ssl
import sys
import time
import threading
import urllib.error
import urllib.parse
import urllib.request
import uuid
from host_strategy import capital_floor, decide
from audit_ledger import Ledger, fixed, dec

D = Decimal
STOP = False


class Fault(Exception):
    pass


def number(value):
    try:
        result = D(str(value))
        if not result.is_finite():
            raise ValueError
        return result
    except (InvalidOperation, ValueError):
        raise Fault('invalid finite numeric value') from None


def stamp(value):
    text = str(value)
    if len(text) == 10:
        text += 'T00:00:00+00:00'
    try:
        result = datetime.fromisoformat(text.replace('Z', '+00:00'))
        if result.tzinfo is None:
            raise ValueError
        return result.astimezone(timezone.utc)
    except ValueError:
        raise Fault('timestamp requires ISO date or timezone-aware ISO timestamp') from None


def emit(event, **values):
    print(json.dumps({'event': event, **values}, default=str, allow_nan=False), flush=True)


def records(path):
    with path.open(encoding='utf-8') as stream:
        line_no = 0
        while True:
            line = stream.readline(4 * 1024 * 1024 + 1)
            if not line:
                break
            line_no += 1
            if len(line) > 4 * 1024 * 1024:
                raise Fault('JSONL record exceeds 4 MiB')
            if not line.strip():
                continue
            try:
                value = json.loads(line)
                if not isinstance(value, dict):
                    raise ValueError
            except (ValueError, TypeError):
                raise Fault(f'malformed JSONL at line {line_no}') from None
            yield value


class Store:
    def __init__(self, root, mode):
        self.lock = (root / f'{mode}.lock').open('a')
        try:
            fcntl.flock(self.lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
        except BlockingIOError:
            self.lock.close()
            raise Fault('another runner owns this project/mode') from None
        self.db = sqlite3.connect(root / f'{mode}.sqlite3')
        self.db.executescript('''PRAGMA journal_mode=WAL; PRAGMA synchronous=FULL;
        CREATE TABLE IF NOT EXISTS state(k TEXT PRIMARY KEY,v TEXT NOT NULL);
        CREATE TABLE IF NOT EXISTS macro(obs TEXT, available TEXT, value TEXT,
          PRIMARY KEY(obs,available));
        CREATE INDEX IF NOT EXISTS macro_available ON macro(available);
        CREATE TABLE IF NOT EXISTS geo(at TEXT PRIMARY KEY,tone TEXT);
        CREATE TABLE IF NOT EXISTS audit(id INTEGER PRIMARY KEY,body TEXT NOT NULL);''')

    def get(self, key, default=None):
        row = self.db.execute('SELECT v FROM state WHERE k=?', (key,)).fetchone()
        return json.loads(row[0]) if row else default

    def put(self, **values):
        own = not self.db.in_transaction
        if own:
            self.db.execute('BEGIN IMMEDIATE')
        try:
            for key, value in values.items():
                self.db.execute('INSERT OR REPLACE INTO state VALUES(?,?)',
                                (key, json.dumps(value, default=str)))
            if own:
                self.db.commit()
        except BaseException:
            if own:
                self.db.rollback()
            raise

    def audit(self, event, **values):
        body = {'event': event, **values}
        own = not self.db.in_transaction
        if own:
            self.db.execute('BEGIN IMMEDIATE')
        try:
            self.db.execute('INSERT INTO audit(body) VALUES(?)', (json.dumps(fixed(body)),))
            if own:
                self.db.commit()
        except BaseException:
            if own:
                self.db.rollback()
            raise
        emit(event, **fixed(values))

    def ingest(self, root):
        # Rebuild indexes transactionally; bounded memory regardless of file size.
        with self.db:
            self.db.execute('DELETE FROM macro')
            self.db.execute('DELETE FROM geo')
            for row in records(root / 'macro.jsonl'):
                if row['series'] != 'INDPRO':
                    continue
                obs = stamp(row['observation_date'])
                supplied = row.get('available_at', row.get('publication_date'))
                available = obs if obs.year < 2000 or not supplied else stamp(supplied)
                if available < obs:
                    raise Fault('macro availability precedes observation')
                self.db.execute('INSERT OR REPLACE INTO macro VALUES(?,?,?)',
                                (obs.isoformat(), available.isoformat(), str(number(row['value']))))
            path = root / 'geopolitical.jsonl'
            if not path.is_file():
                raise Fault('geopolitical.jsonl required; empty file explicitly means no events')
            for row in records(path):
                at = stamp(row['at']).isoformat()
                # Preserve simultaneous events by conservatively retaining worst tone.
                old = self.db.execute('SELECT tone FROM geo WHERE at=?', (at,)).fetchone()
                tone = number(row['tone'])
                if old:
                    tone = min(tone, number(old[0]))
                self.db.execute('INSERT OR REPLACE INTO geo VALUES(?,?)', (at, str(tone)))

    def indicators(self, at):
        # Select latest visible vintage per month, then demand four consecutive months.
        months = {}
        for obs, available, value in self.db.execute(
                'SELECT obs,available,value FROM macro WHERE obs<=? AND available<=? '
                'ORDER BY obs DESC,available DESC', (at.isoformat(), at.isoformat())):
            month = obs[:7]
            if month not in months:
                months[month] = number(value)
            if len(months) == 4:
                break
        keys = sorted(months)
        indices = [int(k[:4]) * 12 + int(k[5:7]) for k in keys]
        values = [months[k] for k in keys] if len(indices) == 4 and indices[-1] - indices[0] == 3 else []
        # Never expose future events merely because their calendar month has begun.
        tones = (number(r[0]) for r in self.db.execute(
            'SELECT tone FROM geo WHERE at>=? AND at<=?',
            (at.replace(day=1, hour=0, minute=0, second=0, microsecond=0).isoformat(), at.isoformat())))
        count = self.db.execute('SELECT count(*) FROM geo WHERE substr(at,1,7)=?',
                                (at.strftime('%Y-%m'),)).fetchone()[0]
        return values, min(tones, default=D('0') if count == 0 else None), bool(count == 0), keys[-1] if keys else ''


class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, *args, **kwargs):
        return None


class AuthenticationFailed(Fault):
    def __init__(self):
        super().__init__('AUTHENTICATION_FAILED')


class Gateway:
    def __init__(self, cfg):
        self.broker = cfg['broker']
        self.account_id = cfg['account_id']
        self.deadline = 0.0
        self.auth_lock = threading.Lock()
        self.revoked = threading.Event()
        self.closed = threading.Event()
        self.revoked.set()
        self.cookies = http.cookiejar.CookieJar()
        if self.broker not in ('alpaca', 'kraken', 'bitstamp'):
            raise Fault('unsupported broker')
        self.base = os.environ.get('LIVE_AUTH_GATEWAY_URL', '')
        if self.base and '://' not in self.base:
            self.base = 'https://' + self.base
        parsed = urllib.parse.urlsplit(self.base)
        if (parsed.scheme != 'https' or not parsed.hostname or parsed.username or
                parsed.password or parsed.query or parsed.fragment or parsed.path not in ('', '/')):
            raise AuthenticationFailed()
        if parsed.hostname != cfg.get('gateway_hostname'):
            raise AuthenticationFailed()
        self.hostname = parsed.hostname
        self.base = self.base.rstrip('/')
        self.http = urllib.request.build_opener(urllib.request.ProxyHandler({}), NoRedirect(),
            urllib.request.HTTPCookieProcessor(self.cookies),
            urllib.request.HTTPSHandler(context=ssl.create_default_context()))
        if platform.system() != 'Darwin' or not sys.stdin.isatty():
            raise AuthenticationFailed()
        resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
        key = os.environ.get('LIVE_BROKER_API_KEY')
        secret = os.environ.get('LIVE_BROKER_SECRET_KEY')
        try:
            if not key or not secret:
                raise AuthenticationFailed()
            self.authenticate()
            # Separate authenticated bootstrap, never extra fields in the gate payload.
            result = self.call('/v1/broker-session', {'broker': self.broker,
                'account_id': self.account_id, 'api_key': key, 'secret_key': secret})
            if result.get('bound') is not True or result.get('account_id') != self.account_id:
                raise AuthenticationFailed()
        except BaseException:
            self.cookies.clear()
            self.revoked.set()
            raise
        finally:
            key = secret = None
            os.environ.pop('LIVE_BROKER_API_KEY', None)
            os.environ.pop('LIVE_BROKER_SECRET_KEY', None)
        self.monitor = threading.Thread(target=self.heartbeat, name='auth-expiry', daemon=True)
        self.monitor.start()
        # Reference disposal is best effort; immutable strings cannot be securely wiped.

    def heartbeat(self):
        while not self.closed.wait(0.1):
            with self.auth_lock:
                if time.monotonic() >= self.deadline:
                    self.revoked.set()

    def authenticate(self):
        self.revoked.set()
        self.cookies.clear()
        password = challenge = None
        try:
            password = getpass.getpass('AlphaBot gateway password: ')
            challenge = hashlib.sha256(password.encode('utf-8')).hexdigest()
            password = None
            started = time.monotonic()
            result = self.call('/api/v1/auth/gate', {'gate_challenge': challenge}, auth=False)
            if (result.get('status') != 'AUTHENTICATED' or
                    type(result.get('session_token_expires_sec')) is not int or
                    result['session_token_expires_sec'] != 3600):
                raise AuthenticationFailed()
            # The specified JSON contains no token: require an authenticated cookie.
            usable = any(c.secure and c.domain.lstrip('.') == self.hostname and
                         c.path == '/' and c.has_nonstandard_attr('HttpOnly') and
                         (c.expires is None or c.expires > time.time()) for c in self.cookies)
            if not usable:
                raise AuthenticationFailed()
            with self.auth_lock:
                self.deadline = started + 3600  # Network time never extends the session.
                self.revoked.clear()
        except BaseException:
            self.cookies.clear()
            self.revoked.set()
            raise AuthenticationFailed() from None
        finally:
            password = challenge = None

    def ensure_auth(self):
        # Called in the main thread; the heartbeat never reads the terminal.
        if self.revoked.is_set() or time.monotonic() >= self.deadline:
            self.authenticate()
        if self.revoked.is_set() or time.monotonic() >= self.deadline:
            raise AuthenticationFailed()

    def call(self, path, body, auth=True):
        if auth:
            self.ensure_auth()
        request = urllib.request.Request(self.base + path,
            data=json.dumps(body, default=str).encode(),
            headers={'Content-Type': 'application/json', 'Accept': 'application/json'}, method='POST')
        if auth and (self.revoked.is_set() or time.monotonic() >= self.deadline):
            raise AuthenticationFailed()
        with self.http.open(request, timeout=15) as response:
            raw = response.read(2 * 1024 * 1024 + 1)
            if len(raw) > 2 * 1024 * 1024:
                raise Fault('oversized gateway response')
            result = json.loads(raw)
            if not isinstance(result, dict):
                raise Fault('invalid gateway response')
            return result

    def snapshot(self, max_age):
        row = self.call('/v1/snapshot', {'broker': self.broker})
        age = time.time() - stamp(row['at']).timestamp()
        if age < -5 or age > max_age:
            raise Fault('stale account snapshot')
        if row.get('account_id') != self.account_id or type(row.get('flat')) is not bool:
            raise Fault('account identity/position status mismatch')
        if row.get('currency') != 'USD' or row.get('long_only') is not True:
            raise Fault('USD long-only account required')
        for field in ('cash', 'equity', 'asset_cost', 'exit_fees'):
            row[field] = number(row[field])
            if row[field] < 0:
                raise Fault('negative account field')
        if type(row.get('open_orders')) is not int or row['open_orders'] < 0:
            raise Fault('invalid open order count')
        positions = row.get('positions')
        if not isinstance(positions, list):
            raise Fault('instrumentation requires complete gateway positions')
        seen, cost, market = set(), D('0'), D('0')
        for item in positions:
            symbol = item['symbol']
            if not isinstance(symbol, str) or not symbol or symbol in seen:
                raise Fault('duplicate/invalid position symbol')
            seen.add(symbol)
            for field in ('quantity', 'price', 'cost'):
                item[field] = number(item[field])
            if item['quantity'] <= 0 or item['price'] <= 0 or item['cost'] < 0:
                raise Fault('invalid position metrics')
            cost += item['cost']
            market += item['quantity'] * item['price']
        if cost != row['asset_cost'] or row['cash'] + market != row['equity']:
            raise Fault('position totals do not reconcile exactly to snapshot')
        if row['flat'] != (not positions):
            raise Fault('flat flag contradicts position snapshot')
        return row

    def close(self):
        self.revoked.set()
        self.closed.set()
        monitor = getattr(self, 'monitor', None)
        if monitor:
            monitor.join(timeout=1)
        self.cookies.clear()


def route(broker, symbol, quantity, client_id):
    """Native order mapping; the gateway signs, rounds, validates and reconciles."""
    if broker not in ('alpaca', 'kraken', 'bitstamp'):
        raise Fault('unsupported broker')
    if broker == 'alpaca':
        if not re.fullmatch(r'[A-Z][A-Z0-9.\-]{0,14}', symbol):
            raise Fault('Alpaca route is reserved for equities')
        return {'method': 'POST', 'path': '/v2/orders', 'body': {
            'symbol': symbol, 'qty': str(quantity), 'side': 'buy', 'type': 'market',
            'time_in_force': 'day', 'client_order_id': client_id}}
    if not re.fullmatch(r'[A-Z0-9]{2,12}/USD', symbol):
        raise Fault('crypto route requires BASE/USD symbol')
    if broker == 'kraken':
        return {'method': 'POST', 'path': '/0/private/AddOrder', 'body': {
            'pair': symbol, 'type': 'buy', 'ordertype': 'market',
            'volume': str(quantity), 'cl_ord_id': client_id}}
    return {'method': 'POST', 'path': '/api/v2/buy/market/' + symbol.replace('/', '').lower() + '/',
            'body': {'amount': str(quantity), 'client_order_id': client_id}}


class Engine:
    def __init__(self, store, cfg, live=False):
        self.s, self.cfg = store, cfg
        self.live = live
        self.g = None
        self.capital = number(cfg['starting_capital'])
        self.fee = number(cfg.get('fee_rate', '0.001'))
        self.slip = number(cfg.get('slippage_rate', '0.001'))
        self.symbol = cfg['index_asset']
        if self.capital <= 0 or not 0 <= self.fee < 1 or not 0 <= self.slip < 1:
            raise Fault('invalid capital/fee/slippage configuration')
        fingerprint = hashlib.sha256(json.dumps(cfg, sort_keys=True).encode()).hexdigest()
        if store.get('config_hash', fingerprint) != fingerprint:
            raise Fault('configuration differs from durable run state; use a new project')
        store.put(config_hash=fingerprint)
        self.cash = number(store.get('cash', str(self.capital)))
        self.qty = number(store.get('qty', '0'))
        self.cost = number(store.get('cost', '0'))
        self.peak = number(store.get('peak', str(self.capital)))
        self.halted = store.get('halted', False)
        self.last_entry = store.get('last_entry', '')
        self.last_at = store.get('last_at', '')
        self.ledger = Ledger(store.db, cfg)
        self.positions = []
        self._frame_active = False
        self.diagnostics_ready = not live
        self._allow_entry = True
        self._executions = []
        self._reasons = []
        self._action = 'HOLD'
        if live:
            self.g = Gateway(cfg)

    def save(self):
        self.s.put(cash=self.cash, qty=self.qty, cost=self.cost, peak=self.peak,
                   halted=self.halted, last_entry=self.last_entry, last_at=self.last_at)

    def account(self, price):
        if self.g:
            a = self.g.snapshot(self.cfg.get('max_snapshot_age_seconds', 15))
            self.cash, self.cost = a['cash'], a['asset_cost']
            self.positions = a['positions']
            equity, exit_fees = a['equity'], a['exit_fees']
        else:
            equity = self.cash + self.qty * price
            exit_fees = self.qty * price * (self.fee + self.slip)
            a = {'equity': equity, 'open_orders': 0}
        self.peak = max(self.peak, equity)
        self.save()  # Persist milestone before strategy/order dispatch.
        floor = capital_floor(self.capital, self.cost, self.peak)
        return equity, exit_fees, floor, a

    def _halt(self, reason, price):
        self._action = 'HALT'
        self._reasons.append(reason)
        self.halted = True
        self.save()  # Latch halt before contacting any order endpoint.
        self.s.audit('SAFE_HALT', reason=reason)
        if self.g:
            # Server must cancel pending orders, reconcile, then close all long positions.
            identity = self.s.get('liquidation_id') or str(uuid.uuid4())
            self.s.put(liquidation_id=identity)
            result = self.g.call('/v1/liquidate', {
                'broker': self.cfg['broker'], 'idempotency_key': identity,
                'reason': reason, 'cancel_open_orders': True})
            verified = self.g.snapshot(self.cfg.get('max_snapshot_age_seconds', 15))
            if result.get('flat') is not True or verified.get('flat') is not True or verified['open_orders']:
                raise Fault('liquidation pending/unconfirmed; strategy remains halted')
            try:
                self._executions.extend(self.execution_receipts(result, price))
            finally:
                self.cash, self.cost, self.positions = verified['cash'], verified['asset_cost'], verified['positions']
        elif self.qty and price > 0:
            fill = price * (1 - self.slip)
            fee = self.qty * fill * self.fee
            proceeds = self.qty * fill - fee
            self._executions.append({'id': str(uuid.uuid4()), 'side': 'SELL',
                'symbol': self.symbol, 'quantity': self.qty, 'price': fill, 'fee': fee,
                'reference_price': price, 'released_cost': self.cost, 'source': 'backtest_modeled_slippage'})
            self._reasons.append(f'SELL net_proceeds={proceeds}; quantity={self.qty} * fill={fill} - fee={fee}; released_cost={self.cost}')
            self.cash += proceeds
            self.qty = self.cost = D('0')
        self.save()
        if not self.g and self.qty:
            raise Fault('missing liquidation price; position remains halted')
        self.s.audit('LIQUIDATION_CONFIRMED', cash=self.cash)

    def _tick(self, at, price, values, tone, neutral, month):
        equity, fees, floor, account = self.account(price)
        self._context.update(cash=self.cash, equity=equity, floor=floor, peak=self.peak,
            modeled_exit_fees=fees, fee_rate=self.fee, slippage_rate=self.slip)
        self.s.audit('TELEMETRY', at=at, price=price, equity=equity, cash=self.cash,
            asset_cost=self.cost, peak=self.peak, floor=floor, modeled_exit_fees=fees,
            indpro=values, tone=tone, empty_event_month=neutral)
        if self.halted or self.s.get('pending'):
            self.halt(f'durable_halt={self.halted}; unresolved_order_id={self.s.get("pending")}; either condition prohibits strategy', price)
            return
        if equity - fees < floor or (tone is not None and tone < D('-5')):
            self.halt(f'floor_threat: equity={equity} - exit_drag={fees} = {equity-fees} < floor={floor}' if equity - fees < floor else f'geopolitical_tone: {tone} < -5.0', price)
            return
        if not self._allow_entry:
            self._action = 'SKIP'
            self._reasons.append('Heartbeat monitoring slice: entry disabled; account and geopolitical risk checked')
            return
        if self.live and not self.diagnostics_ready:
            raise Fault('read-only diagnostics must be prepared before live strategy calls')
        if self.g:
            self.g.ensure_auth()  # No strategy callback after expiry without a fresh challenge.
        action = decide({'cash': self.cash, 'indpro': values, 'tone': tone,
                         'month': month, 'last_entry_month': self.last_entry,
                         'lesson_count': self.ledger.count(), 'risk_multiplier': self.ledger.multiplier()})
        self._reasons.append(action['reason'])
        if action['action'] != 'buy' or account['open_orders']:
            self._action = 'SKIP'
            if account['open_orders']:
                self._reasons.append(f'open_orders={account["open_orders"]} > 0; no overlapping entry')
            return
        # Re-evaluate account immediately before dispatch; gateway must enforce atomically too.
        equity, fees, floor, account = self.account(price)
        if equity - fees < floor:
            self.halt(f'pre_order_floor_threat: {equity} - {fees} < {floor}', price)
            return
        if account['open_orders']:
            self._action = 'SKIP'
            self._reasons.append(f'pre_order open_orders={account["open_orders"]} > 0')
            return
        budget = min(number(action['budget']), self.cash * D('.20') * self.ledger.multiplier())
        fill = price * (1 + self.slip)
        qty = (budget / (fill * (1 + self.fee))).quantize(D('.00000001'), rounding='ROUND_DOWN')
        spend = qty * fill + qty * fill * self.fee
        if qty <= 0:
            self._action = 'SKIP'
            self._reasons.append(f'rounded_quantity={qty} <= 0; budget={budget}')
            return
        projected = equity - (spend - qty * price)
        projected_floor = capital_floor(self.capital, self.cost + spend, max(self.peak, projected))
        if projected - fees - qty * price * (self.fee + self.slip) < projected_floor:
            self._action = 'SKIP'
            self._reasons.append(f'projected_net={projected-fees-qty*price*(self.fee+self.slip)} < projected_floor={projected_floor}; budget={budget}')
            self.s.audit('BUY_BLOCKED', reason=self._reasons[-1])
            return
        identity = str(uuid.uuid4())
        self.s.put(pending=identity)
        if self.g:
            current = self.view(price)
            intent = self.ledger.record(at.isoformat(), self.symbol, price, current, current,
                'BUY', self._reasons + ['Durable ORDER_INTENT; not a confirmed fill'],
                self._context, kind='ORDER_INTENT')
            emit('FRAME', **intent['body'], frame_id=intent['frame_id'], hash=intent['hash'])
            result = self.g.call('/v1/order', {
                'broker': self.cfg['broker'], 'idempotency_key': identity,
                'native_order': route(self.cfg['broker'], self.symbol, qty, identity),
                'max_total_debit': str(budget), 'starting_capital': str(self.capital),
                'peak_equity': str(self.peak), 'floor': str(floor),
                'require_atomic_floor_check': True})
            if result.get('status') != 'filled' or result.get('idempotency_key') != identity:
                raise Fault('order not fully reconciled; halt required')
            # Independently fetch authoritative totals; never model live fills as completed.
            receipts = self.execution_receipts(result, price)
            self._executions.extend(receipts)
            self.account(price)
            if not receipts or any(r['side'] != 'BUY' or r['symbol'] != self.symbol for r in receipts):
                raise Fault('missing or contradictory entry fill receipts')
            if sum((r['quantity'] * r['price'] + r['fee'] for r in receipts), D('0')) > budget:
                raise Fault('authoritative fill debit exceeded budget cap')
        else:
            self._executions.append({'id': identity, 'side': 'BUY', 'symbol': self.symbol,
                'quantity': qty, 'price': fill, 'fee': qty * fill * self.fee,
                'reference_price': price, 'source': 'backtest_modeled_slippage'})
            self.cash -= spend
            self.qty += qty
            self.cost += spend
        self._action = 'BUY'
        self._reasons.append(f'BUY quantity={qty}; quote={price}; modeled_fill={fill}; fee={qty*fill*self.fee}; total_debit={spend}; budget_cap={budget}')
        self.last_entry = month
        self.save()
        self.s.put(pending=None)
        self.s.audit('BUY_CONFIRMED', id=identity, symbol=self.symbol, modeled_quantity=qty)

    def view(self, price):
        positions = self.positions if self.g else ([{'symbol': self.symbol,
            'quantity': self.qty, 'price': price, 'cost': self.cost}] if self.qty else [])
        equity = self.cash + sum((dec(p['quantity']) * dec(p['price']) for p in positions), D('0'))
        return {'cash': self.cash, 'equity': equity, 'asset_cost': self.cost,
                'floor': capital_floor(self.capital, self.cost, self.peak),
                'peak': self.peak, 'halted': self.halted, 'positions': positions}

    def prepare_diagnostics(self, root):
        # Local read-only viewer validation is mandatory before any new live entry.
        from audit_viewer import open_readonly, verify, report_html
        db = open_readonly(root / 'live.sqlite3')
        try:
            frame_id, at, raw = db.execute('SELECT id,at,body FROM frames ORDER BY id DESC LIMIT 1').fetchone()
            meta = {'as_of_frame': frame_id, 'since': 'first instrumented snapshot',
                'until': at, 'snapshot': json.loads(raw), 'integrity': verify(db, frame_id)}
            report = report_html(db, meta, 'id<=?', (frame_id,), 1000)
            destination = root / f'live_diagnostics_{frame_id}.html'
            with destination.open('x', encoding='utf-8') as output:
                output.write(report)
            self.s.audit('DIAGNOSTICS_READY', report=str(destination), frame_id=frame_id,
                sha256=hashlib.sha256(report.encode()).hexdigest())
            self.diagnostics_ready = True
        finally:
            db.close()

    def execution_receipts(self, result, reference):
        receipts = result.get('fills')
        if not isinstance(receipts, list):
            raise Fault('instrumentation requires complete authoritative fill receipts')
        normalized = []
        marks = {p['symbol']: dec(p['price']) for p in self.positions}
        for row in receipts:
            item = {'id': row['id'], 'side': row['side'], 'symbol': row['symbol'],
                'quantity': number(row['quantity']), 'price': number(row['price']),
                'fee': number(row['fee']), 'source': 'gateway_authoritative_receipt',
                'reference_price': marks.get(row['symbol'], reference)}
            if not isinstance(item['id'], str) or not item['id']:
                raise Fault('invalid fill identity')
            if item['side'] == 'SELL':
                item['released_cost'] = number(row['released_cost'])
            normalized.append(item)
        return normalized

    def tick(self, at, price, values, tone, neutral, month, allow_entry=True, kind='PRICE'):
        return self.instrument(at, price, values, tone, neutral, month,
            lambda: self._tick(at, price, values, tone, neutral, month), allow_entry, kind)

    def halt(self, reason, price):
        if self._frame_active:
            return self._halt(reason, price)
        at = datetime.now(timezone.utc) if self.live else stamp(self.last_at or '1970-01-01')
        return self.instrument(at, price, [], None, False, '',
            lambda: self._halt(reason, price), False, 'HALT')

    def instrument(self, at, price, values, tone, neutral, month, operation, allow_entry, kind):
        if self._frame_active:
            raise Fault('nested instrumentation frame')
        self._frame_active, self._allow_entry = True, allow_entry
        self._executions, self._reasons, self._action = [], [], 'HOLD'
        self._context = {'indpro': values, 'tone': tone, 'empty_event_month': neutral,
            'month': month, 'last_entry_month': self.last_entry, 'cash': self.cash,
            'lesson_count': self.ledger.count(), 'risk_multiplier': self.ledger.multiplier(),
            'target_deviation_pct': self.ledger.threshold, 'lesson_risk_decay': self.ledger.decay,
            'entry_enabled': allow_entry,
            'data_label': self.cfg.get('data_label', 'LOCAL_PROJECT_DATA_NOT_INDEPENDENTLY_VERIFIED'),
            'reference_price_available': price > 0}
        state = (self.cash, self.qty, self.cost, self.last_entry, self.last_at)
        if not self.live:
            self.s.db.execute('BEGIN IMMEDIATE')
        before = self.view(price)
        error, result = None, None
        try:
            # The observed pre-decision account and complete positions are part of the frame.
            self.account(price)
            before = self.view(price)
            self._context['cash'] = self.cash
            operation()
        except BaseException as exc:
            error = exc
            self.halted, self._action = True, 'HALT'
            self._reasons.append(f'exception_path={type(exc).__name__}; execution outcome requires reconciliation')
        try:
            failures = ([{'code': 'EXECUTION_PATH_FAILURE', 'stage': kind,
                'actual': type(error).__name__, 'details': 'Sanitized failure class; no secret-bearing exception text'}]
                if error else [])
            result = self.ledger.record(at.isoformat(), self.symbol, price, before,
                self.view(price), self._action, self._reasons, self._context,
                self._executions, kind, failures)
            if result['unresolved_reconciliation']:
                self.halted = True
                error = error or Fault('unclassified account movement; reconciliation required')
            if kind == 'PRICE':
                self.last_at = at.isoformat()
            self.save()
            if not self.live:
                self.s.db.commit()
            emit('FRAME', **result['body'], frame_id=result['frame_id'], hash=result['hash'])
            for item in result['body']['lessons']:
                emit('LESSON', **item, frame_id=result['frame_id'])
        except BaseException:
            if not self.live:
                self.s.db.rollback()
                self.cash, self.qty, self.cost, self.last_entry, self.last_at = state
            self.halted = True
            self.save()
            raise
        finally:
            self._frame_active = False
        if error:
            raise error
        return result


def run(args):
    root = Path(args.project).expanduser().resolve(strict=True)
    cfg = json.loads((root / 'config.json').read_text())
    until = stamp(args.until)
    if not 1 <= args.start_year <= until.year:
        raise Fault('invalid start year/until range')
    live = args.mode == 'live'
    if live and not args.continuous:
        raise Fault('live mode requires --continuous')
    store = Store(root, args.mode)
    engine = None
    price = None
    try:
        store.ingest(root)
        engine = Engine(store, cfg, live)
        store.audit('START', mode=args.mode,
                    macro_alignment='pre2000_observation_date; later_available_at_or_observation_assumed',
                    strategy='monthly_INDPRO_vs_previous_3_months')
        if live:
            engine.tick(datetime.now(timezone.utc), D('0'), [], None, False, '',
                        allow_entry=False, kind='PREFLIGHT')
            engine.prepare_diagnostics(root)
            equity, fees, floor, _ = engine.account(D('0'))
            if engine.halted or store.get('pending') or equity - fees < floor:
                engine.halt('startup_risk_intercept', D('0'))
                return 2
        previous = None
        with (root / 'prices.jsonl').open(encoding='utf-8') as stream:
            while not STOP:
                if live and datetime.now(timezone.utc) >= until:
                    break
                position = stream.tell()
                line = stream.readline(4 * 1024 * 1024 + 1)
                if len(line) > 4 * 1024 * 1024:
                    raise Fault('price record exceeds 4 MiB')
                if not line or not line.endswith('\n'):
                    stream.seek(position)
                    if args.continuous and datetime.now(timezone.utc) < until:
                        if live:
                            store.ingest(root)
                            now = datetime.now(timezone.utc)
                            values, tone, neutral, month = store.indicators(now)
                            engine.tick(now, price or D('0'), values, tone, neutral, month,
                                        allow_entry=False, kind='HEARTBEAT')
                            if engine.halted:
                                break
                        time.sleep(1)
                        continue
                    if line:
                        raise Fault('incomplete final price record')
                    break
                row = json.loads(line)
                at = stamp(row['at'])
                if previous is not None and at <= previous:
                    raise Fault('prices must have strictly increasing timestamps')
                previous = at
                if at.year < args.start_year or (engine.last_at and at <= stamp(engine.last_at)):
                    continue
                if at > until:
                    break
                if live:
                    age = time.time() - at.timestamp()
                    if age > cfg.get('max_price_age_seconds', 30):
                        store.audit('PRICE_SKIPPED', source_at=at,
                            reason=f'quote_age_seconds={age} > max_price_age_seconds={cfg.get("max_price_age_seconds",30)}')
                        continue  # Historical replay must never dispatch live orders.
                    if age < -5:
                        raise Fault('future live price timestamp')
                    store.ingest(root)  # Refresh local macro/event files at each live tick.
                price = number(row['quotes'][engine.symbol])
                if price <= 0:
                    raise Fault('price must be positive')
                values, tone, neutral, month = store.indicators(at)
                engine.tick(at, price, values, tone, neutral, month)
                engine.last_at = at.isoformat()
                engine.save()
                if engine.halted:
                    break
        if live and price is None:
            engine.halt('no_fresh_price_before_stop', D('0'))
        elif live and not engine.halted:
            engine.halt('runner_stopped_or_until_reached', price)
        store.audit('COMPLETE', halted=engine.halted, last_at=engine.last_at)
        return 2 if engine.halted else 0
    except BaseException as exc:
        # Avoid exception messages/tracebacks that could contain HTTP headers or secrets.
        store.audit('AUTHENTICATION_FAILED' if isinstance(exc, AuthenticationFailed) else 'ERROR',
                    kind=type(exc).__name__)
        if engine is None and live:
            store.put(halted=True)
        if engine is not None and isinstance(exc, AuthenticationFailed):
            engine.halted = True
            engine.save()
            store.audit('LIQUIDATION_UNCONFIRMED', intervention_required=True)
        elif engine is not None:
            try:
                engine.halt('execution_error', price or D('0'))
            except BaseException:
                store.audit('LIQUIDATION_UNCONFIRMED', intervention_required=True)
        return 2
    finally:
        if engine and engine.g:
            engine.g.close()
        store.db.close()
        store.lock.close()


def stop(signum, frame):
    global STOP
    STOP = True


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--project', required=True)
    parser.add_argument('--start-year', required=True, type=int)
    parser.add_argument('--until', required=True)
    parser.add_argument('--continuous', action='store_true')
    parser.add_argument('--mode', choices=('backtest', 'live'), default='backtest')
    args = parser.parse_args()
    signal.signal(signal.SIGTERM, stop)
    signal.signal(signal.SIGINT, stop)
    try:
        return run(args)
    except (OSError, ValueError, KeyError, Fault):
        emit('STARTUP_REJECTED', reason='invalid project, configuration, or input')
        return 2


if __name__ == '__main__':
    raise SystemExit(main())

```

## scripts/host_strategy.py

```python
# SPDX-License-Identifier: MIT
"""AlphaBot V2 declarative monthly momentum; all economics use Decimal."""
from decimal import Decimal as D


def capital_floor(starting_capital, asset_cost, peak_equity):
    minimum, milestone = D('250'), D('1000')
    while peak_equity >= milestone:
        minimum, milestone = milestone / 2, milestone * 2
    return max(minimum, starting_capital - asset_cost)


def decide(context):
    tone = context['tone']
    if tone is not None and tone < D('-5'):
        return {'action': 'liquidate', 'reason': f'GDELT tone={tone} < -5.0'}
    if tone is None:
        return {'action': 'hold', 'reason': 'GDELT tone unknown: recorded events are not yet contemporaneously visible'}
    values = context['indpro']
    if len(values) != 4:
        return {'action': 'hold', 'reason': f'visible consecutive INDPRO months={len(values)} != 4'}
    if context['month'] == context['last_entry_month']:
        return {'action': 'hold', 'reason': f'observation month={context["month"]} equals last_entry_month; one-entry limit'}
    average = sum(values[:-1]) / 3
    multiplier = context.get('risk_multiplier', D('1'))
    if values[-1] > average:
        budget = context['cash'] * D('.20') * multiplier
        return {'action': 'buy', 'budget': budget,
            'reason': f'INDPRO_current={values[-1]} > prior_three_mean={average}; cash={context["cash"]} * 0.20 * risk_multiplier={multiplier} = budget={budget}; prior_lessons={context.get("lesson_count",0)}'}
    return {'action': 'hold', 'reason': f'INDPRO_current={values[-1]} <= prior_three_mean={average}'}
```

## tests/test_core.py

```python
# SPDX-License-Identifier: MIT
import contextlib
from datetime import datetime, timezone
from decimal import Decimal as D
import io
import json
from pathlib import Path
import sys
import tempfile
import unittest
from unittest.mock import patch
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'scripts'))
import gauntlet_v2 as g
from host_strategy import capital_floor, decide

class CoreTests(unittest.TestCase):
    def setUp(self):
        self.tmp = tempfile.TemporaryDirectory()
        self.root = Path(self.tmp.name)
        self.store = g.Store(self.root, 'backtest')
        self.cfg = {'starting_capital': '500', 'index_asset': 'BTC/USD', 'fee_rate': '0.001', 'slippage_rate': '0.001'}
        self.out = io.StringIO()
        self.redirect = contextlib.redirect_stdout(self.out)
        self.redirect.__enter__()
    def tearDown(self):
        self.store.db.close()
        self.store.lock.close()
        self.redirect.__exit__(None,None,None)
        self.tmp.cleanup()
    def write(self, name, rows):
        (self.root/name).write_text(''.join(json.dumps(r)+'\n' for r in rows))
    def engine(self):
        return g.Engine(self.store, self.cfg)
    def tick(self,e,price='100',tone='0',month='1999-04'):
        e.tick(g.stamp('1999-04-01'),D(price),[D(1),D(2),D(3),D(4)],D(tone),True,month)
    def test_floor_exact_milestones(self):
        for peak, expected in [('999.99','250'),('1000','500'),('1999.99','500'),('2000','1000'),('4000','2000'),('8000','4000')]:
            self.assertEqual(capital_floor(D(500),D(490),D(peak)),D(expected))
    def test_cash_formula(self):
        self.assertEqual(capital_floor(D(500),D('100.10'),D(500)),D('399.90'))
    def test_initial_equality_permits_entry(self):
        e=self.engine();self.tick(e)
        self.assertGreater(e.qty,0);self.assertFalse(e.halted)
    def test_twenty_percent_includes_fees(self):
        e=self.engine();self.tick(e)
        self.assertLessEqual(e.cost,D(100));self.assertGreater(e.cost,D('99.99'))
        self.assertEqual(e.cash+e.cost,D(500))
    def test_once_monthly(self):
        e=self.engine();self.tick(e);q=e.qty;self.tick(e)
        self.assertEqual(e.qty,q)
    def test_next_month_entry(self):
        e=self.engine();self.tick(e);q=e.qty;self.tick(e,month='1999-05')
        self.assertGreater(e.qty,q)
    def test_floor_loss_halts_and_liquidates(self):
        e=self.engine()
        for m in range(1,5):self.tick(e,month=f'1999-{m:02}')
        self.tick(e,price='1')
        self.assertTrue(e.halted);self.assertEqual(e.qty,0)
    def test_tone_exit(self):
        e=self.engine();self.tick(e);self.tick(e,tone='-5.01')
        self.assertTrue(e.halted);self.assertEqual(e.qty,0)
    def test_tone_threshold_is_strict(self):
        e=self.engine();self.tick(e,tone='-5');self.assertFalse(e.halted)
    def test_restart_preserves_peak(self):
        e=self.engine();e.peak=D(4000);e.save()
        self.assertEqual(self.engine().peak,D(4000))
    def test_restart_halt_latched(self):
        e=self.engine();e.halt('test',D(100));e=self.engine();self.tick(e)
        self.assertTrue(e.halted);self.assertEqual(e.qty,0)
    def test_pending_order_halts(self):
        e=self.engine();self.store.put(pending='unknown');self.tick(e)
        self.assertTrue(e.halted);self.assertEqual(e.qty,0)
    def test_config_change_rejected(self):
        self.engine();self.cfg['starting_capital']='600'
        with self.assertRaises(g.Fault):self.engine()
    def test_no_shorting_or_negative_cash(self):
        e=self.engine()
        for m in range(1,13):self.tick(e,month=f'1999-{m:02}')
        self.assertGreaterEqual(e.cash,0);self.assertGreaterEqual(e.qty,0)
    def test_nan_rejected(self):
        for v in ['NaN','Infinity','-Infinity','bad']:
            with self.assertRaises(g.Fault):g.number(v)
    def test_naive_datetime_rejected(self):
        with self.assertRaises(g.Fault):g.stamp('2026-01-01T12:00:00')
    def test_missing_geo_rejected(self):
        self.write('macro.jsonl',[])
        with self.assertRaises(g.Fault):self.store.ingest(self.root)
    def test_pre2000_observation_override_and_no_future_geo(self):
        self.write('macro.jsonl',[{'series':'INDPRO','observation_date':f'1999-{m:02}-01','value':str(m),'available_at':'2001-01-01'} for m in range(1,5)])
        self.write('geopolitical.jsonl',[{'at':'1999-04-20','tone':'-9'}])
        self.store.ingest(self.root)
        vals,tone,neutral,month=self.store.indicators(g.stamp('1999-04-01'))
        self.assertEqual(vals,[D(1),D(2),D(3),D(4)]);self.assertIsNone(tone);self.assertFalse(neutral)
        self.assertEqual(self.store.indicators(g.stamp('1999-04-21'))[1],D(-9))
    def test_post2000_vintage_hidden(self):
        self.write('macro.jsonl',[{'series':'INDPRO','observation_date':'2001-01-01','value':'1','available_at':'2001-03-01'}])
        self.write('geopolitical.jsonl',[]);self.store.ingest(self.root)
        vals,tone,neutral,month=self.store.indicators(g.stamp('2001-02-01'))
        self.assertEqual(month,'');self.assertEqual(vals,[]);self.assertEqual(tone,0);self.assertTrue(neutral)
    def test_missing_month_blocks_signal(self):
        self.write('macro.jsonl',[{'series':'INDPRO','observation_date':f'1999-{m:02}-01','value':str(m)} for m in (1,2,4,5)])
        self.write('geopolitical.jsonl',[]);self.store.ingest(self.root)
        self.assertEqual(self.store.indicators(g.stamp('1999-05-01'))[0],[])
    def test_concurrent_runner_rejected(self):
        with self.assertRaises(g.Fault):g.Store(self.root,'backtest')
    def test_three_native_order_mappings(self):
        self.assertEqual(g.route('alpaca','SPY',D(1),'abc')['path'],'/v2/orders')
        self.assertEqual(g.route('kraken','BTC/USD',D(1),'abc')['body']['ordertype'],'market')
        self.assertEqual(g.route('bitstamp','BTC/USD',D(1),'abc')['path'],'/api/v2/buy/market/btcusd/')
    def test_wrong_asset_broker_rejected(self):
        with self.assertRaises(g.Fault):g.route('alpaca','BTC/USD',D(1),'abc')
        with self.assertRaises(g.Fault):g.route('kraken','SPY',D(1),'abc')
    def test_unknown_tone_holds(self):
        self.assertEqual(decide({'tone':None})['action'],'hold')
    def test_mocked_live_order_requires_fill_reconciliation(self):
        e=self.engine()
        class MockGateway:
            def ensure_auth(self):pass
            def snapshot(self,age):return {'cash':D(500),'asset_cost':D(0),'equity':D(500),'exit_fees':D(0),'open_orders':0,'positions':[]}
            def call(self,path,body):return {'status':'pending'}
        self.cfg['broker']='kraken';e.g=MockGateway()
        with self.assertRaises(g.Fault):self.tick(e)
        self.assertIsNotNone(self.store.get('pending'))
    def test_gateway_rejects_http_and_mismatched_host(self):
        for url in ['http://example.com','https://wrong.example','https://example.com/path']:
            with patch.dict(g.os.environ,{'LIVE_AUTH_GATEWAY_URL':url}):
                with self.assertRaises(g.Fault):g.Gateway({'broker':'kraken','account_id':'a','gateway_hostname':'example.com'})
    def gate(self):
        obj=g.Gateway.__new__(g.Gateway)
        obj.deadline=0;obj.revoked=g.threading.Event();obj.revoked.set()
        obj.closed=g.threading.Event();obj.auth_lock=g.threading.Lock()
        obj.cookies=g.http.cookiejar.CookieJar();obj.hostname='example.com'
        return obj
    def cookie(self,obj,secure=True):
        obj.cookies.set_cookie(g.http.cookiejar.Cookie(0,'session','test',None,False,
            'example.com',False,False,'/',True,secure,None,True,None,None,{'HttpOnly':None},False))
    def test_expiry_requires_fresh_authentication(self):
        obj=self.gate()
        with patch.object(obj,'authenticate',side_effect=g.AuthenticationFailed()) as auth:
            with self.assertRaises(g.AuthenticationFailed):obj.ensure_auth()
            auth.assert_called_once()
    def test_exact_gate_contract(self):
        obj=self.gate();seen=[]
        def call(path,body,auth=True):
            seen.append((path,body,auth));self.cookie(obj)
            return {'status':'AUTHENTICATED','session_token_expires_sec':3600}
        obj.call=call
        with patch.object(g.getpass,'getpass',return_value='test password'):
            before=g.time.monotonic();obj.authenticate()
        self.assertEqual(seen,[('/api/v1/auth/gate',{'gate_challenge':g.hashlib.sha256(b'test password').hexdigest()},False)])
        self.assertFalse(obj.revoked.is_set());self.assertLessEqual(obj.deadline,before+3601)
    def test_gate_status_must_match_exactly(self):
        obj=self.gate();obj.call=lambda *a,**k:{'status':'authenticated','session_token_expires_sec':3600}
        with patch.object(g.getpass,'getpass',return_value='test'):
            with self.assertRaisesRegex(g.AuthenticationFailed,'AUTHENTICATION_FAILED'):obj.authenticate()
        self.assertTrue(obj.revoked.is_set())
    def test_gate_without_session_binding_rejected(self):
        obj=self.gate();obj.call=lambda *a,**k:{'status':'AUTHENTICATED','session_token_expires_sec':3600}
        with patch.object(g.getpass,'getpass',return_value='test'):
            with self.assertRaises(g.AuthenticationFailed):obj.authenticate()
    def test_unreachable_gate_fails_immediately(self):
        obj=self.gate()
        with patch.object(obj,'call',side_effect=OSError('unreachable')) as call:
            with patch.object(g.getpass,'getpass',return_value='test'):
                with self.assertRaises(g.AuthenticationFailed):obj.authenticate()
            call.assert_called_once()
    def test_wrong_expiry_rejected(self):
        obj=self.gate();obj.call=lambda *a,**k:{'status':'AUTHENTICATED','session_token_expires_sec':7200}
        with patch.object(g.getpass,'getpass',return_value='test'):
            with self.assertRaises(g.AuthenticationFailed):obj.authenticate()
    def test_heartbeat_revokes_expired_session(self):
        obj=self.gate();obj.revoked.clear();obj.deadline=g.time.monotonic()-1
        t=g.threading.Thread(target=obj.heartbeat);t.start()
        self.assertTrue(obj.revoked.wait(1));obj.closed.set();t.join(1)
    def test_current_session_does_not_reprompt(self):
        obj=self.gate();obj.deadline=g.time.monotonic()+60;obj.revoked.clear()
        with patch.object(obj,'authenticate') as auth:obj.ensure_auth();auth.assert_not_called()
    def test_missing_liquidation_price_does_not_erase_position(self):
        e=self.engine();self.tick(e)
        with self.assertRaises(g.Fault):e.halt('missing_price',D(0))
        self.assertGreater(e.qty,0);self.assertTrue(e.halted)

if __name__=='__main__':unittest.main(verbosity=2)
```

## tests/test_instrumentation.py

```python
# SPDX-License-Identifier: MIT
import contextlib
from decimal import Decimal as D
import hashlib
import io
import json
from pathlib import Path
import sqlite3
import subprocess
import sys
import tempfile
import unittest
from unittest.mock import patch
sys.path.insert(0,str(Path(__file__).resolve().parents[1] / 'scripts'))
import gauntlet_v2 as g
import audit_viewer as v
from audit_ledger import Ledger,canonical,digest

class InstrumentationTests(unittest.TestCase):
    def setUp(self):
        self.tmp=tempfile.TemporaryDirectory();self.root=Path(self.tmp.name)
        self.store=g.Store(self.root,'backtest')
        self.cfg={'starting_capital':'500','index_asset':'BTC/USD','slippage_rate':'0.001','fee_rate':'0.001'}
        self.e=g.Engine(self.store,self.cfg)
        self.redirect=contextlib.redirect_stdout(io.StringIO());self.redirect.__enter__()
    def tearDown(self):
        self.store.db.close();self.store.lock.close();self.redirect.__exit__(None,None,None);self.tmp.cleanup()
    def tick(self,month='1999-04',price='100',tone='0',values=None,**kwargs):
        return self.e.tick(g.stamp(month+'-01'),D(price),values or [D(1),D(2),D(3),D(4)],
            D(tone),True,month,**kwargs)
    def last(self):
        return json.loads(self.store.db.execute('SELECT body FROM frames ORDER BY id DESC LIMIT 1').fetchone()[0])
    def verify(self):
        end=self.store.db.execute('SELECT max(id) FROM frames').fetchone()[0]
        return v.verify(self.store.db,end)
    def test_buy_journal_is_balanced_and_reconciles_holdings(self):
        self.tick();body=self.last();checked=self.verify()
        self.assertEqual(body['action'],'BUY');self.assertEqual(body['debit_total'],body['credit_total'])
        self.assertEqual(D(checked['balances']['Assets:Cash']),self.e.cash)
        self.assertEqual(D(checked['balances']['Assets:Inventory']),self.e.cost)
    def test_fee_is_capitalized_once_and_disclosed(self):
        self.tick();b=self.last();fill=b['executions'][0]
        fee=D(fill['quantity'])*D(fill['price'])*D('.001')
        self.assertEqual(D(b['fee_drag']),fee)
        self.assertEqual(self.e.cost,D(fill['quantity'])*D(fill['price'])+fee)
        self.assertNotIn('Expenses:EntryFee',self.verify()['balances'])
    def test_each_hold_has_a_frame_and_reason(self):
        self.tick();self.tick()
        self.assertEqual(self.store.db.execute('SELECT count(*) FROM frames').fetchone()[0],2)
        self.assertEqual(self.last()['action'],'SKIP');self.assertIn('one-entry limit',self.last()['reasons'][0])
        self.verify()
    def test_hold_marks_to_market_without_fabricated_trade(self):
        self.tick();self.tick(price='110')
        self.assertEqual(self.last()['executions'],[]);self.assertEqual(self.last()['fee_drag'],'0')
        self.assertGreater(D(self.verify()['balances']['Assets:ValuationAdjustment']),0)
    def test_sell_realized_and_unrealized_balances_reconcile(self):
        self.tick();self.tick(price='110',tone='-6')
        b=self.last();self.assertEqual(b['action'],'HALT');self.assertEqual(b['executions'][0]['side'],'SELL')
        trial=self.verify()['balances'];self.assertEqual(D(trial['Assets:Inventory']),0)
        self.assertEqual(D(trial['Assets:ValuationAdjustment']),0)
        self.assertEqual(D(trial['Assets:Cash']),self.e.cash)
    def test_exact_deviation_threshold_not_float_comparison(self):
        self.cfg['slippage_rate']='0.000001';self.e=g.Engine.__new__(g.Engine)
        # Use a separate pristine project because config fingerprint must remain immutable.
        other=g.Store(self.root,'precise');e=g.Engine(other,self.cfg)
        try:
            e.tick(g.stamp('1999-04-01'),D(100),[D(1),D(2),D(3),D(4)],D(0),True,'1999-04')
            lesson=json.loads(other.db.execute('SELECT body FROM lessons').fetchone()[0])
            self.assertEqual(D(lesson['details']['deviation_pct']),D('0.0001'))
        finally:other.db.close();other.lock.close()
    def test_below_threshold_no_lesson(self):
        self.cfg['slippage_rate']='0.0000001';other=g.Store(self.root,'precise');e=g.Engine(other,self.cfg)
        try:
            e.tick(g.stamp('1999-04-01'),D(100),[D(1),D(2),D(3),D(4)],D(0),True,'1999-04')
            self.assertEqual(e.ledger.count(),0)
        finally:other.db.close();other.lock.close()
    def test_lessons_constrain_next_budget_and_survive_restart(self):
        self.tick();self.assertEqual(self.e.ledger.count(),1)
        cash=self.e.cash;self.tick(month='1999-05');b=self.last()
        self.assertEqual(D(b['context']['risk_multiplier']),D('.95'))
        self.assertLessEqual(sum(D(x['quantity'])*D(x['price'])+D(x['fee']) for x in b['executions']),cash*D('.20')*D('.95'))
        reloaded=g.Engine(self.store,self.cfg);self.assertEqual(reloaded.ledger.count(),2)
        self.assertEqual(reloaded.ledger.multiplier(),D('.9025'))
    def test_heartbeat_does_not_place_order_or_advance_price_cursor(self):
        self.tick();last=self.e.last_at
        self.tick(month='1999-05',allow_entry=False,kind='HEARTBEAT')
        self.assertEqual(self.last()['executions'],[]);self.assertEqual(self.e.last_at,last)
        self.assertEqual(self.last()['kind'],'HEARTBEAT')
    def test_live_snapshot_requires_complete_positions(self):
        gate=g.Gateway.__new__(g.Gateway);gate.account_id='acct';gate.broker='kraken'
        base={'at':g.datetime.now(g.timezone.utc).isoformat(),'account_id':'acct','flat':True,
              'currency':'USD','long_only':True,'cash':'500','equity':'500','asset_cost':'0','exit_fees':'0','open_orders':0}
        gate.call=lambda *a,**k:dict(base)
        with self.assertRaises(g.Fault):gate.snapshot(15)
        base['positions']=[];self.assertEqual(gate.snapshot(15)['positions'],[])
    def test_reconciliation_suspense_is_explicit_and_halts(self):
        self.tick();self.e.cash+=D(1)
        with self.assertRaises(g.Fault):self.tick()
        self.assertTrue(self.e.halted);self.assertTrue(self.last()['unresolved_reconciliation'])
        self.assertEqual(self.last()['lessons'][0]['code'],'UNCLASSIFIED_MOVEMENT');self.verify()
    def test_journal_tables_reject_updates_and_deletes(self):
        self.tick()
        for table in ('frames','journal_lines','lessons','execution_receipts'):
            with self.assertRaises(sqlite3.IntegrityError):self.store.db.execute('DELETE FROM '+table)
            self.store.db.rollback()
    def test_altered_frame_detected_even_after_trigger_removed(self):
        self.tick();db=self.store.db
        db.execute('DROP TRIGGER immutable_frames_UPDATE');db.execute("UPDATE frames SET body=replace(body,'BTC/USD','ETH/USD')");db.commit()
        with self.assertRaisesRegex(ValueError,'hash chain'):self.verify()
    def test_altered_journal_detected(self):
        self.tick();db=self.store.db
        db.execute('DROP TRIGGER immutable_journal_lines_UPDATE');db.execute("UPDATE journal_lines SET debit='1' WHERE seq=0");db.commit()
        with self.assertRaisesRegex(ValueError,'journal/body'):self.verify()
    def test_atomic_rollback_if_frame_write_fails(self):
        with patch.object(self.e.ledger,'record',side_effect=sqlite3.OperationalError('disk failure')):
            with self.assertRaises(sqlite3.OperationalError):self.tick()
        self.assertEqual(D(self.store.get('cash')),D(500));self.assertEqual(D(self.store.get('qty')),0)
        self.assertTrue(self.store.get('halted'));self.assertEqual(self.store.db.execute('SELECT count(*) FROM frames').fetchone()[0],0)
    def test_readonly_view_rejects_writes_and_keeps_database_bytes(self):
        self.tick();path=self.root/'backtest.sqlite3';before=path.read_bytes()
        db=v.open_readonly(path)
        try:
            with self.assertRaises(sqlite3.OperationalError):db.execute('DELETE FROM frames')
            self.assertEqual(v.verify(db,1)['verified_frames'],1)
        finally:db.close()
        self.assertEqual(before,path.read_bytes())
    def test_viewer_date_window_json_is_complete(self):
        self.tick();self.tick(month='1999-05')
        script=Path(v.__file__)
        result=subprocess.run([sys.executable,str(script),'--project',str(self.root),'--date','1999-04-30'],capture_output=True,text=True)
        self.assertEqual(result.returncode,0,result.stderr);data=json.loads(result.stdout)
        self.assertEqual(len(data['frames']),1);self.assertEqual(data['metadata']['snapshot']['at'][:7],'1999-04')
        self.assertEqual(data['metadata']['integrity']['verified_frames'],1)
    def test_viewer_milestone_not_reached_is_error(self):
        self.tick();result=subprocess.run([sys.executable,str(Path(v.__file__)),'--project',str(self.root),'--milestone','1000'],capture_output=True,text=True)
        self.assertEqual(result.returncode,2);self.assertIn('VIEW_REJECTED',result.stderr)
    def test_html_has_no_external_resources_or_scripts(self):
        self.tick();out=self.root/'view.html'
        result=subprocess.run([sys.executable,str(Path(v.__file__)),'--project',str(self.root),'--format','html','--output',str(out)],capture_output=True,text=True)
        self.assertEqual(result.returncode,0,result.stderr);html=out.read_text()
        self.assertIn('<svg',html);self.assertIn('BTC/USD',html);self.assertIn('Trial balance',html)
        self.assertNotIn('<script',html);self.assertNotIn('src=',html);self.assertIn("default-src 'none'",html)
    def test_viewer_missing_database_does_not_create_one(self):
        missing=self.root/'missing.sqlite3'
        with self.assertRaises(FileNotFoundError):v.open_readonly(missing)
        self.assertFalse(missing.exists())

    def test_live_entry_blocked_until_diagnostics_ready(self):
        self.e.live=True;self.e.diagnostics_ready=False
        with patch.object(g,'decide') as decide:
            with self.assertRaises(g.Fault):self.tick()
            decide.assert_not_called()
        self.assertTrue(self.e.halted);self.assertEqual(self.last()['executions'],[])
    def test_diagnostics_preparation_creates_local_readonly_report(self):
        other=g.Store(self.root,'live');e=g.Engine(other,self.cfg)
        try:
            e.tick(g.stamp('1999-04-01'),D(100),[],D(0),True,'1999-04',allow_entry=False,kind='PREFLIGHT')
            e.diagnostics_ready=False;e.prepare_diagnostics(self.root)
            self.assertTrue(e.diagnostics_ready)
            self.assertTrue((self.root/'live_diagnostics_1.html').is_file())
        finally:other.db.close();other.lock.close()
    def test_lesson_ancestry_and_risk_decay_are_explicit(self):
        self.tick();first=self.last()['lessons'][0]
        self.tick(month='1999-05');second=self.last()['lessons'][0]
        self.assertEqual(second['parent_lesson_hash'],first['lesson_hash'])
        self.assertEqual(D(second['risk_multiplier_before']),D('.95'))
        self.assertEqual(D(second['risk_multiplier_after']),D('.9025'))
        self.verify()
    def test_milestone_selects_first_crossing(self):
        self.tick();self.tick(month='1999-05',price='1000')
        result=subprocess.run([sys.executable,str(Path(v.__file__)),'--project',str(self.root),'--milestone','1000'],capture_output=True,text=True)
        self.assertEqual(result.returncode,0,result.stderr)
        data=json.loads(result.stdout)
        self.assertEqual(data['metadata']['as_of_frame'],2)
        self.assertGreaterEqual(D(data['metadata']['snapshot']['after']['floor']),D(500))
    def test_output_cannot_overwrite_project_database(self):
        self.tick();database=self.root/'backtest.sqlite3'
        before=database.read_bytes()
        result=subprocess.run([sys.executable,str(Path(v.__file__)),'--project',str(self.root),'--output',str(database)],capture_output=True,text=True)
        self.assertEqual(result.returncode,2);self.assertEqual(before,database.read_bytes())

if __name__=='__main__':unittest.main(verbosity=2)
```

