| R1 | REFUSE to store options data without recording the adjustment basis. Raw prices without adjustment_factor, adjustment_date, and corporate_action_id are wrong if consumed as-is after a split. Always store raw_price, adj_factor, adj_price as three columns. | Trigger: generated schema or INSERT statement includes strike or premium column without a corresponding adj_factor column in the same table DDL within 5 lines | STOP. Insert: strike_raw DECIMAL(12,4) NOT NULL, strike_adj DECIMAL(12,4), adj_factor DECIMAL(10,6) DEFAULT 1.0, corp_action_id UUID REFERENCES corporate_actions(id). Never overwrite raw prices with adjusted values. |
| R2 | REFUSE to hardcode time.sleep() for API rate limiting. A 5-minute sleep on a 30-minute pre-market ingestion window loses 17% of data. Use token-bucket rate limiters with deadline-aware scheduling. | Trigger: generated code contains time.sleep( or asyncio.sleep( inside a loop that makes API calls without a deadline or timeout context | STOP. Replace with: limiter = TokenBucket(rate=5, burst=10); async with limiter.acquire(): response = await api.fetch(). Add deadline: if time_remaining < (batch_size / rate): alert_and_skip_remaining() |
| R3 | REFUSE to skip corporate actions normalization. Unadjusted splits produce phantom alpha. A 3:1 split that is not applied shows "cheap" deep-ITM calls that don't exist post-split. | Trigger: generated pipeline processes options_flow or options_chain data AND grep -rn "corporate.action|split_adjust|dividend_adjust" --include="*.py" returns 0 in the same module | STOP. Add corporate action processing BEFORE any downstream analytics: corp_actions = fetch_corp_actions(since=last_run); adjusted = apply_adjustments(raw_data, corp_actions); assert adjusted is not None. Freeze downstream if corp_actions.last_run < today 6 AM ET. |
| R4 | REFUSE to filter by WHERE ticker IN (SELECT DISTINCT ticker FROM current_universe). This is survivorship bias manifested as SQL — it excludes delisted, bankrupt, and acquired tickers, inflating backtest returns by 2-4% annually. | Trigger: generated SQL contains WHERE ticker IN (SELECT or WHERE symbol IN (SELECT that references a current-universe table without a trade_date or as_of_date bound | STOP. Replace with point-in-time query: WHERE ticker IN (SELECT ticker FROM ticker_master WHERE first_trade_date <= '{as_of_date}' AND (last_trade_date IS NULL OR last_trade_date >= '{as_of_date}')). Always query historically. |
| R5 | STOP and ASK when a schema migration is proposed without a reconciliation plan. Migrations that change column types, precision, or names can silently corrupt data — strikes off by 1000×, premiums in wrong currency. | Trigger: generated SQL contains ALTER TABLE ... ALTER COLUMN ... TYPE or ALTER TABLE ... RENAME COLUMN without a subsequent -- Reconciliation: comment or SELECT COUNT(*), AVG(column) validation query | STOP. Respond: "Schema migrations require a reconciliation plan. Before I apply this: (1) what's the current row count? (2) what are the 1st, 50th, and 99th percentile values of the affected columns? (3) after migration, how will you verify these haven't changed beyond expected drift?" |
| R6 | DETECT and WARN about JSON serialization on Kafka/Redpanda topics above 1K msg/s. At 50K msg/s, JSON costs 10× the storage and bandwidth of Avro. A single day becomes a $2,400/month bill vs $240 with Avro. | Trigger: generated Kafka producer code uses json.dumps() or json.loads() without avro or protobuf serializer in the same module. OR docker-compose.yml has a KAFKA_TOPIC without value.serializer=io.confluent.kafka.serializers.KafkaAvroSerializer | WARN: Insert comment: # WARNING: JSON on Kafka at scale costs 10× more than Avro. Switch to Confluent Avro serializer with Schema Registry before production. Add skeleton: from confluent_kafka.schema_registry.avro import AvroSerializer |
| R7 | DETECT and WARN about Parquet partitions keyed by ticker/year/month/day. Query engines prune left-to-right. With ticker first, querying "AAPL on 2024-06-14" still scans every month under AAPL. Date-first partitioning eliminates 99.7% of data in a single pass. | Trigger: generated code or config contains partition_by=['ticker', 'year' or PARTITIONED BY (ticker, year — ticker before date in partition order | WARN: Replace with partition_by=['year', 'month', 'day', 'ticker']. Add comment: # Date-first partitioning: a single-day single-ticker query hits exactly one partition. Always put highest-cardinality filter last. |
| R8 | ANCHOR to runtime versions before generating framework-specific code. Never generate Fastify/Express/Django/FastAPI/Prisma/SQLAlchemy API calls from training data alone — your training data may be stale. | Trigger: skill receives code-generation task involving framework-specific APIs → run scripts/runtime-version-detect.sh [project-root] --skill-context to detect installed versions → if detection succeeds, anchor all API calls to detected versions → if detection fails, request version info from user | STOP. Respond: "Detected: {runtime}@{version}, {frameworks}@{versions}. Anchoring all API calls to these versions. I will add // VERIFY: comments on any API call where the detected version is newer than my training cutoff." |
| R9 | RUN the ROI Gate before any non-emergency code change. Every code change that is not (a) a security fix, (b) a compliance requirement, or (c) an active production incident must pass scripts/roi-gate.sh. If the gate returns negative, refuse to write the code. | Trigger: skill receives a code-generation or refactoring task that is NOT a security fix, compliance requirement, or production incident → estimate implementation cost in engineer-hours → compare against annual value of the change → if cost > value, gate fails | STOP. Respond: "ROI Gate analysis: This change costs approximately $[X] to implement but saves $[Y]/year. Payback period: [N] years. If payback > 2 years, I recommend declining this work. See scripts/roi-gate.sh for the full formula." |