schema_version: gas_migration_wave2_4.v1
generated_at: '2026-06-10'
playbook_ref: suggest/quant_engine_refactor_playbook_v1.yaml#P5-T01
total_findings: 103
wave: "2-4"
wave_size: 78
wave_items:

  - idx: 25
    file: gas_data_feed.gs
    line: 656
    text: 'priceBasis = Number.isFinite(tp2Price) ? "TAKE_PROFIT_TIER2_PRICE" : "PRIOR_CLOSE_X_0.998";'
    classification: MIGRATE_TO_PYTHON
    rationale: GAS determines price basis label and fallback in take-profit dispatch logic. Python should select and return priceBasis as part of TAKE_PROFIT_LADDER_V2 output.
    python_path: src/quant_engine/
    formula_id: TAKE_PROFIT_LADDER_V2

  - idx: 26
    file: gas_data_feed.gs
    line: 665
    text: 'priceBasis = Number.isFinite(tp2Price) ? "TAKE_PROFIT_TIER2_PRICE" : "PRIOR_CLOSE_X_0.998";'
    classification: MIGRATE_TO_PYTHON
    rationale: Duplicate of idx=25 in adjacent branch; same priceBasis fallback decision.
    python_path: src/quant_engine/
    formula_id: TAKE_PROFIT_LADDER_V2

  - idx: 27
    file: gas_data_feed.gs
    line: 674
    text: 'priceBasis = Number.isFinite(tp1Price) ? "TAKE_PROFIT_TIER1_PRICE" : "PRIOR_CLOSE_X_0.998";'
    classification: MIGRATE_TO_PYTHON
    rationale: Tier1 variant of priceBasis fallback; same migration target as idx=25.
    python_path: src/quant_engine/
    formula_id: TAKE_PROFIT_LADDER_V2

  - idx: 28
    file: gas_data_feed.gs
    line: 678
    text: 'action = "TAKE_PROFIT_TIER1";'
    classification: MIGRATE_TO_PYTHON
    rationale: GAS directly assigns action string — this is a decision outcome that should come from Python.
    python_path: src/quant_engine/
    formula_id: TAKE_PROFIT_LADDER_V2

  - idx: 29
    file: gas_data_feed.gs
    line: 683
    text: 'priceBasis = Number.isFinite(tp1Price) ? "TAKE_PROFIT_TIER1_PRICE" : "PRIOR_CLOSE_X_0.998";'
    classification: MIGRATE_TO_PYTHON
    rationale: Another branch of same priceBasis fallback; TAKE_PROFIT_LADDER_V2 migration target.
    python_path: src/quant_engine/
    formula_id: TAKE_PROFIT_LADDER_V2

  - idx: 30
    file: gas_data_feed.gs
    line: 741
    text: '// SL003_PRIORITY_MATRIX: 복수 손절 조건 동시 발동 시 max(prices) 적용 — spec/exit/stop_loss.yaml'
    classification: COMMENT_FALSE_POSITIVE
    rationale: Comment referencing spec/exit/stop_loss.yaml; no code logic.
    python_path: null
    formula_id: null

  - idx: 31
    file: gas_data_feed.gs
    line: 742
    text: '// TP 계열(PROFIT_TRIM_*, TAKE_PROFIT_TIER1)은 별도 프레임워크이므로 이 블록 적용 제외'
    classification: COMMENT_FALSE_POSITIVE
    rationale: Documentation comment only.
    python_path: null
    formula_id: null

  - idx: 32
    file: gas_data_feed.gs
    line: 1577
    text: 'score += THRESHOLDS["SP_TAKE_PROFIT"];'
    classification: MIGRATE_TO_PYTHON
    rationale: GAS computes a score by adding the SP_TAKE_PROFIT threshold. Score computation must move to Python.
    python_path: src/quant_engine/
    formula_id: SELL_PRIORITY_SCORING_V1

  - idx: 33
    file: gas_data_feed.gs
    line: 1578
    text: 'breakdown.push(`take_profit:+${THRESHOLDS["SP_TAKE_PROFIT"]}`);'
    classification: MIGRATE_TO_PYTHON
    rationale: "Part of the same score breakdown accumulation as idx=32. Migration target: Python returns the breakdown list."
    python_path: src/quant_engine/
    formula_id: SELL_PRIORITY_SCORING_V1

  - idx: 34
    file: gas_data_feed.gs
    line: 2164
    text: 'TAKE_PROFIT_BASE:    10,'
    classification: MIGRATE_TO_PYTHON
    rationale: Threshold constant defined inline in GAS constants object. Should be in spec/calibration_registry.yaml (source=SPEC_DERIVED, owner_formula=TAKE_PROFIT_LADDER_V2).
    python_path: spec/calibration_registry.yaml
    formula_id: TAKE_PROFIT_LADDER_V2
    migration_action: Register TAKE_PROFIT_BASE=10 in calibration_registry.yaml.

  - idx: 35
    file: gas_data_feed.gs
    line: 2259
    text: "+ ' h5.decisions=' + ((h5 && h5[\"decisions\"]) ? h5[\"decisions\"].length : 0)"
    classification: ADAPTER_OK
    rationale: Log line reading length of Python decisions array; no business logic.
    python_path: null
    formula_id: null

  - idx: 36
    file: gas_data_feed.gs
    line: 2330
    text: 'var routeCount = ((h5 && h5["decisions"]) ? h5["decisions"].length : 0);'
    classification: ADAPTER_OK
    rationale: Reading Python decisions count for logging/routing. No decision made.
    python_path: null
    formula_id: null

  - idx: 37
    file: gas_data_feed.gs
    line: 2339
    text: "+ ' | decisions=' + routeCount"
    classification: ADAPTER_OK
    rationale: Log string interpolation; no business logic.
    python_path: null
    formula_id: null

  - idx: 38
    file: gas_data_feed.gs
    line: 2343
    text: "Logger.log('[LOG_METRIC_MISMATCH_WARN] decisions>0 이지만 sellCandidates=0');"
    classification: ADAPTER_OK
    rationale: Warning log only; no score or decision computation.
    python_path: null
    formula_id: null

  - idx: 39
    file: gas_data_feed.gs
    line: 3172
    text: "formula_id: 'ROUTING_DECISION_EXPLAIN_LOCK_V1',"
    classification: ADAPTER_OK
    rationale: Setting formula_id field in output packet using a string constant; no computation.
    python_path: null
    formula_id: null

  - idx: 40
    file: gas_data_feed.gs
    line: 3285
    text: "formula_id: 'ROUTING_SERVING_DECISION_TRACE_V2'"
    classification: ADAPTER_OK
    rationale: Formula ID tag in output struct; no computation.
    python_path: null
    formula_id: null

  - idx: 41
    file: gas_data_feed.gs
    line: 3399
    text: "* spec/exit/take_profit.yaml:secular_leader_profit_lock.activation_required_all 완전 구현."
    classification: COMMENT_FALSE_POSITIVE
    rationale: JSDoc comment referencing spec; no code logic.
    python_path: null
    formula_id: null

  - idx: 42
    file: gas_data_feed.gs
    line: 5246
    text: 'var profitLockBase = SP["TAKE_PROFIT_BASE"];'
    classification: ADAPTER_OK
    rationale: Reading TAKE_PROFIT_BASE from SP constants object to pass downstream. The constant itself (idx=34) is the migration target; this read is adapter use.
    python_path: null
    formula_id: null

  - idx: 43
    file: gas_data_feed.gs
    line: 5472
    text: '*   TAKE_PROFIT_LADDER_V2 (tier1/tier2) → TICK_NORMALIZER_V1'
    classification: COMMENT_FALSE_POSITIVE
    rationale: JSDoc comment describing data flow; no logic.
    python_path: null
    formula_id: null

  - idx: 44
    file: gas_data_feed.gs
    line: 5551
    text: '// ── TAKE_PROFIT_LADDER_V2 ─────────────────────────────────────────────'
    classification: COMMENT_FALSE_POSITIVE
    rationale: Section header comment only.
    python_path: null
    formula_id: null

  - idx: 45
    file: gas_data_feed.gs
    line: 5571
    text: '// spec/exit/take_profit.yaml:profit_lock_ratchet.ratchet_table 기준'
    classification: COMMENT_FALSE_POSITIVE
    rationale: Comment citing spec; no logic.
    python_path: null
    formula_id: null

  - idx: 46
    file: gas_data_feed.gs
    line: 5714
    text: '* spec/09_decision_flow.yaml 핵심 경로 GAS 구현'
    classification: COMMENT_FALSE_POSITIVE
    rationale: JSDoc comment; no logic.
    python_path: null
    formula_id: null

  - idx: 47
    file: gas_data_feed.gs
    line: 5895
    text: 'return { ["decisions"]: routes, traces: traces, lock: true };'
    classification: MIGRATE_TO_PYTHON
    rationale: GAS function returns a decisions array it built (routes). Decision routing belongs in Python; GAS should receive this as input.
    python_path: src/quant_engine/
    formula_id: ROUTING_SERVING_DECISION_TRACE_V2

  - idx: 48
    file: gas_data_feed.gs
    line: 5920
    text: "if (holding && holding.stopBreach) return 'STOP_LOSS';"
    classification: MIGRATE_TO_PYTHON
    rationale: GAS evaluates stopBreach condition and returns 'STOP_LOSS' — this is a sell decision in GAS that must live in Python stop_loss logic.
    python_path: src/quant_engine/
    formula_id: STOP_LOSS_TRIGGER_V1

  - idx: 49
    file: gas_data_feed.gs
    line: 5941
    text: "var h5RouteRows_ = (h5 && h5[\"decisions\"]) ? h5[\"decisions\"] : [];"
    classification: ADAPTER_OK
    rationale: Reading Python decisions array with null-guard; no computation.
    python_path: null
    formula_id: null

  - idx: 50
    file: gas_data_feed.gs
    line: 5992
    text: "} else if (orderType === 'STOP_LOSS') {"
    classification: ADAPTER_OK
    rationale: Dispatching on Python-provided orderType string; routing-only, not making the stop_loss decision.
    python_path: null
    formula_id: null

  - idx: 51
    file: gas_data_feed.gs
    line: 6028
    text: '["take_profit_price_krw"]: validation === "PASS" ? (priceRow.tp1_price || null) : null,'
    classification: ADAPTER_OK
    rationale: Reading Python-provided tp1_price with validation gate check. No price computation.
    python_path: null
    formula_id: null

  - idx: 52
    file: gas_data_feed.gs
    line: 6029
    text: '["take_profit_quantity"]: validation === "PASS" ? (priceRow.tp1_qty || null) : null,'
    classification: ADAPTER_OK
    rationale: Reading Python-provided tp1_qty; same pattern as idx=51.
    python_path: null
    formula_id: null

  - idx: 53
    file: gas_data_feed.gs
    line: 6058
    text: "if (ot !== 'SELL' && ot !== 'STOP_LOSS') {"
    classification: ADAPTER_OK
    rationale: Filtering on Python-provided order type string; dispatch logic only.
    python_path: null
    formula_id: null

  - idx: 54
    file: gas_data_feed.gs
    line: 6206
    text: "|| (row.order_type || '').toString().toUpperCase() === 'STOP_LOSS';"
    classification: ADAPTER_OK
    rationale: Reading order_type from Python-provided row; string comparison for filtering.
    python_path: null
    formula_id: null

  - idx: 55
    file: gas_data_feed.gs
    line: 6688
    text: '["distribution_risk_score"]: Math.min(100, Math.max(0, score)),'
    classification: DELETE_DUPLICATE
    rationale: GAS computes distribution_risk_score; Python already has DISTRIBUTION_RISK_SCORE_V1. Remove GAS computation, read Python output instead.
    python_path: src/quant_engine/compute_formula_outputs.py
    formula_id: DISTRIBUTION_RISK_SCORE_V1
    migration_action: Delete GAS computation block (L6658-6700 approx), read Python result from sheet row.

  - idx: 56
    file: gas_data_feed.gs
    line: 6692
    text: "formula_id: 'DISTRIBUTION_RISK_SCORE_V1'"
    classification: DELETE_DUPLICATE
    rationale: Part of same GAS computation block as idx=55; remove with the block.
    python_path: src/quant_engine/compute_formula_outputs.py
    formula_id: DISTRIBUTION_RISK_SCORE_V1

  - idx: 57
    file: gas_data_feed.gs
    line: 6774
    text: '["late_chase_risk_score"]: Math.min(100, Math.max(0, Math.round(lateChaseRisk))),'
    classification: DELETE_DUPLICATE
    rationale: GAS computes late_chase_risk_score; Python has this via LATE_CHASE_RISK_SCORE_V1. Remove GAS block, read Python output.
    python_path: src/quant_engine/compute_formula_outputs.py
    formula_id: LATE_CHASE_RISK_SCORE_V1
    migration_action: Delete GAS computation block (L6740-6780 approx), read Python result from alpha sheet row.

  - idx: 58
    file: gas_data_feed.gs
    line: 6902
    text: 'var distributionRiskScore = distRow && typeof distRow["distribution_risk_score"] === "number" ? distRow["distribution_risk_score"]...'
    classification: ADAPTER_OK
    rationale: Reading distribution_risk_score from a sheet row (Python output); null-safe read.
    python_path: null
    formula_id: null

  - idx: 59
    file: gas_data_feed.gs
    line: 6903
    text: 'var lateChaseRiskScore = alphaRow && typeof alphaRow["late_chase_risk_score"] === "number" ? alphaRow["late_chase_risk_score"]...'
    classification: ADAPTER_OK
    rationale: Reading late_chase_risk_score from alphaRow (Python output); null-safe read.
    python_path: null
    formula_id: null

  - idx: 60
    file: gas_data_feed.gs
    line: 7285
    text: 'if (bqRow.breakout_quality_gate === "BLOCKED_LATE_CHASE" || alphaRow["late_chase_risk_score"] >= 70)'
    classification: MIGRATE_TO_PYTHON
    rationale: GAS applies a score threshold (>=70) to make a gate decision. This threshold comparison should live in Python as part of the anti-late-entry gate.
    python_path: src/quant_engine/
    formula_id: ANTI_LATE_ENTRY_GATE_V2
    migration_action: Move threshold check to Python; GAS reads gate result string.

  - idx: 61
    file: gas_data_feed.gs
    line: 7315
    text: '["late_chase_risk_score"]: alphaRow["late_chase_risk_score"] != null ? alphaRow["late_chase_risk_score"]...'
    classification: ADAPTER_OK
    rationale: Null-guarded read from alphaRow; no computation.
    python_path: null
    formula_id: null

  - idx: 62
    file: gas_data_feed.gs
    line: 7995
    text: '* 외국인 순매도 연속일·USD/KRW·FOMC·VIX 등 거시 변수를 macro_risk_score로 환산.'
    classification: COMMENT_FALSE_POSITIVE
    rationale: JSDoc comment describing inputs; no logic.
    python_path: null
    formula_id: null

  - idx: 63
    file: gas_data_feed.gs
    line: 8877
    text: '// stop_loss: final_stop_price/stop_price(없는 키) → protected_stop_price/auto_trailing_stop.'
    classification: COMMENT_FALSE_POSITIVE
    rationale: Comment describing field mapping; no logic.
    python_path: null
    formula_id: null

  - idx: 64
    file: gas_data_feed.gs
    line: 8891
    text: '["stop_loss"]: [pp0.auto_trailing_stop, pp0.protected_stop_price, pp0.profit_preservation_state]'
    classification: ADAPTER_OK
    rationale: Assembling stop_loss fields from Python-computed pp0 object; no decision logic.
    python_path: null
    formula_id: null

  - idx: 65
    file: gas_data_feed.gs
    line: 8968
    text: "{ id: 'Stage_04_macro', key: 'macro_risk_score' },"
    classification: ADAPTER_OK
    rationale: Stage config entry referencing a Python-provided key; no computation.
    python_path: null
    formula_id: null

  - idx: 66
    file: gas_data_feed.gs
    line: 9047
    text: "{ yaml: 'TAKE_PROFIT_LADDER_V1', gs: 'calcTpQuantityLadder_' },"
    classification: ADAPTER_OK
    rationale: GAS/YAML function mapping table entry; registration metadata, not logic.
    python_path: null
    formula_id: null

  - idx: 67
    file: gas_data_feed.gs
    line: 9133
    text: "{ yaml: 'ROUTING_SERVING_DECISION_TRACE_V2', gs: 'buildRoutingServingTraceV2_' },"
    classification: ADAPTER_OK
    rationale: Function mapping table entry; no computation.
    python_path: null
    formula_id: null

  - idx: 68
    file: gas_data_feed.gs
    line: 9138
    text: "{ yaml: 'ROUTING_DECISION_EXPLAIN_LOCK_V1', gs: 'calcRoutingExplainLockV1_' },"
    classification: ADAPTER_OK
    rationale: Function mapping table entry; no computation.
    python_path: null
    formula_id: null

  - idx: 69
    file: gas_data_feed.gs
    line: 9508
    text: '["stop_loss_calc"]: bp["stop_loss"] || df["stop_loss_price"] || null,'
    classification: ADAPTER_OK
    rationale: Reading Python-provided stop_loss with fallback to df field; no computation.
    python_path: null
    formula_id: null

  - idx: 70
    file: gas_data_feed.gs
    line: 9509
    text: '["take_profit_calc"]: bp["take_profit"] || df["tp1_price"] || null,'
    classification: ADAPTER_OK
    rationale: Reading Python-provided take_profit/tp1_price with null fallback; no computation.
    python_path: null
    formula_id: null

  - idx: 71
    file: gas_harness_rows.gs
    line: 3
    text: '// Pure output assembly - no decision logic. Rarely changes after V stabilizes.'
    classification: COMMENT_FALSE_POSITIVE
    rationale: File-level comment; no logic.
    python_path: null
    formula_id: null

  - idx: 72
    file: gas_harness_rows.gs
    line: 60
    text: '* source_manifest_json, decision_trace_json 등에 사용.'
    classification: COMMENT_FALSE_POSITIVE
    rationale: JSDoc comment; no logic.
    python_path: null
    formula_id: null

  - idx: 73
    file: gas_harness_rows.gs
    line: 166
    text: 'var p6DecisionMap = {};'
    classification: ADAPTER_OK
    rationale: Initializing a lookup map to organize Python decisions for sheet writing; no business logic.
    python_path: null
    formula_id: null

  - idx: 74
    file: gas_harness_rows.gs
    line: 167
    text: '(h5.decisions || []).forEach(function(row) { p6DecisionMap[row.ticker] = row; });'
    classification: ADAPTER_OK
    rationale: Building ticker→decision lookup map from Python h5.decisions array; pure organization.
    python_path: null
    formula_id: null

  - idx: 75
    file: gas_harness_rows.gs
    line: 187
    text: 'Object.keys(p6DecisionMap).forEach(function(t) { p6Tickers[t] = true; });'
    classification: ADAPTER_OK
    rationale: Building ticker set from Python decisions for iteration; no computation.
    python_path: null
    formula_id: null

  - idx: 76
    file: gas_harness_rows.gs
    line: 193
    text: 'var da = p6DecisionMap[a] || {};'
    classification: ADAPTER_OK
    rationale: Null-safe lookup of Python decision by ticker; no computation.
    python_path: null
    formula_id: null

  - idx: 77
    file: gas_harness_rows.gs
    line: 194
    text: 'var db = p6DecisionMap[b] || {};'
    classification: ADAPTER_OK
    rationale: Null-safe lookup of Python decision by ticker; no computation.
    python_path: null
    formula_id: null

  - idx: 78
    file: gas_harness_rows.gs
    line: 200
    text: "if (action.indexOf('SELL') >= 0 || action.indexOf('TRIM') >= 0 || action.indexOf('EXIT') >= 0 || act..."
    classification: ADAPTER_OK
    rationale: Sorting rows by Python-provided action string category for sheet display order; presentation logic.
    python_path: null
    formula_id: null

  - idx: 79
    file: gas_harness_rows.gs
    line: 227
    text: 'var d = p6DecisionMap[ticker] || {};'
    classification: ADAPTER_OK
    rationale: Null-safe lookup from Python decisions map.
    python_path: null
    formula_id: null

  - idx: 80
    file: gas_harness_rows.gs
    line: 235
    text: "if (finalAction.indexOf('SELL') >= 0 || finalAction.indexOf('TRIM') >= 0 || finalAction.indexOf('EXI..."
    classification: ADAPTER_OK
    rationale: Categorizing Python-provided finalAction string for sheet rendering; display logic.
    python_path: null
    formula_id: null

  - idx: 81
    file: gas_harness_rows.gs
    line: 265
    text: "} else if (finalAction.indexOf('TAKE_PROFIT') >= 0 || orderType.indexOf('TAKE_PROFIT') >= 0) {"
    classification: ADAPTER_OK
    rationale: Categorizing Python-provided finalAction for display; presentation routing.
    python_path: null
    formula_id: null

  - idx: 82
    file: gas_harness_rows.gs
    line: 464
    text: "['decisions_json', JSON.stringify(h5.decisions)],"
    classification: ADAPTER_OK
    rationale: Serializing Python h5.decisions to sheet; no computation.
    python_path: null
    formula_id: null

  - idx: 83
    file: gas_harness_rows.gs
    line: 465
    text: "['decision_trace_json', (function() { ..."
    classification: ADAPTER_OK
    rationale: Serializing Python decision_trace to sheet cell; no computation.
    python_path: null
    formula_id: null

  - idx: 84
    file: gas_harness_rows.gs
    line: 475
    text: "['decision_lock', 'true'],"
    classification: ADAPTER_OK
    rationale: Writing static lock flag to sheet; no business logic.
    python_path: null
    formula_id: null

  - idx: 85
    file: gas_harness_rows.gs
    line: 489
    text: "['decision_trace_checksum', computeStringChecksum_(JSON.stringify(h5.traces))],"
    classification: ADAPTER_OK
    rationale: Computing integrity checksum of Python trace data for audit; sheet-writing utility, not business logic.
    python_path: null
    formula_id: null

  - idx: 86
    file: gas_harness_rows.gs
    line: 699
    text: "'ALPHA_LEAD_SCORE_V1,FOLLOW_THROUGH_CONFIRM_V1,DISTRIBUTION_RISK_SCORE_V1,'"
    classification: ADAPTER_OK
    rationale: String literal listing formula IDs for a manifest field; no computation.
    python_path: null
    formula_id: null

  - idx: 87
    file: gas_harness_rows.gs
    line: 728
    text: "['macro_risk_score', (hApex || {}).macro_risk_score !== undefined ? String(...)..."
    classification: ADAPTER_OK
    rationale: Reading Python macro_risk_score from hApex for sheet writing; null-safe adapter.
    python_path: null
    formula_id: null

  - idx: 88
    file: gas_harness_rows.gs
    line: 1133
    text: "+ '▶ 재계산 금지: sell_priority_lock·quantities_lock·prices_lock·decision_lock...'"
    classification: COMMENT_FALSE_POSITIVE
    rationale: UI warning string constant embedded in a log/display message; not decision logic.
    python_path: null
    formula_id: null

  - idx: 89
    file: gas_harness_rows.gs
    line: 1267
    text: "['routing_decision_explain_json', JSON.stringify((hApex || {}).routing_decision_explain_json || {})]"
    classification: ADAPTER_OK
    rationale: Serializing Python routing explanation to sheet; no computation.
    python_path: null
    formula_id: null

  - idx: 90
    file: gas_harness_rows.gs
    line: 1299
    text: "'decision_lock', 'blueprint_row_count', 'blueprint_checksum', 'blueprint_hash_algo',"
    classification: ADAPTER_OK
    rationale: Column name list for sheet schema definition; no computation.
    python_path: null
    formula_id: null

  - idx: 91
    file: gas_harness_rows.gs
    line: 1300
    text: "'source_manifest_checksum', 'decision_trace_checksum', 'checksum_hash_algo',"
    classification: ADAPTER_OK
    rationale: Column name list; no computation.
    python_path: null
    formula_id: null

  - idx: 92
    file: gas_harness_rows.gs
    line: 1305
    text: "'prices_json', 'decisions_json', 'decision_trace_json',"
    classification: ADAPTER_OK
    rationale: Column name list; no computation.
    python_path: null
    formula_id: null

  - idx: 93
    file: gas_harness_rows.gs
    line: 1419
    text: "'cashflow_stability_json','routing_decision_explain_json'"
    classification: ADAPTER_OK
    rationale: Column name list; no computation.
    python_path: null
    formula_id: null

  - idx: 94
    file: gas_lib.gs
    line: 145
    text: '"Setup_Decision","Exit_Reason"'
    classification: ADAPTER_OK
    rationale: Column header list; no computation.
    python_path: null
    formula_id: null

  - idx: 95
    file: gas_lib.gs
    line: 158
    text: '"Timing_Score_Entry","Timing_Score_Exit","T1_Forced_Sell_Risk_Score","Sell_Conflict_Score",'
    classification: ADAPTER_OK
    rationale: Column header list; no computation.
    python_path: null
    formula_id: null

  - idx: 96
    file: gas_lib.gs
    line: 946
    text: '"Sector_Score","Sector_Rank","Alert_Level","Data_Quality","Decision_Use","Reason","AsOfDate"'
    classification: ADAPTER_OK
    rationale: Column header list; no computation.
    python_path: null
    formula_id: null

  - idx: 97
    file: gas_lib.gs
    line: 1091
    text: '"Flow_Breadth_5D","Alert_Level","Data_Quality","Decision_Use","ETF_Liquidity_Status","ETF_Execution_..."'
    classification: ADAPTER_OK
    rationale: Column header list; no computation.
    python_path: null
    formula_id: null

  - idx: 98
    file: gas_lib.gs
    line: 1226
    text: '"Alert_Level","Data_Quality","Decision_Use","Reason","RW1","RW3","AsOfDate",'
    classification: ADAPTER_OK
    rationale: Column header list; no computation.
    python_path: null
    formula_id: null

  - idx: 99
    file: gas_lib.gs
    line: 1411
    text: '// MARKET_RISK_SCORE_V1'
    classification: COMMENT_FALSE_POSITIVE
    rationale: Section comment only.
    python_path: null
    formula_id: null

  - idx: 100
    file: gas_lib.gs
    line: 1517
    text: '// spec/05_position_sizing.yaml:net_return_feedback'
    classification: COMMENT_FALSE_POSITIVE
    rationale: Comment referencing spec; no logic.
    python_path: null
    formula_id: null

  - idx: 101
    file: gas_lib.gs
    line: 1553
    text: 'rows.push(["MRS_COMPUTED", "Market_Risk_Score", "Computed", mrs, ...]'
    classification: ADAPTER_OK
    rationale: Assembling a display row with Python-computed mrs value; sheet output assembly.
    python_path: null
    formula_id: null

  - idx: 102
    file: gas_lib.gs
    line: 2128
    text: 'const mrsRow = byName["Market_Risk_Score"] ?? {};'
    classification: ADAPTER_OK
    rationale: Null-safe lookup of Market_Risk_Score row from Python-provided data; no computation.
    python_path: null
    formula_id: null

wave2_4_summary:
  ADAPTER_OK: 50
  COMMENT_FALSE_POSITIVE: 14
  MIGRATE_TO_PYTHON: 11
  DELETE_DUPLICATE: 3
  total_wave2_4: 78

full_migration_summary:
  wave1:
    ADAPTER_OK: 17
    COMMENT_FALSE_POSITIVE: 7
    MIGRATE_TO_PYTHON: 1
    DELETE_DUPLICATE: 0
    total: 25
  wave2_4:
    ADAPTER_OK: 50
    COMMENT_FALSE_POSITIVE: 14
    MIGRATE_TO_PYTHON: 11
    DELETE_DUPLICATE: 3
    total: 78
  all_103:
    ADAPTER_OK: 67
    COMMENT_FALSE_POSITIVE: 21
    MIGRATE_TO_PYTHON: 12
    DELETE_DUPLICATE: 3
    grand_total: 103

migration_priority:
  phase1_delete_duplicates:
    - note: "3 DELETE_DUPLICATE items — GAS computations that duplicate Python exactly"
    - gas_data_feed.gs:6688 distribution_risk_score (DISTRIBUTION_RISK_SCORE_V1)
    - gas_data_feed.gs:6692 formula_id reference for above (remove with block)
    - gas_data_feed.gs:6774 late_chase_risk_score (LATE_CHASE_RISK_SCORE_V1)

  phase2_migrate_to_python:
    - note: "12 MIGRATE_TO_PYTHON items — logic that must move to Python"
    - gas_data_feed.gs:186 SP_TAKE_PROFIT=10 threshold (→ calibration_registry.yaml) [wave1]
    - gas_data_feed.gs:2164 TAKE_PROFIT_BASE=10 constant (→ calibration_registry.yaml)
    - gas_data_feed.gs:656/665/674/678/683 priceBasis + action for TAKE_PROFIT (→ TAKE_PROFIT_LADDER_V2 output)
    - gas_data_feed.gs:1577/1578 score += SP_TAKE_PROFIT (→ SELL_PRIORITY_SCORING_V1)
    - gas_data_feed.gs:5895 return decisions (→ ROUTING_SERVING_DECISION_TRACE_V2)
    - gas_data_feed.gs:5920 stopBreach → STOP_LOSS (→ STOP_LOSS_TRIGGER_V1)
    - gas_data_feed.gs:7285 late_chase_risk_score >= 70 gate (→ ANTI_LATE_ENTRY_GATE_V2)

  phase3_comment_cleanup:
    - note: "21 COMMENT_FALSE_POSITIVE items — no code changes needed, scanner false positives"

  phase4_adapter_ok:
    - note: "67 ADAPTER_OK items — compliant thin-adapter reads; no changes needed"

action_items:
  - id: REGISTER_SP_TAKE_PROFIT
    status: TODO
    file: spec/calibration_registry.yaml
    action: Add SP_TAKE_PROFIT=10, source=SPEC_DERIVED, owner_formula=TAKE_PROFIT_LADDER_V2

  - id: REGISTER_TAKE_PROFIT_BASE
    status: TODO
    file: spec/calibration_registry.yaml
    action: Add TAKE_PROFIT_BASE=10, source=SPEC_DERIVED, owner_formula=TAKE_PROFIT_LADDER_V2

  - id: DELETE_DISTRIBUTION_RISK_GAS
    status: TODO
    file: gas_data_feed.gs
    action: Delete distribution_risk_score computation block (~L6658-6700), replace with read from Python output sheet row

  - id: DELETE_LATE_CHASE_RISK_GAS
    status: TODO
    file: gas_data_feed.gs
    action: Delete late_chase_risk_score computation block (~L6740-6780), replace with read from Python output alpha row

  - id: MIGRATE_PRICEBASIS_TO_PYTHON
    status: TODO
    file: gas_data_feed.gs + src/quant_engine/
    action: Move priceBasis/action assignment (L656-683) to TAKE_PROFIT_LADDER_V2 Python output; GAS reads result

  - id: MIGRATE_SCORE_CALCULATION
    status: TODO
    file: gas_data_feed.gs + src/quant_engine/
    action: Move score += THRESHOLDS["SP_TAKE_PROFIT"] + breakdown.push (L1577-1578) to SELL_PRIORITY_SCORING_V1 Python

  - id: MIGRATE_STOP_BREACH_DECISION
    status: TODO
    file: gas_data_feed.gs + src/quant_engine/
    action: Move stopBreach → STOP_LOSS decision (L5920) to Python STOP_LOSS_TRIGGER_V1

  - id: MIGRATE_DECISIONS_ROUTING
    status: TODO
    file: gas_data_feed.gs + src/quant_engine/
    action: Move decisions route-building (L5895 return block) to Python ROUTING_SERVING_DECISION_TRACE_V2

  - id: MIGRATE_LATE_CHASE_GATE
    status: TODO
    file: gas_data_feed.gs + src/quant_engine/
    action: Move late_chase_risk_score >= 70 threshold gate (L7285) to Python ANTI_LATE_ENTRY_GATE_V2

  - id: UPDATE_GAS_SCANNER_FALSE_POSITIVES
    status: TODO
    file: tools/validate_gas_thin_adapter_v2.py
    action: Add 21 confirmed false-positive patterns to scanner exclusion list to reduce noise in future runs
