Edit test - report writing autonomy

17 March 2026 - ML and Cursor AI

1. Karpathy Approach

1.1 Original Intent

Andrej Karpathy’s AutoResearch (Karpathy, A., 2026) is an autonomous experiment loop for ML model training. The purpose is to let an AI agent iterate on training code overnight—modifying, running, evaluating, and refining—without human intervention. You wake up to a log of experiments and, ideally, a better model.

Core idea: Give an agent a real LLM training setup and let it experiment. The agent acts as researcher, executor, and judge in a closed loop.

1.2 The Loop (Original)

No.  Step Action
1 Modify Agent edits train.py (model, optimizer, hyperparameters)
2 Run Training runs for a fixed 5 minutes
3 Evaluate Measure val_bpb (validation bits per byte)—lower is better
4 Keep or discard If val_bpb improves, keep; otherwise revert via git
5 Iterate Repeat autonomously (~12×/hour, ~100× overnight)

Key elements: (1) modifying executable code, (2) running experiments that produce measurable outcomes, (3) a metric tied to task success (val_bpb), (4) archive of prior runs so the agent avoids repeating failures.


2. The Adaptation

2.1 Domain Shift

The india_karpathy adaptation transplants this loop from ML training to report writing. Instead of improving a model, the system iteratively improves a written report on India energy markets and Maruti Suzuki’s business prospects.

Original (Karpathy) Adaptation (india_karpathy)
Modify train.py Modify report text
Run training → val_bpb Call LLM to improve report
val_bpb (model quality) Section counts, structure, coverage
Ground truth: validation loss Ground truth: structural checklist

2.2 Output

A Maruti-centric report that answers:

  • How oil/energy/gas crises affect Maruti Suzuki and the automotive sector
  • Mitigation strategies Maruti can employ
  • Peer positioning (vs M&M, Tata, TVS)
  • Financial impact (cash flows, margins, working capital)
  • Country-level and company-level metrics to monitor

The loop iteratively improves this report by reformatting, restructuring, and synthesizing a fixed fact base. It does not perform web search, new APIs, or hypothesis-driven data collection—only live fetch (Brent, WTI, Maruti) and LLM synthesis of given facts.


3. How It Works

3.1 Four-Phase Loop

No.  Phase Action Time
1 Ideation LLM reads archive, proposes hypotheses and refinements; when evaluation failed, failure-driven refinements are injected ~5–15s
2 Execution Fetch Brent/WTI/Maruti via yfinance → LLM improves report using facts + fresh data + archive + current report ~30–90s
3 Evaluation Score report against success metrics (strategies ≥4, benchmarks ≥4, peer comparison, etc.) <1s
4 Archive Append hypotheses, refinements, evaluation stats, token usage to archive_v9.log <1s

The LLM must read the archive before each iteration to avoid repeating failures and to target specific gaps.

3.2 Inputs and Provenance

Source Share Role
maruti_energy_facts_v3.md 70–80% Numbers, citations, core facts
fetch_data.py (yfinance) 5–10% Live Brent, WTI, Maruti stock
LLM synthesis 15–25% Structure, narrative, strategy framing
PROBLEM_V7.md Structural Section layout, objectives

3.3 Success Metrics (V7+)

The report must have: Maruti impact section, ≥4 mitigation strategies, peer comparison, cash flow/financial impact, ≥4 country-level metrics, ≥4 company-level metrics, and citations or data.


4. Limitations

4.1 Conceptual

Limitation Explanation
No executable experiments The original modifies code and runs training; the adaptation modifies text and calls an LLM. There is no “run” that produces a measurable outcome tied to the task (e.g., validation loss).
Structural metric vs. task success Success is measured by section counts and format (strategies ≥4, benchmarks ≥4), not by factual correctness, novelty, or business utility.
Fixed fact base The loop synthesizes and restructures given facts; it does not discover new data, run web search, or expand the research scope.

4.2 Operational

Limitation Explanation
Evaluation–content mismatch Regex-based evaluator can fail on valid content (e.g., 1. Bold Title vs expected 1. Title). V9 fixes this, but format drift can reintroduce mismatches.
Reflection stagnation “What to refine” often repeats across iterations without implementation. The LLM may propose the same refinement repeatedly.
Regression risk One-refinement focus + LLM tendency to replace rather than add can cause content loss (e.g., Commodity section replaced by Red Sea).
Convergence Loop may never reach “passed” if evaluator bugs persist, or may pass early with diminishing returns in later iterations.

4.3 Optimal Usage

  • 3–5 iterations is the sweet spot; 4 recommended.
  • Beyond 5–6: diminishing returns or regression.
  • Stop when: evaluation passes and reflection is empty, or same refinement repeats 2+ times, or word count drops.

5. Token Assessment

The final report produced by the V9 loop is in Appendix E. The automation script is in Appendix F; inputs (problem definition, fixed facts, live data) are in Appendices B–D.

5.1 Per-Iteration

Token usage is logged in archive_v9.log for each Execution phase. The following data is from the V9 run that produced the final report:

Iteration Prompt_tokens Completion_tokens Total_tokens
1 9062 2083 11145
2 9431 2083 11514
3 9416 2084 11500
## 
## **Cumulative (Execution phase only):** 34159 tokens
## The final report (iter 3) consumed **11,500 tokens** in a single Execution call (9,416 prompt + 2,084 completion).

5.2 Summary

Metric Value
Iterations (Execution) 3
Execution tokens (cumulative) 34,159
Last iteration (Execution) 11,500 total (9,416 prompt + 2,084 completion)
Ideation tokens ~1,500–3,000 per iteration (not logged)
Estimated total run ~39,000–44,000 tokens

Note: The archive logs only Execution (report-improvement) token usage. Ideation calls add roughly 1,500–3,000 tokens per iteration. For a 3-iteration run, total estimated consumption is ~39,000–44,000 tokens (gpt-4o-mini).


6. Code and Data

6.1 Required Files

File Purpose
automation/energy_maruti_loop_v9.py Main loop (V9 with all effectiveness-report fixes)
automation/PROBLEM_V7.md Problem definition, success metrics, report structure
data/maruti_energy_facts_v3.md Hard statistics, citations, CNG/EV/underbody facts
data/fetched.json Live Brent, WTI, Maruti (generated by fetch_data.py)
automation/fetch_data.py Fetches yfinance data before each Execution

6.2 Environment

# From project root
cd /path/to/india_karpathy
pip install -r requirements.txt   # openai, python-dotenv, yfinance

# Set API key (required for LLM)
echo "OPENAI_API_KEY=sk-your-key" > .env
# Optional: OPENAI_MODEL=gpt-4o (default: gpt-4o-mini)

6.3 Run Commands

# Default: 4 iterations (recommended)
python automation/energy_maruti_loop_v9.py -n 4

# Warm start: 1–2 iterations
python automation/energy_maruti_loop_v9.py -n 2

# Human checkpoint after 3 iters
python automation/energy_maruti_loop_v9.py -n 3 --checkpoint-after 3

# One improve pass (standalone)
python automation/energy_maruti_loop_v9.py --standalone

# Dry run (no writes, no LLM)
python automation/energy_maruti_loop_v9.py --dry-run

6.4 Evaluation Logic

The V9 evaluator uses robust patterns:

# Strategy: Count ^\s*\d+\. (handles 1. **Bold** and 1. Plain)
strategy_count = len(re.findall(r"^\s*\d+\.\s+", strategy_block, re.M))

# Benchmark: B1/B2 style OR numbered items in Metrics to Monitor
metrics_numbered = len(re.findall(r"^\s*\d+\.\s+", metrics_block, re.M))
benchmark_count = max(b_style, metrics_numbered, ...)

6.5 Sample Data

fetched.json (live data):

{
  "timestamp": "2026-03-17T00:45:16Z",
  "data": {
    "yfinance": {
      "brent": { "value": 102.35, "unit": "bbl" },
      "wti": { "value": 95.46, "unit": "bbl" },
      "maruti": { "value": 12757.0, "currency": "INR" }
    }
  }
}

maruti_energy_facts_v3.md (excerpt): Contains tables for India macro, energy market, Maruti sales, CNG exposure (71–73% share), underbody CNG (Victoris, Brezza), EV vs CNG cost (CareEdge, Crisil TCO), fuel tax (petrol ~100%, CNG 15–20%).


7. Recommendations

7.1 High Priority

  1. Validate evaluator against known-good reports — Manually verify strategy/benchmark counts before relying on loop control.
  2. Failure-driven refinements — V9 implements this; ensure failure_summary is correctly parsed from archive.
  3. Additive instruction — Strengthen prompt: “ADD the new section. Do NOT remove or shorten existing sections.”

7.2 Medium Priority

  1. Deduplicate reflection — V9 forces different refinement when stagnant; consider escalating to new analytical section.
  2. Multi-refinement — V9 allows 2 refinements per iter when multiple gaps exist; monitor for regression.
  3. Pre/post diff — V9 retries on >10% word-count drop; consider section-level diff for finer regression detection.

7.3 Enrichment

  1. V3 data rollout — Use maruti_energy_facts_v3.md fully: CNG exposure magnitude, underbody CNG, EV vs CNG, fuel tax.
  2. Explicit structure hints — V9 adds format hints; keep aligned with evaluator regex.
  3. Human-in-the-loop — Use --checkpoint-after N or --checkpoint-on-pass for review before continuing.
  4. Meta-analysis integration — V9 runs meta_analysis_reports.py every 5 iters; feed findings into archive.

7.4 When to Stop

  • Evaluation passes and reflection is empty or generic
  • Same refinement appears in 2+ consecutive iterations
  • Word count or section count drops (regression)
  • Upper bound: 5–6 iterations — beyond this, diminishing returns

8. Conclusion

The india_karpathy adaptation applies the Karpathy loop pattern to report writing: an LLM iteratively improves a Maruti-centric energy report by reading an archive, proposing refinements, and synthesizing a fixed fact base with live data. The approach produces a report but faces conceptual limitations (no executable experiments, structural metrics) and operational limitations (evaluator sensitivity, reflection stagnation, regression risk). With 3–4 iterations and the V9 fixes, the system delivers a usable report at an estimated ~39,000–44,000 tokens per run.


9. References

All sources used in the report and appendices are listed below. The fixed facts (Appendix C) cite RBI, MoF, MOSPI, EIA, RePEc, KAPSARC, CareEdge, Crisil, NREL, Autocar Professional, Fortune India, Quartz, Livemint, Business Standard, Outlook Business, Reuters, MoneyControl, Hindu BusinessLine, Economic Times, ANI, CNBC-TV18, Upstox, Nomura, Maruti IR, and Ministry of Petroleum.


ANI. (2025). Maruti suzuki export growth Q2.
Autocar Professional. (2025). Maruti suzuki CY2025 sales up 3% at 1.80 million; GST cut powers growth in last quarter. https://www.autocarpro.in/analysis-sales/maruti-suzuki-cy2025-sales-up-3-at-180-million-gst-cut-powers-growth-in-last-quarter-130390
Automotive Logistics. (2025). Vehicles moved by rail FY2024-25.
Business Standard. (2026). M&m, tata motors CV, maruti suzuki, TVS motor, hero MotoCorp, bajaj auto: Nifty auto, natural gas shortage impact. https://www.business-standard.com/markets/news/m-m-tata-motors-cv-maruti-suzuki-tvs-motor-hero-motocorp-bajaj-auto-nifty-auto-natural-gas-shortage-impact-126031200191_1.html
CareEdge Ratings. (2024). EV and CNG premium vs petrol: Cost comparison.
CNBC-TV18. (2026). Gas use in paint shop, forging, casting, heat treatment.
Crisil. (2024). Total cost of ownership: EV vs CNG over 2 lakh km.
Economic Times. (2025). Maruti FY25 CNG target 6 lakh units.
Fortune India. (2026). Maruti suzuki looks to take its underbody CNG breakthrough across other product lines. https://www.fortuneindia.com/auto/fortune-india-exclusive-maruti-suzuki-looks-to-take-its-underbody-cng-breakthrough-across-other-product-lines/131273
ImportGenius. (n.d.). Component imports: Japan, germany, china.
KAPSARC. (n.d.). Fuel consumption elasticity to fuel price.
Karpathy, A. (2026). AutoResearch: Autonomous ML training experiments. https://github.com/karpathy/autoresearch
Livemint. (2025). Maruti suzuki Q1 FY26 results: SUV, indian auto market trends, profitability. https://www.livemint.com/companies/company-results/maruti-suzuki-q1-fy26-results-maruti-suzuki-suv-indian-auto-market-trends-maruti-suzuki-profitability-11753970120334.html
Maruti Suzuki India Limited. (2026). Financial results Q3 and 9M FY 2025-26. https://www.marutisuzuki.com/corporate/media/press-releases/2026/january/maruti-suzuki-india-limited-financial-results-q3-and-9m-fy-2025-26
Ministry of Petroleum and Natural Gas. (2026). Parliament statement on gas supply allocation.
Ministry of Statistics and Programme Implementation. (n.d.). Inflation, fuel share in WPI/CPI.
MoneyControl. (2024). Red sea crisis: India’s russian oil buys get more expensive on detour due to houthi attacks. https://www.moneycontrol.com/news/business/red-sea-crisis-indias-russian-oil-buys-get-more-expensive-on-detour-due-to-houthi-attacks-12260001.html
Nomura. (2026). India auto & auto parts research: March 2026 gas shortage impact.
Outlook Business. (2025). Booming CNG car sales may dent india’s electric dream. https://www.outlookbusiness.com/magazine/booming-cng-car-sales-may-dent-indias-electric-dream
Quartz India. (2018). Why maruti is more excited about CNG than electric vehicles right now. https://qz.com/india/1265198/why-maruti-is-more-excited-about-cng-than-electric-vehicles-right-now
RePEc. (2023). Crude oil price elasticity of automobile demand. https://ideas.repec.org/a/eaa/aeinde/v23y2023i2_6.html
Reserve Bank of India. (n.d.). Oil import bill, current account, CAD drivers.
Reuters. (2024). India’s jan oil imports hit record high; red sea delays. https://www.reuters.com/business/energy/indias-jan-oil-imports-hit-record-high-red-sea-delays-trade-data-shows-2024-02-23/
The Hindu Business Line. (2025). Indian refiners balance barrel economics with geopolitical trade tensions as 2025 redraws energy flows. https://www.thehindubusinessline.com/economy/indian-refiners-balance-barrel-economics-with-geopolitical-trade-tensions-as-2025-redraws-energy-flows/article70433443.ece
Upstox. (2025). Maruti CNG portfolio, analyst reports.
US Energy Information Administration. (n.d.). Brent crude forecasts, oil market data.
US National Renewable Energy Laboratory. (n.d.). CNG vs petrol emissions: CO2, CO, NOx comparison.

Appendix A: File Locations

Path Description
india_karpathy/automation/energy_maruti_loop_v9.py Main loop script
india_karpathy/automation/PROBLEM_V7.md Problem definition
india_karpathy/data/maruti_energy_facts_v3.md Facts file
india_karpathy/data/fetched.json Live data (generated)
india_karpathy/archive_v9.log Iteration archive
india_karpathy/REPORT_India_Energy_Maruti_Suzuki_Analysis.md Current report
india_karpathy/reports/ Timestamped report copies
reflection_karpathy/REPORT_India_Karpathy_Approach_Effectiveness.md Effectiveness analysis

Appendix B: Problem Definition (PROBLEM_V7.md)

Input: problem definition, success metrics, and report structure used by the V9 loop.

# Maruti Suzuki & Automotive Sector — Energy Crisis Impact (V7)

**Karpathy Loop Configuration**  
*Maruti-centric focus: company impact, strategies, peer positioning, financial metrics, monitoring.*

---

## I. Objective & Success Metrics

| Element | Value |
|---------|-------|
| **Primary Goal** | Deliver a report focused on **Maruti Suzuki and the automotive sector**: how energy/oil/gas crises affect the company, what strategies it can employ to mitigate, how well positioned it is vs peers to withstand, financial impact (cash flows, margins), and metrics to monitor at both country and company level. |
| **Report Must Answer** | • Maruti & automotive sector impact (sales, production, supply chain) • Mitigation strategies Maruti can employ • Peer positioning (vs M&M, Tata, TVS, etc.) — who is better/worse positioned • Financial impact: cash flows, margins, working capital • How macro energy (oil, gas, shipping) impacts these dynamics • Country-level metrics to gauge crisis (GDP, CAD, fuel demand) • **Company-level metrics** to monitor and manage the crisis |
| **Success Metric** | Report has: Maruti impact section, ≥4 mitigation strategies, peer comparison, cash flow/financial impact, ≥4 country-level metrics, ≥4 company-level metrics. All with citations or data. |
| **Safety Guardrails** | No fabricated data. Cite sources. Distinguish facts from projections. |

---

## II. Report Structure (V7)

1. **Objective & Success Metrics**
2. **Maruti Suzuki & Automotive Sector — Impact** (primary focus)
   - How energy/oil/gas crises affect Maruti and the sector
   - Production, supply chain, sales, margin channels
3. **Mitigation Strategies** (≥4 actionable strategies Maruti can employ)
4. **Peer Positioning** (vs M&M, Tata, TVS, etc. — who withstands better and why)
5. **Financial Impact** (cash flows, margins, working capital, EBITDA)
6. **Macro Energy Context** (how oil/gas/shipping dynamics feed into the above)
7. **Metrics to Monitor**
   - Country-level (GDP, CAD, fuel demand, Brent, etc.)
   - **Company-level** (inventory days, working capital, margin trajectory, export mix, etc.)
8. **Evaluation & Reflection**

---

## III. Key Questions to Address

- How does the energy crisis affect Maruti Suzuki specifically (vs generic macro)?
- What strategies can Maruti employ to mitigate (hedging, localisation, product mix, supply chain)?
- How well positioned is Maruti vs M&M, Tata, TVS given CNG exposure, paint shop, supply chain?
- What is the financial impact on cash flows, margins, working capital?
- What company-level KPIs should management monitor to manage the crisis?
- How do macro energy dynamics (oil, gas, Red Sea) translate into company-level risk?

Appendix C: Fixed Facts (maruti_energy_facts_v3.md)

Input: fixed fact base on which the V9 loop report is based. Sources are cited in the tables and in the References section.

# India Energy & Maruti Suzuki — Combined Facts (v3)

**Use these facts in the report. Cite sources. Do not fabricate.**

*Combines v2 content with augmented facts from CNG strategy (Quartz, Fortune India, Outlook Business), underbody CNG breakthrough, and India EV vs CNG dynamics.*

---

# Part I: Hard Statistics (Baseline)

---

## India Macro — Oil Impact

| Variable | Value | Source |
|----------|-------|--------|
| Oil import bill (India) | ~$100–120 bn/year at $80/bbl | RBI, MoF |
| Current account | Oil imports = major CAD driver; 1% GDP swing per $10/bbl | RBI |
| Inflation | Diesel/petrol pass-through to CPI; fuel ~6% of WPI | MOSPI |
| Fiscal | Excise cuts on fuel reduce govt revenue | Budget docs |

---

## Energy Market

| Variable | Value | Source |
|----------|-------|--------|
| Brent crude (FY24 avg) | ~$82.6/bbl | EIA, industry reports |
| Brent forecast 2025 | ~$73–75/bbl | EIA |
| Red Sea freight premium | +$2–3/bbl (detours via Cape) | MoneyControl, Hindu BusinessLine |
| India oil demand growth 2025 | >300,000 bpd | Industry forecasts |
| India fuel demand (petrol+diesel) | Jan 2025: 20.49 MMT, +3.2% YoY | Reuters |
| Ethanol blending | Up ~5 pp, moderates crude imports | Govt policy |

---

## Fuel Tax Structure (CNG vs Petrol)

| Variable | Value | Source |
|----------|-------|--------|
| Petrol effective tax rate | ~100% | Outlook Business |
| CNG effective tax rate | ~15–20% | Outlook Business |
| Rationale | Post-Covid welfare burden on petrol/diesel; CNG spared as cleaner fuel | Outlook Business |

---

## Maruti Suzuki — Sales

| Variable | Value | Source |
|----------|-------|--------|
| CY2025 total sales | 1.80 million units (+3% YoY) | Autocar Professional, Maruti IR |
| Jan–Sep 2025 | 1.28 million units (−3% YoY) | Autocar Professional |
| Q4 2025 | 525,935 units (+22% YoY) | Autocar Professional |
| December 2025 | 178,646 units (record, +37% YoY) | Autocar Professional |
| GST 2.0 cut | 28% → 18% for small cars (≤4,000mm, ≤1,200cc petrol) | Govt, Sep 2025 |
| 9M FY26 domestic | 1,435,945 units | Maruti IR |
| 9M FY26 exports | 310,559 units | Maruti IR |
| Export growth Q2 | +42.2% to 110,487 units | ANI, Livemint |
| Exports early 2026 | Crossed 400,000 units | Augmented |
| Jan 2026 export growth | +88% YoY | Augmented |
| Feb 2026 export growth | +56% YoY | Augmented |
| Order backlog | ~175,000 vehicles | Augmented |
| Domestic market share | ~40% | Augmented |
| Inventory (dealer) | ~12 days; 3 days in some regions | Augmented |

---

## Maruti Suzuki — CNG Sales & Mix

| Variable | Value | Source |
|----------|-------|--------|
| FY2025 CNG share | ~34% of passenger vehicle sales (~6.2 lakh units) | Fortune India |
| FY26 CNG share (YTD) | ~35–36% | Fortune India |
| Maruti CNG share (2022–23) | 20% | Outlook Business |
| Maruti CNG share (2024–25) | 33% | Outlook Business |
| Maruti CNG target (2029–30) | 35% | Outlook Business |
| Maruti EV target (2029–30) | 15% | Outlook Business |
| CNG cost premium vs petrol | ~₹40,000 (historical) | Quartz, RC Bhargava |
| Maruti share of Indian CNG vehicle market | ~71–73% | Autocar Professional, analyst reports |
| CY2024 CNG car/SUV sales (industry) | 715,000 units (+35% YoY) | Autocar Professional |
| Maruti share of CY2024 CNG sales | 72% | Autocar Professional |
| Tata share of CY2024 CNG sales | 16% | Autocar Professional |
| Maruti CNG sales FY2024 | 4.55 lakh units (+50% YoY) | Reuters, analyst reports |
| Maruti FY25 CNG target | 6 lakh units (+30% jump) | Reuters, Economic Times |
| CNG models in portfolio | 14 models | Autocar Professional, Upstox |

---

## Maruti Suzuki — Margins & Costs

| Variable | Value | Source |
|----------|-------|--------|
| Q1 FY26 margin decline | ~200 bps (commodity inflation, employee costs) | Livemint |
| Average selling price | ₹7.27 lakh (+7% YoY) | Livemint |
| SUV mix | Grand Vitara, Fronx, Jimny, Brezza | Maruti portfolio |
| Commodity pressure | Platinum, aluminium, copper, rare-earth elements | Maruti IR Jan 2026 |
| Q3 FY26 net profit | ₹3,879.1 cr (+4.1% YoY) | Business Standard |
| Q3 FY26 revenue | ₹47,537.2 cr (+29.2% YoY) | Business Standard |
| Total assets (Feb 2026) | ~₹1,152 billion ($13.8 billion) | Augmented |
| Cash and equivalents (Feb 2026) | Over ₹150 billion | Augmented |

---

## Elasticity & Demand Drivers

| Variable | Value | Source |
|----------|-------|--------|
| Crude oil price elasticity (auto demand) | −0.214 (long-run, inelastic) | RePEc 1987–2020 study |
| Income elasticity | 1.620 | RePEc |
| Fuel consumption elasticity to fuel price | 0.12–0.15 (new car buyers) | KAPSARC, arXiv |

---

## Supply Chain & Logistics

| Variable | Value | Source |
|----------|-------|--------|
| Vehicles moved by rail FY2024-25 | 500,000+ | Automotive Logistics |
| Component imports | Japan (Suzuki), Germany, China | ImportGenius |
| Red Sea impact | Longer routes, higher freight; Russian oil detours | MoneyControl |

---

# Part II: CNG Market & Strategy

---

## India CNG Market — Industry-Wide

| Variable | Value | Source |
|----------|-------|--------|
| Industry CNG share (2022–23) | ~10% | Outlook Business |
| Industry CNG share (2024–25) | 19.3% | Outlook Business |
| CNG four-wheelers (2019–20) | 1.8 lakh units | Outlook Business |
| CNG four-wheelers (2024–25) | 4.8 lakh units | Outlook Business |
| Tata CNG share (2021–22) | 3% | Outlook Business |
| Tata CNG share (2024–25) | 25% | Outlook Business |
| Shared mobility / commercial fleets | ~10% of PV sales; CNG ~8–10% within that | Outlook Business |

---

## Maruti CNG Exposure — Gas Squeeze Impact (Analyst View)

*Analysts identify Maruti Suzuki as uniquely exposed to CNG gas supply squeeze due to overwhelming segment dominance.*

### Magnitude of Exposure

| Factor | Value | Source |
|--------|-------|--------|
| Sales dependence | One in every three cars sold is CNG | Fortune India, analyst reports |
| CNG share of Maruti sales (early FY26) | 35–36% | Fortune India |
| Maruti share of Indian CNG market | ~71–73% | Autocar Professional |
| Pending bookings for CNG (early 2022) | 43% of backlog (~1.2 lakh+ cars) | Analyst reports |
| Ertiga CNG | Persistent waiting periods | Analyst reports |

### Demand–Supply Imbalance

- Despite 50% growth in CNG sales to 4.55 lakh units in FY2024, Maruti faced significant demand–supply imbalances.
- 43% of pending bookings were for CNG models; situation persisted, especially for Ertiga.
- FY25 target of 6 lakh CNG vehicles (30% jump) is directly at risk from gas supply disruptions.

### LNG & Running Cost Risk

- Disruptions in imported gas (LNG) supply lead to increased costs and reduced availability.
- Can directly squeeze demand by **eroding the running cost advantage over petrol**—the core value proposition of CNG.

### March 2026 Impact (Nomura)

- Maruti listed among auto companies **most impacted** by March 2026 gas shortages.
- Potential for further production and cost disruptions.

### Mitigation & Residual Risk

| Mitigation | Status |
|------------|--------|
| Manesar plant capacity expansion | Underway |
| 14-model CNG portfolio | Active |
| City gas distribution (CGD) network supply | **Primary risk**—constraints remain |

*Sources: Autocar Professional, Reuters, Economic Times, Nomura, Upstox, CNBC-TV18*

---

## CNG Infrastructure

| Variable | Value | Source |
|----------|-------|--------|
| CNG stations (2022–23) | 5,665 | Outlook Business |
| CNG stations (2024–25) | 8,000+ | Outlook Business |
| Govt target CNG stations by 2034 | 18,336 | Outlook Business |
| Petrol pumps (India) | 88,500+ | Outlook Business |
| CNG nozzles per station (avg) | ~4 | Outlook Business |
| Simultaneous CNG refills (capacity) | ~30,000–32,000 vehicles | Outlook Business, Crisil |

---

## Natural Gas Sourcing & Policy

| Variable | Value | Source |
|----------|-------|--------|
| Local natural gas for CNG | ~40% | Outlook Business |
| Imported LNG for CNG | ~60% | Outlook Business |
| Kirit Parikh Committee (2023) | Rationalised prices of locally produced natural gas; predictability for CNG input | Outlook Business |

---

## CNG vs Petrol — Emissions (US NREL Study)

| Pollutant | CNG vs Petrol |
|-----------|---------------|
| CO₂ | 18% less |
| CO | 65% less |
| NOx | 30%+ less |

*Source: US National Renewable Energy Laboratory, cited in Outlook Business*

---

## EV vs CNG — Cost Comparison

| Variable | Value | Source |
|----------|-------|--------|
| EV premium vs petrol | 80% costlier | CareEdge Ratings 2024 |
| CNG premium vs petrol | 12% | CareEdge Ratings 2024 |
| EV TCO (2,00,000 km) | ₹16.70 lakh | Crisil |
| CNG TCO (2,00,000 km) | ₹20.46 lakh | Crisil |
| EV advantage for heavy users | ~18% lower TCO | Crisil |

---

## Maruti Strategy — CNG vs EV (Quartz, RC Bhargava)

- **India market structure**: 75% of cars under 4 metres and under ₹5 lakh; no other market has this small-car dominance.
- **EV affordability**: Converting small cars to EV would more than double cost; adding ₹6–7 lakh to a ₹5 lakh car is untenable.
- **CNG advantage**: Technology exists; cost increase ~₹40,000 vs petrol; customers buy happily.
- **CNG constraint**: Distribution limits production; cannot ask customers to queue 1 hour to refill.
- **Multi-powertrain stance** (Rahul Bharti, Maruti): “Multi-powertrain route makes more sense than leaping headlong into EV transition… supply chain resilience [for EVs] is not fully established.” — Outlook Business

---

## Maruti Underbody CNG Breakthrough (Fortune India, March 2026)

### Victoris Template

| Variable | Value | Source |
|----------|-------|--------|
| First model with underbody CNG | Maruti Suzuki Victoris (mid-size SUV, 2025) | Fortune India |
| Victoris price range | ₹10.5 lakh – ₹19.9 lakh (ex-showroom) | Fortune India |
| Layout | Two cylinders mounted beneath vehicle body; full luggage capacity retained | Fortune India |

### Rollout Strategy

| Model | Status | Source |
|-------|--------|--------|
| Victoris | Launched | Fortune India |
| Brezza | Testing underway | Fortune India |
| Grand Vitara | Under evaluation; technically feasible | Fortune India |
| Alto, WagonR, S-Presso, Swift, Baleno | Continue with S-CNG (no underbody) | Fortune India |
| Ertiga | May not move to underbody; existing packaging adequate | Fortune India |

### Analyst View (Puneet Gupta, S&P Global Mobility)

- CNG strongest in entry-level due to running costs; luggage loss discouraged adoption in other segments.
- Underbody-mounted cylinders strengthen value proposition and expand CNG appeal.

---

## Competitor CNG Packaging

| OEM | Technology | Models | Price Range | Source |
|-----|-------------|--------|-------------|--------|
| Tata Motors | iCNG twin-cylinder (under luggage floor) | Altroz, Tiago, Tigor, Punch | ₹6.5–9.6 lakh | Fortune India |
| Hyundai | Hy-CNG Duo (dual cylinders under luggage floor) | Exter || Fortune India |

---

## Mass Market & EV Gap

| Variable | Value | Source |
|----------|-------|--------|
| Mass market (below ₹12–13 lakh) | EVs largely missing except Tata; range constraints persist | S&P Global Mobility |
| CAFE 4 norms | Slated 2032; stricter emissions; path for EVs | Outlook Business |
| CNG as bridge | “Bridge technology from petrol to EV”; EV charging time and running cost key | S&P Global Mobility |

---

# Part III: Natural Gas Crisis (March 2026)

---

## Market Reaction & Index Slump

- **Nifty Auto Crash**: The Nifty Auto index plunged **2.6% to 3.19%** in a single session on **March 12, 2026**, hitting its lowest level since September 2025.
- **Broad Sell-off**: All 15 index constituents traded in the red.
- **Top Losers**:
  - TVS Motor: **-5.35%**
  - M&M: **-4.39%**
  - Eicher Motors: **-3.92%**
  - Maruti Suzuki: **-3.72%**
- **Tata Motors Passenger Vehicles (TMPV)** hit a new 52-week low.

---

## Energy Supply Disruption

### West Asia Conflict

- Escalations in the Middle East led to fears of energy supply disruptions through the **Strait of Hormuz**.

### Natural Gas Supply Order (March 9, 2026)

- The Indian government invoked the **Natural Gas Supply Order** to manage the crisis.
- **Industrial consumers** (including auto plants) are capped at **80%** of their average gas consumption from the past six months.
- This measure aims to save approximately **31%** of overall gas.

---

## Operational Impact on Manufacturing

### Critical Processes

Natural gas is vital for heat-intensive processes:

- **Paint curing** (paint shops)
- **Forging**
- **Casting**
- **Metal heat treatment**

### Supply Chain Vulnerability

- The sector's **"just-in-time"** model leaves no room for error.
- If even one **Tier-2 or Tier-3 supplier** faces gas constraints, entire vehicle assembly lines could halt.
- Shifting to alternative energy sources (e.g., electricity) is **not feasible in the short term**—it requires extensive machinery modifications.
- **Paint shops** are the most vulnerable—they typically operate at 100% capacity with no short-term alternative for curing heat.

---

## Company-Specific Risks (Nomura / Axis)

### High Exposure

| Company | Risk Factor |
|---------|-------------|
| **Maruti Suzuki** | Large CNG portfolio; gas-intensive paint shop operations |
| **TVS Motor** | Large CNG portfolio; gas-intensive paint shop operations |
| **Bajaj Auto** | Large CNG portfolio; gas-intensive paint shop operations |

### Production Loss Risk

Companies operating **near peak capacity** with **low inventories** face the highest risk of immediate production losses:

- Maruti Suzuki, M&M, Ashok Leyland, Tata Motors CV, Eicher Motors

### Ancillary/Supplier Risk

- **Bharat Forge**, **Uno Minda**: Critical for forged and cast components; any gas cut here immediately impacts OEMs.

### Margin Pressure

- Reliance on expensive **"spot LNG"** to fill the 20% supply gap could increase manufacturing costs by **15–25%**.
- Potential **EBITDA margin compression** of **80–100 bps** in Q4FY26.
- Nomura reduced December 2026 target for Nifty 50 by **15%** (from 29,300 to 24,900).

---

## Government Stance: Ministry of Petroleum

*Statement to Parliament, March 12, 2026 — Union Minister Hardeep Singh Puri*

| Sector | Supply Allocation |
|--------|-------------------|
| Domestic Piped Natural Gas (PNG) | 100% |
| CNG for transport | 100% |
| Industrial users | 80% |
| Fertilizer plants | 70% |

- India has secured crude volumes **exceeding** what the disrupted routes would have delivered; non-Hormuz sourcing has risen to **70%**.
- **No shortage** of petrol, diesel, or ATF.
- LPG pressure attributed to **panic-booking and hoarding**, not production failure.
- Government absorbing roughly **₹74 per cylinder** of the global price hike.

---

## Nomura Analyst Team & Ratings (March 2026)

| Lead | Role |
|------|------|
| **Kapil Singh** | Lead Analyst, India Auto & Auto Parts Research |
| **Siddhartha Bera** | India Autos Research Analyst |
| **Saion Mukherjee** | Head of India Equity Research |

### Updated Ratings

| Company | Rating | Context |
|---------|--------|---------|
| **M&M** | Buy (Top Pick) | Strong EV transition (XEV 9S); limited CNG reliance |
| **Tata Motors (CV)** | Buy | Replacement cycle, infrastructure boom beneficiary |
| **Maruti Suzuki** | Neutral | Most exposed; massive CNG portfolio; gas-heavy paint shops |
| **TVS Motor** | Buy | Top 2W pick; EV leadership as hedge |
| **Hyundai Motor India** | Buy | Target ₹2,698; premium mix, strong exports |

---

## Production Engineer Perspective: Maruti Response

### Immediate Operational War Room

| Action | Description |
|--------|-------------|
| **Batch-and-hold painting** | Run curing ovens at max capacity for shorter bursts; reduce idling gas |
| **Temperature optimization** | Explore lower curing temps for base coats / internal components |
| **Supplier strike teams** | Deploy engineers to Tier-2/3 vendors for furnace insulation, heat recovery |

### Leveraging Market Share for Cash Flow

- **High-margin prioritization**: Shift mix toward SUVs (Grand Vitara, Invicto) and away from entry-level hatchbacks.
- **CNG inventory strategy**: Pre-sell remaining inventory at premium to pull forward cash.
- **Export pivot**: Accelerate exports; USD realizations strengthen balance sheet.

### Mitigating Future Vulnerability

| Short-Term | Long-Term |
|------------|-----------|
| Batch-painting & temperature reduction | Electrification of ovens (eRTO) |
| Vendor energy-optimization strike teams | Hybridization to reduce component complexity |
| SUV & Export prioritization | Direct-to-Biogas energy sourcing |

### Implementation Timelines

| Strategy | Speed | Cash Flow Impact |
|----------|-------|------------------|
| Batch-painting & oven optimization | 1–2 weeks | Immediate (stabilization) |
| High-margin/Export pivot | 1 month | Rapid (improvement) |
| Vendor strike teams | 2–4 weeks | Immediate (protection) |
| Electrification of ovens (eRTO) | 6–12 months | Delayed (efficiency) |

### Suzuki-Toyota Strategic Hedge

- **Series hybrid shift**: Fronx/Swift Hybrid; engine as generator; less heat treatment/forging.
- **Biogas**: Banas Suzuki Biogas Plant (Gujarat, Jan 2026); scaling to factory-scale biogas.
- **Fortress approach**: Use ₹150 bn cash to subsidize Tier-2 suppliers (electric furnaces, biogas digesters); accelerate 2027 Hybrid Roadmap to 2026.

---

## Comparative Context (Nomura)

- Parallel to **2021–2022 semiconductor shortage**—but this is an **energy crisis** hitting the foundation of manufacturing.
- Investors advised to watch **"production impact indicators"** (e.g., plant shift reductions).

---

# Part IV: Historical Context (Quartz)

*Note: Quartz article reflects earlier period; figures have since evolved significantly.*

- **RC Bhargava (Maruti)**: India market unique—75% cars under 4m and under ₹5 lakh; no comparable market.
- **CNG production constraint**: Historically limited by CNG distribution; Maruti in dialogue with govt and oil companies to widen network.
- **Suzuki-Toyota EV partnership**: India-specific EV development.
- **Suzuki battery factory**: $180 million lithium-ion battery unit in Gujarat (historical context).

---

# Sources (URLs)

### Original (v2)

- https://www.autocarpro.in/analysis-sales/maruti-suzuki-cy2025-sales-up-3-at-180-million-gst-cut-powers-growth-in-last-quarter-130390
- https://www.marutisuzuki.com/corporate/media/press-releases/2026/january/maruti-suzuki-india-limited-financial-results-q3-and-9m-fy-2025-26
- https://www.livemint.com/companies/company-results/maruti-suzuki-q1-fy26-results-maruti-suzuki-suv-indian-auto-market-trends-maruti-suzuki-profitability-11753970120334.html
- https://www.thehindubusinessline.com/economy/indian-refiners-balance-barrel-economics-with-geopolitical-trade-tensions-as-2025-redraws-energy-flows/article70433443.ece
- https://www.reuters.com/business/energy/indias-jan-oil-imports-hit-record-high-red-sea-delays-trade-data-shows-2024-02-23/
- https://www.moneycontrol.com/news/business/red-sea-crisis-indias-russian-oil-buys-get-more-expensive-on-detour-due-to-houthi-attacks-12260001.html
- https://ideas.repec.org/a/eaa/aeinde/v23y2023i2_6.html

### Augmented (March 2026 Gas Crisis)

- https://www.business-standard.com/markets/news/m-m-tata-motors-cv-maruti-suzuki-tvs-motor-hero-motocorp-bajaj-auto-nifty-auto-natural-gas-shortage-impact-126031200191_1.html
- Nomura Report, March 12, 2026 — India Auto & Auto Parts Research
- Ministry of Petroleum, Parliament statement, March 12, 2026

### Augmented (CNG Strategy, Underbody, EV vs CNG)

- https://qz.com/india/1265198/why-maruti-is-more-excited-about-cng-than-electric-vehicles-right-now
- https://www.fortuneindia.com/auto/fortune-india-exclusive-maruti-suzuki-looks-to-take-its-underbody-cng-breakthrough-across-other-product-lines/131273
- https://www.outlookbusiness.com/magazine/booming-cng-car-sales-may-dent-indias-electric-dream

### Augmented (Maruti CNG Exposure, Gas Squeeze Magnitude)

- Autocar Professional — CNG car/SUV sales CY2024, Maruti 72% share
- Reuters, Economic Times — Maruti FY25 CNG target 6 lakh units
- Nomura — March 2026 gas shortage impact
- CNBC-TV18 — Gas use in paint shop, forging, casting, heat treatment
- Upstox — Analyst reports

Appendix D: Live Data (fetched.json)

Input: live market data (Brent, WTI, Maruti stock) fetched via yfinance before each Execution phase.

{
  "timestamp": "2026-03-17T00:45:16.054674Z",
  "sources": [
    "yfinance/Yahoo Finance"
  ],
  "data": {
    "yfinance": {
      "brent": {
        "symbol": "BZ=F",
        "value": 102.35,
        "prev_close": 100.21,
        "change_pct": 2.14,
        "currency": "USD",
        "unit": "bbl",
        "5d_high": 103.9,
        "5d_low": 86.32
      },
      "wti": {
        "symbol": "CL=F",
        "value": 95.46,
        "prev_close": 93.5,
        "change_pct": 2.1,
        "currency": "USD",
        "unit": "bbl",
        "5d_high": 99.32,
        "5d_low": 81.79
      },
      "maruti": {
        "symbol": "MARUTI.NS",
        "value": 12757.0,
        "prev_close": 12591.0,
        "change_pct": 1.32,
        "currency": "INR",
        "unit": "share",
        "5d_high": 13948.0,
        "5d_low": 12400.0
      }
    }
  }
}

Appendix E: Final Report (REPORT_India_Energy_Maruti_Suzuki_Analysis.md)

Output: complete report produced by the V9 loop, as of the final iteration.

## Maruti Suzuki & Automotive Sector — Energy Crisis Impact (V9)

**Karpathy Loop Configuration**  
*Maruti-centric focus: company impact, strategies, peer positioning, financial metrics, monitoring.*

---

## I. Objective & Success Metrics

| Element | Value |
|---------|-------|
| **Primary Goal** | Deliver a report focused on **Maruti Suzuki and the automotive sector**: how energy/oil/gas crises affect the company, what strategies it can employ to mitigate, how well positioned it is vs peers to withstand, financial impact (cash flows, margins), and metrics to monitor at both country and company level. |
| **Report Must Answer** | • Maruti & automotive sector impact (sales, production, supply chain) • Mitigation strategies Maruti can employ • Peer positioning (vs M&M, Tata, TVS, etc.) — who is better/worse positioned • Financial impact: cash flows, margins, working capital • How macro energy (oil, gas, shipping) impacts these dynamics • Country-level metrics to gauge crisis (GDP, CAD, fuel demand) • **Company-level metrics** to monitor and manage the crisis |
| **Success Metric** | Report has: Maruti impact section, ≥4 mitigation strategies, peer comparison, cash flow/financial impact, ≥4 country-level metrics, ≥4 company-level metrics. All with citations or data. |
| **Safety Guardrails** | No fabricated data. Cite sources. Distinguish facts from projections. |

---

## II. Report Structure (V9)

1. **Objective & Success Metrics**
2. **Maruti Suzuki & Automotive Sector — Impact** (primary focus)
   - How energy/oil/gas crises affect Maruti and the sector
   - Production, supply chain, sales, margin channels
3. **Mitigation Strategies** (≥4 actionable strategies Maruti can employ)
4. **Peer Positioning** (vs M&M, Tata, TVS, etc. — who withstands better and why)
5. **Financial Impact** (cash flows, margins, working capital, EBITDA)
6. **Macro Energy Context** (how oil/gas/shipping dynamics feed into the above)
7. **Metrics to Monitor**
   - Country-level (GDP, CAD, fuel demand, Brent, etc.)
   - **Company-level** (inventory days, working capital, margin trajectory, export mix, etc.)
8. **Evaluation & Reflection**

---

## III. Key Questions to Address

- How does the energy crisis affect Maruti Suzuki specifically (vs generic macro)?
- What strategies can Maruti employ to mitigate (hedging, localisation, product mix, supply chain)?
- How well positioned is Maruti vs M&M, Tata, TVS given CNG exposure, paint shop, supply chain?
- What is the financial impact on cash flows, margins, working capital?
- What company-level KPIs should management monitor to manage the crisis?
- How do macro energy dynamics (oil, gas, Red Sea) translate into company-level risk?

---

## IV. Maruti Suzuki & Automotive Sector — Impact

### A. Energy Crisis Effects on Maruti Suzuki

The ongoing energy crisis significantly impacts Maruti Suzuki, particularly due to its reliance on CNG and natural gas for critical manufacturing processes, especially in paint shops. The recent **Natural Gas Supply Order** caps industrial gas consumption at **80%**, which could lead to production halts if supply chain disruptions occur ([Business Standard](https://www.business-standard.com)). This situation is compounded by the rising Brent crude prices, currently at **$102.35/bbl**, which affects overall operational costs and consumer demand.

### B. Sales and Margin Impact

Maruti's total sales target for **CY2025** is **1.80 million units**, reflecting a **3% YoY increase** ([Autocar Professional](https://www.autocarpro.in/analysis-sales/maruti-suzuki-cy2025-sales-up-3-at-180-million-gst-cut-powers-growth-in-last-quarter-130390)). However, rising oil prices could lead to a **1.5% decrease** in sales volume, translating to a potential reduction of **27,000 units** due to the inelastic demand for automobiles (elasticity of **−0.214**) ([RePEc](https://ideas.repec.org/a/eaa/aeinde/v23y2023i2_6.html)). The company has already experienced a margin decline of approximately **200 bps** due to commodity inflation and increased employee costs ([Livemint](https://www.livemint.com/companies/company-results/maruti-suzuki-q1-fy26-results-maruti-suzuki-suv-indian-auto-market-trends-maruti-suzuki-profitability-11753970120334.html)).

### C. CNG Exposure Magnitude

Maruti Suzuki's reliance on CNG is substantial, with approximately **35-36%** of its sales being CNG vehicles ([Fortune India](https://www.fortuneindia.com)). This high exposure makes the company particularly vulnerable to gas supply disruptions, especially given that **43%** of pending bookings are for CNG models. The ongoing gas supply squeeze could severely impact production and sales targets, particularly for popular models like the Ertiga.

---

## V. Mitigation Strategies

1. **Cost & Margin Defence**
   - **Commodity Hedging**: Lock in prices for critical materials like aluminium and copper to mitigate volatility.
   - **Localisation**: Increase local sourcing of components to reduce foreign exchange and freight exposure.
   - **Product Mix Optimization**: Focus on higher-margin SUVs to counterbalance pressures on lower-end models.

2. **Supply Chain Resilience**
   - **Dual Sourcing**: Identify alternative suppliers for components sourced from high-risk regions.
   - **Inventory Buffers**: Maintain strategic stock for long-lead components to avoid production delays.
   - **Rail-First Distribution**: Expand the use of rail for domestic deliveries to minimize fuel costs and congestion risks.

3. **Demand-Side Levers**
   - **Policy Engagement**: Work with the government to ensure stable GST rates and incentives for EVs and hybrids.
   - **Affordability Programs**: Introduce financing options and value packs to enhance vehicle affordability.
   - **Export Diversification**: Explore new markets outside the Red Sea region to mitigate export risks.

4. **Energy Transition Hedge**
   - **CNG/Hybrid Portfolio Expansion**: Increase offerings in CNG and hybrid vehicles to reduce reliance on petrol and diesel.
   - **EV Readiness**: Align product development with India's EV roadmap to capitalize on the shift towards electric mobility.

---

## VI. Peer Positioning

### A. Comparative Analysis

| Company          | CNG Exposure | Margin Pressure | Nomura Rating |
|------------------|--------------|-----------------|---------------|
| **Maruti Suzuki** | High         | Significant      | Neutral       |
| **M&M**          | Moderate     | Moderate         | Buy           |
| **Tata Motors**  | Low          | Low              | Buy           |
| **TVS Motor**    | High         | Moderate         | Buy           |

Maruti Suzuki is rated **Neutral** by Nomura due to its high exposure to CNG and gas-intensive operations, making it more vulnerable compared to M&M and Tata Motors, which have lower reliance on gas ([Nomura Report](https://www.nomura.com)).

---

## VII. Financial Impact

### A. Cash Flows and Margins

The energy crisis is expected to compress Maruti's EBITDA margins by **80–100 bps** in Q4FY26 due to increased reliance on expensive spot LNG to fill the supply gap ([Business Standard](https://www.business-standard.com)). The company’s cash flows may also be affected as operational costs rise, potentially leading to a **15–25% increase** in manufacturing costs.

### B. Key Financial Metrics

| Metric                     | Value                |
|----------------------------|----------------------|
| Q3 FY26 Net Profit         | ₹3,879.1 cr (+4.1% YoY) |
| Q3 FY26 Revenue            | ₹47,537.2 cr (+29.2% YoY) |
| Cash and Equivalents       | Over ₹150 billion     |
| Average Selling Price      | ₹7.27 lakh (+7% YoY) |

---

## VIII. Macro Energy Context

The energy crisis, driven by geopolitical tensions and supply chain disruptions, has led to increased volatility in oil and gas prices. The current Brent crude price is approximately **$102.35/bbl**, which places additional pressure on Maruti's operational costs and margins. The reliance on CNG for manufacturing processes further exacerbates the risk, as any disruption in gas supply can halt production, particularly in paint shops that require consistent energy supply for curing processes.

---

## IX. Metrics to Monitor

### A. Country-Level KPIs

1. **GDP Growth Rate**: Monitor for economic resilience.
2. **Current Account Deficit (CAD)**: Assess oil import impacts.
3. **Fuel Demand**: Track petrol and diesel consumption trends.
4. **Brent Crude Prices**: Gauge global oil market fluctuations.

### B. Company-Level KPIs

1. **Inventory Days**: Monitor to ensure supply chain efficiency.
2. **Working Capital Ratio**: Assess liquidity and operational efficiency.
3. **Margin Trajectory**: Track EBITDA margins over time.
4. **Export Mix**: Evaluate the balance between domestic and international sales.

---

## X. Evaluation & Reflection

**What worked:**
- The report effectively highlighted the critical areas where Maruti Suzuki is impacted by the energy crisis.
- Mitigation strategies provided actionable insights for management to consider.
- Peer comparison offered a clear view of Maruti's positioning in the market.

**What to refine in next loop:**
- Further analysis of the correlation between shipping costs in the Red Sea region and Maruti Suzuki's operational costs could enhance understanding of external risks.

---

## Token Usage

| Metric | Count |
|--------|-------|
| Prompt tokens | 9,416 |
| Completion tokens | 2,084 |
| Total tokens | 11,500 |

---

*Report generated using the Karpathy AutoResearch methodology. Last updated: March 2026.*

Appendix F: Automation Script (energy_maruti_loop_v9.py)

Main loop script: V9 implementation with evaluation fixes, failure-driven refinements, and meta-analysis integration.

#!/usr/bin/env python3
"""
India Energy & Maruti Suzuki — Karpathy-Style Autonomous Analysis Loop (Version 9)

V9: Implements all recommendations from REPORT_India_Karpathy_Approach_Effectiveness.md

Key changes from V8:
1. FIX EVALUATION LOGIC: Strategy counting handles `1. **Bold Title**`; benchmark counting
   accepts V7 format (Country-Level/Company-Level KPIs with numbered items) and B1/B2.
2. FAILURE-DRIVEN REFINEMENTS: When evaluation fails, inject refinements from failure_summary
   into ideation/execution (not only from report reflection).
3. DEDUPLICATE REFLECTION: If "What to refine" repeats 2+ iters with no implementation,
   force different refinement or escalate.
4. RELAX all_refinements_done: Evaluation failure = refinements not done, even if keyword says done.
5. REDUCE REGRESSION: Stronger additive instruction; pre/post word-count diff; multi-refinement
   (2 refinements per iter when multiple gaps exist).
6. EXPLICIT STRUCTURE HINTS: Execution prompt lists required headings and format for evaluator compat.
7. HUMAN-IN-THE-LOOP: --checkpoint-after N and --checkpoint-on-pass for optional review pause.
8. META-ANALYSIS INTEGRATION: Run meta_analysis_reports.py periodically; feed findings into archive.

Uses PROBLEM_V7.md, maruti_energy_facts_v3.md.

Usage:
  python energy_maruti_loop_v9.py                    # 1 iteration
  python energy_maruti_loop_v9.py -n 3               # 3 iterations
  python energy_maruti_loop_v9.py --checkpoint-after 3  # Pause after 3 iters for review
  python energy_maruti_loop_v9.py --checkpoint-on-pass # Pause on first pass
"""

import argparse
import json
import os
import re
import subprocess
import sys
import time
from datetime import datetime
from pathlib import Path
from typing import List, Optional, Tuple

# Load .env from project root or automation/
try:
    from dotenv import load_dotenv
    base = Path(__file__).resolve().parent.parent
    load_dotenv(base / ".env")
    load_dotenv(base / "automation" / ".env")
except ImportError:
    pass

# Paths — V9
BASE = Path(__file__).resolve().parent.parent
REPORTS_DIR = BASE / "reports"
AUTOMATION_DIR = BASE / "automation"
PROBLEM_PATH = BASE / "automation" / "PROBLEM_V7.md"
FACTS_PATH = BASE / "data" / "maruti_energy_facts_v3.md"
FETCHED_PATH = BASE / "data" / "fetched.json"

REPORT_FOOTER = "*Report generated using the Karpathy AutoResearch methodology. Last updated:"

# Refinement templates — V9: same as V8
REFINEMENT_TEMPLATES = {
    "strategies": "### Mitigation Strategies — Add or expand a section on strategies Maruti can employ: commodity hedging, localisation, product mix (SUVs), supply chain dual-sourcing, rail-first distribution, CNG/hybrid expansion. Make each strategy actionable with implementation considerations.",
    "peer_positioning": "### Peer Positioning — Add a subsection comparing Maruti vs M&M, Tata, TVS, Eicher. Who is better/worse positioned given CNG exposure, paint shop reliance, supply chain, capacity? Cite Nomura ratings (Maruti Neutral vs M&M Buy). Explain why.",
    "cash_flow": "### Financial Impact — Cash Flows & Margins — Add analysis of impact on Maruti's cash flows: working capital (inventory, receivables), margin compression (80–100 bps from gas, 200 bps from commodities), capex implications. Include revenue/EBITDA figures from facts.",
    "company_metrics": "### Company-Level Metrics to Monitor — Add a subsection on KPIs Maruti management should track: inventory days, dealer stock, working capital ratio, margin trajectory, export mix, commodity basket index, gas consumption vs cap. Make each metric actionable.",
    "country_metrics": "### Country-Level Metrics — Add or expand metrics to gauge the crisis at macro level: Brent, CAD, fuel demand, Red Sea freight premium, gas supply allocation. Link how each feeds into Maruti risk.",
    "gas_crisis": "### Natural Gas Supply Order & Manufacturing Impact — Add a subsection on the March 2026 Natural Gas Supply Order. Explain: industrial consumers capped at 80%; paint curing, forging, casting as critical processes; just-in-time vulnerability; Tier-2/Tier-3 supplier risk.",
    "maruti_gas_exposure": "### Maruti Gas Exposure (Nomura) — Add analysis of Maruti's high exposure: large CNG portfolio, gas-intensive paint shop. Compare to Nomura ratings (Maruti Neutral vs M&M Buy). Include spot LNG cost impact (15–25%) and EBITDA margin compression (80–100 bps).",
    "cng_exposure_magnitude": "### Maruti CNG Exposure — Gas Squeeze Magnitude — Add analysis of Maruti's unique exposure: ~71–73% share of Indian CNG vehicle market; one in three cars sold is CNG; 43% of pending bookings for CNG; FY25 target 6 lakh units. Explain how LNG disruption erodes running cost advantage. Cite Autocar Professional, Fortune India.",
    "underbody_cng": "### Underbody CNG Breakthrough — Add a subsection on Maruti's underbody CNG technology: Victoris first; Brezza testing; Grand Vitara under evaluation. Compare to Tata iCNG and Hyundai Hy-CNG Duo. Explain how it addresses luggage space loss and expands CNG appeal. Cite Fortune India.",
    "ev_vs_cng": "### EV vs CNG Cost Comparison — Add analysis: CareEdge (EV 80% costlier vs petrol, CNG 12%); Crisil TCO (EV ₹16.7L vs CNG ₹20.46L over 2L km for heavy users); fuel tax (petrol ~100%, CNG 15–20%). Explain implications for Maruti's multi-powertrain strategy. Cite Outlook Business, CareEdge, Crisil.",
    "fuel_tax": "### Fuel Tax Structure — Add a subsection on India's fuel tax: petrol ~100% effective tax, CNG ~15–20%. Explain how this drives CNG's running cost advantage and Maruti's CNG focus. Cite Outlook Business.",
}


def _get_paths(version: int = 9) -> Tuple[Path, Path]:
    """Return (ARCHIVE_PATH, REPORT_PATH) from env or version-based defaults."""
    archive = os.environ.get("ARCHIVE_PATH")
    report = os.environ.get("REPORT_PATH")
    if archive and report:
        return Path(archive), Path(report)
    return BASE / f"archive_v{version}.log", BASE / "REPORT_India_Energy_Maruti_Suzuki_Analysis.md"


def strip_artifact_blocks(content: str) -> str:
    """Remove prompt artifacts (MANDATORY, hypotheses blocks); truncate at footer."""
    if not content or not content.strip():
        return content
    content = re.sub(r"\n##\s+MANDATORY:.*?(?=\n##|\n---|\Z)", "", content, flags=re.I | re.S)
    content = re.sub(r"\n##\s+This iteration's hypotheses\s*\n.*?(?=\n##|\n---|\Z)", "", content, flags=re.I | re.S)
    footer_match = re.search(r"(\*Report generated using the Karpathy AutoResearch methodology\. Last updated:.*?\*)", content, re.I)
    if footer_match:
        content = content[:footer_match.end()].rstrip()
    else:
        reflection_end = re.search(r"(.*?\*\*Archive instruction for Iteration N\+1:\*\*.*?)\n\n", content, re.I | re.S)
        if reflection_end:
            content = reflection_end.group(1).rstrip()
    return content


def read_archive(archive_path: Path) -> str:
    if archive_path.exists():
        return archive_path.read_text(encoding="utf-8")
    return ""


def read_problem() -> str:
    if PROBLEM_PATH.exists():
        return PROBLEM_PATH.read_text(encoding="utf-8")
    return ""


def read_facts() -> str:
    if FACTS_PATH.exists():
        return FACTS_PATH.read_text(encoding="utf-8")
    return ""


def extract_report_reflection(report_text: str) -> List[str]:
    report_text = strip_artifact_blocks(report_text)
    if not report_text:
        return []
    match = re.search(
        r"(?:what to refine|refine in next loop|refinements? for next)[\s:]+(.*?)(?=\*\*Archive|##\s|$)",
        report_text, re.I | re.S,
    )
    if not match:
        return []
    block = match.group(1).strip()
    items = re.findall(r"[-*]\s+(.+?)(?=\n[-*]|\n\n|\Z)", block, re.S)
    return [item.strip().lstrip("- ").strip() for item in items if len(item.strip()) > 10]


def _keywords_for_refinement(refinement: str) -> List[str]:
    """V9: Maruti-centric + gas crisis + v3 CNG/EV/underbody keywords."""
    r = refinement.lower()
    if "strateg" in r or "mitigat" in r or "hedging" in r or "localisation" in r:
        return ["strategy", "mitigation", "hedging", "localisation"]
    if "peer" in r or "position" in r or "m&m" in r or "tata" in r or "tvs" in r:
        return ["peer", "M&M", "Tata", "Nomura", "position"]
    if "cash flow" in r or "cashflow" in r or "margin" in r and "financial" in r:
        return ["cash flow", "margin", "EBITDA", "working capital"]
    if "company-level" in r or "company level" in r or "kpi" in r or "metric" in r and "monitor" in r:
        return ["company-level", "KPI", "inventory days", "working capital"]
    if "country-level" in r or "country level" in r or "macro" in r and "metric" in r:
        return ["country-level", "Brent", "CAD", "fuel demand"]
    if "natural gas" in r or "gas supply" in r or "80%" in r or "paint shop" in r:
        return ["Natural Gas Supply Order", "80%", "paint curing", "paint shop"]
    if "cng" in r or "nomura" in r or "gas-intensive" in r or "spot lng" in r:
        return ["CNG", "Nomura", "gas-intensive", "spot LNG"]
    if "cng exposure" in r or "71" in r or "72" in r or "73%" in r or "gas squeeze" in r or "magnitude" in r:
        return ["71", "72", "73%", "CNG market share", "gas squeeze"]
    if "underbody" in r or "victoris" in r or "brezza" in r or "icng" in r or "hy-cng" in r:
        return ["underbody", "Victoris", "Brezza", "iCNG", "Hy-CNG Duo"]
    if "ev vs cng" in r or "ev vs" in r or "careedge" in r or "crisil" in r and "tco" in r:
        return ["EV", "CNG", "CareEdge", "Crisil", "TCO"]
    if "fuel tax" in r or "petrol" in r and "100%" in r or "cng" in r and "15%" in r:
        return ["fuel tax", "100%", "15%", "20%"]
    if "stress test" in r or "90/bbl" in r:
        return ["stress test", "$90/bbl", "27,000"]
    if "commodity" in r or "cost structure" in r:
        return ["cost structure", "commodity"]
    if "correlation" in r and ("red sea" in r or "shipping" in r):
        return ["correlation", "operational cost"]
    words = [w.strip(".,") for w in re.split(r"\s+", refinement) if len(w) > 6]
    return words[:3] if words else [refinement[:30]]


def detect_implemented_refinements(report_text: str, refinement_list: List[str]) -> Tuple[List[str], List[str]]:
    if not report_text or not refinement_list:
        return [], list(refinement_list)
    report_text = strip_artifact_blocks(report_text)
    report_for_check = re.sub(r"##+\s+(?:VII|VIII|VI)\.?\s+Reflection.*$", "", report_text, flags=re.I | re.S)
    report_lower = report_for_check.lower()
    implemented, unimplemented = [], []
    for r in refinement_list:
        keywords = _keywords_for_refinement(r)
        found = any(kw.lower() in report_lower for kw in keywords)
        if found:
            implemented.append(r)
        else:
            unimplemented.append(r)
    return implemented, unimplemented


# --- Failure-driven refinements (V9) ---

def failure_to_refinements(failure_summary: str) -> List[str]:
    """
    Map evaluation failure_summary to concrete refinements to inject into next iteration.
    Returns list of refinement descriptions (can be passed to _refinement_to_template).
    """
    if not failure_summary or "All checks passed" in failure_summary:
        return []
    fs = failure_summary.lower()
    refinements = []
    if "strateg" in fs and ("0 found" in fs or "need ≥4" in fs):
        refinements.append("Add or expand mitigation strategies (≥4): hedging, localisation, supply chain, product mix.")
    if "benchmark" in fs and ("0 found" in fs or "need ≥4" in fs):
        refinements.append("Add country-level AND company-level KPIs as numbered lists (≥4 each).")
    if "company metric" in fs or "company-level" in fs:
        refinements.append("Add company-level metrics: inventory days, working capital, margin trajectory, export mix.")
    if "country metric" in fs or "country-level" in fs:
        refinements.append("Add country-level metrics: Brent, CAD, fuel demand, Red Sea freight.")
    if "peer" in fs:
        refinements.append("Add peer comparison: Maruti vs M&M, Tata, TVS — Nomura ratings.")
    if "cash flow" in fs or "financial" in fs:
        refinements.append("Add financial impact: cash flows, margins, working capital, EBITDA.")
    return refinements


# --- Deduplicate reflection (V9) ---

def extract_last_reflections_from_archive(archive: str, n: int = 3) -> List[str]:
    """Extract 'What to refine' from last N archive entries for stagnation detection."""
    entries = re.findall(r"---\s*\n\[.*?\]\s*\n(\{.*?\})", archive, re.S | re.DOTALL)
    reflections = []
    for e in entries[-n:]:
        try:
            d = json.loads(e)
            refs = d.get("refinements_pending", [])
            if isinstance(refs, list):
                reflections.extend(refs)
            elif isinstance(refs, str) and refs.strip():
                for line in refs.split("\n"):
                    m = re.match(r"[-*]\s+(.+)", line.strip())
                    if m and len(m.group(1)) > 10:
                        reflections.append(m.group(1).strip())
        except (json.JSONDecodeError, TypeError):
            pass
    return reflections


def is_reflection_stagnant(current_reflection: str, last_reflections: List[str]) -> bool:
    """True if current refinement appears in last 2+ archive entries with no implementation."""
    if not current_reflection or len(last_reflections) < 2:
        return False
    curr_lower = current_reflection.lower()[:80]
    matches = sum(1 for r in last_reflections if curr_lower in r.lower() or r.lower()[:80] in curr_lower)
    return matches >= 2


# --- Meta-analysis (V9: integrate into loop) ---

MARUTI_SUBSECTION_PATTERNS = [
    "mitigation", "strateg", "peer", "position", "cash flow", "financial impact",
    "company-level", "company metric", "country-level", "commodity basket", "cost structure",
    "red sea", "natural gas", "gas supply", "nomura", "operational response",
    "cng exposure", "gas squeeze", "underbody", "victoris", "ev vs cng", "fuel tax",
]


def _extract_maruti_subsections(text: str) -> List[str]:
    text = strip_artifact_blocks(text)
    subsections = []
    maruti_match = re.search(
        r"(?:##\s+[IVX]+\.?\s+Maruti|###\s+Maruti)[\s\S]*?(?=##\s+[IVX]+\.|##\s+[V]\.|$)",
        text, re.I,
    )
    if not maruti_match:
        return subsections
    block = maruti_match.group(0).lower()
    for m in re.finditer(r"###\s+(.+?)(?:\n|$)", block):
        heading = m.group(1).strip()
        if len(heading) > 5:
            subsections.append(heading)
    return subsections


def _subsections_match(a: str, b: str) -> bool:
    aa, bb = a.lower(), b.lower()
    for pat in MARUTI_SUBSECTION_PATTERNS:
        if pat in aa and pat in bb:
            return True
    wa, wb = set(re.findall(r"\w{4,}", aa)), set(re.findall(r"\w{4,}", bb))
    return len(wa & wb) >= 2


def run_meta_analysis(
    prev_report: str,
    current_report: str,
    targeted_refinement: Optional[str] = None,
) -> dict:
    prev_subs = _extract_maruti_subsections(prev_report)
    curr_subs = _extract_maruti_subsections(current_report)
    subsections_removed = [p for p in prev_subs if not any(_subsections_match(p, c) for c in curr_subs)]
    subsections_added = [c for c in curr_subs if not any(_subsections_match(c, p) for p in prev_subs)]
    regression_detected = len(subsections_removed) > 0
    reflection_updated = True
    if targeted_refinement:
        curr_reflections = extract_report_reflection(current_report)
        target_lower = targeted_refinement.lower()
        for r in curr_reflections:
            if target_lower in r.lower() or any(kw in r.lower() for kw in _keywords_for_refinement(targeted_refinement)):
                reflection_updated = False
                break
    return {
        "regression_detected": regression_detected,
        "subsections_removed": subsections_removed,
        "subsections_added": subsections_added,
        "reflection_updated": reflection_updated,
    }


def run_meta_analysis_script() -> Optional[str]:
    """Run meta_analysis_reports.py and return path to output. V9: periodic integration."""
    script = AUTOMATION_DIR / "meta_analysis_reports.py"
    if not script.exists():
        return None
    try:
        result = subprocess.run(
            [sys.executable, str(script)],
            cwd=BASE,
            capture_output=True,
            text=True,
            timeout=60,
        )
        if result.returncode == 0:
            # Extract output path from stdout
            m = re.search(r"Meta-analysis written to:.*?([^\s]+\.md)", result.stdout or "")
            return m.group(1).strip() if m else None
    except Exception:
        pass
    return None


def _refinement_to_template(refinement: str) -> Optional[str]:
    r = refinement.lower()
    if "strateg" in r or "mitigat" in r or "hedging" in r:
        return REFINEMENT_TEMPLATES["strategies"]
    if "peer" in r or "position" in r or "m&m" in r or "tata" in r or "tvs" in r:
        return REFINEMENT_TEMPLATES["peer_positioning"]
    if "cash flow" in r or "cashflow" in r or ("financial" in r and "impact" in r):
        return REFINEMENT_TEMPLATES["cash_flow"]
    if "company-level" in r or "company level" in r or "kpi" in r or ("metric" in r and "monitor" in r):
        return REFINEMENT_TEMPLATES["company_metrics"]
    if "country-level" in r or "country level" in r or ("macro" in r and "metric" in r):
        return REFINEMENT_TEMPLATES["country_metrics"]
    if "natural gas" in r or "gas supply" in r or "80%" in r or "paint shop" in r:
        return REFINEMENT_TEMPLATES["gas_crisis"]
    if "cng" in r and ("nomura" in r or "gas exposure" in r or "spot lng" in r):
        return REFINEMENT_TEMPLATES["maruti_gas_exposure"]
    if "cng exposure" in r or "gas squeeze" in r or "71" in r or "72" in r or "73%" in r or "magnitude" in r:
        return REFINEMENT_TEMPLATES["cng_exposure_magnitude"]
    if "underbody" in r or "victoris" in r or "brezza" in r or "icng" in r or "hy-cng" in r:
        return REFINEMENT_TEMPLATES["underbody_cng"]
    if "ev vs cng" in r or "ev vs" in r or "careedge" in r or ("crisil" in r and "tco" in r):
        return REFINEMENT_TEMPLATES["ev_vs_cng"]
    if "fuel tax" in r or ("petrol" in r and "100%" in r):
        return REFINEMENT_TEMPLATES["fuel_tax"]
    return None


# --- V9: Fixed evaluation logic ---

def _extract_report_stats(text: str) -> dict:
    """
    V9: Robust strategy and benchmark counting.
    - Strategy: Count ^\s*\d+\. in strategy section (handles 1. **Bold** and 1. Plain)
    - Benchmark: B1/B2 style OR numbered items in Country-Level/Company-Level KPI sections
    """
    text = strip_artifact_blocks(text)
    text_lower = text.lower()
    # Strategy section
    strategy_section = re.search(r"(?:strategies?|crisis management)[\s\S]{0,4000}", text_lower, re.I)
    strategy_block = strategy_section.group(0) if strategy_section else text_lower
    # V9: Count ^\s*\d+\. (robust — works with 1. **Bold** and 1. Plain)
    strategy_numered = len(re.findall(r"^\s*\d+\.\s+", strategy_block, re.M))
    strategy_old = len(re.findall(r"^\s*\d+\.\s+[A-Za-z*][^\n]{5,}", strategy_block, re.M))
    strategy_count = max(
        len(re.findall(r"#{2,4}\s+\d+\.", strategy_block)),
        strategy_numered,
        strategy_old,
        1 if "strategy" in strategy_block else 0,
    )
    # Benchmark: B1/B2 style OR V7 format (numbered items in Country-Level/Company-Level KPIs)
    b_style = len(re.findall(r"\b[bB]\d+\b", text))
    # V9: Target the "Metrics to Monitor" section specifically (has Country-Level + Company-Level KPIs)
    metrics_section = re.search(
        r"(?:##\s+[IVX]*\.?\s*metrics to monitor|##\s+VI\.\s+metrics)[\s\S]{0,2500}",
        text_lower, re.I,
    )
    if not metrics_section:
        metrics_section = re.search(
            r"###\s+[AB]\.\s+(?:country-level|company-level)\s+kpis?[\s\S]{0,2000}",
            text_lower, re.I,
        )
    metrics_block = metrics_section.group(0) if metrics_section else ""
    # Count numbered items (1. **GDP**, 2. **CAD**, etc.) in metrics section
    metrics_numbered = len(re.findall(r"^\s*\d+\.\s+", metrics_block, re.M))
    benchmark_count = max(b_style, metrics_numbered, 1 if "benchmark" in text_lower else 0)
    citation_count = len(re.findall(r"https?://[^\s\)\]>]+", text))
    figures = []
    for m in re.finditer(r"\$[\d.]+/?(?:bbl)?|[\d.]+%|[\d.]+(?:\s*mbpd|\s*bps)|[\d.]+(?:\s*lakh|\s*million)", text):
        figures.append(m.group(0).strip())
    return {
        "strategy_count": strategy_count,
        "benchmark_count": benchmark_count,
        "citation_count": citation_count,
        "key_figures": list(dict.fromkeys(figures))[:15],
        "word_count": len(text.split()),
    }


def evaluate_report(report_path: Path) -> dict:
    """V9: Maruti-centric evaluation with fixed strategy/benchmark counting."""
    if not report_path.exists():
        return {"score": 0, "checks": {}, "passed": False, "stats": {}, "failure_summary": "No report exists.", "targets": {}}
    text = strip_artifact_blocks(report_path.read_text(encoding="utf-8"))
    text_lower = text.lower()
    stats = _extract_report_stats(text)

    maruti_ok = any(k in text_lower for k in ["maruti", "suzuki", "automotive", "automobile"])
    strategies_ok = stats["strategy_count"] >= 4
    has_peer_comparison = any(k in text_lower for k in ["m&m", "tata", "tvs", "eicher", "peer", "nomura", "competitor"])
    has_cash_flow = any(k in text_lower for k in ["cash flow", "cashflow", "working capital", "ebitda", "margin"])
    has_company_metrics = any(k in text_lower for k in ["company-level", "company level", "inventory days", "kpi", "metric"])
    has_country_metrics = any(k in text_lower for k in ["brent", "cad", "fuel demand", "country-level", "macro"])
    citations_ok = stats["citation_count"] >= 1 or "http" in text or "reuters" in text_lower
    has_objectives_table = "objective" in text_lower and ("success metric" in text_lower or "primary goal" in text_lower)

    has_maruti_volume = any(k in text for k in ["1.8", "1.80", "million units", "525,935", "178,646"])
    has_margin_figure = any(k in text_lower for k in ["margin", "bps", "200 bps", "1.5%", "operating margin"])
    has_elasticity = any(k in text_lower for k in ["elasticity", "-0.214", "0.12", "0.15"])
    has_brent_or_freight = any(k in text for k in ["$/bbl", "$73", "$80", "freight", "premium"])
    has_gst_or_policy = any(k in text_lower for k in ["gst", "28%", "18%", "tax cut"])
    hard_stats_count = sum([has_maruti_volume, has_margin_figure, has_elasticity, has_brent_or_freight, has_gst_or_policy])
    hard_stats_ok = hard_stats_count >= 3

    energy_ok = any(k in text_lower for k in ["energy", "oil", "shipping", "red sea", "gas"])
    has_gas_crisis = any(k in text_lower for k in ["natural gas", "gas supply", "80%", "paint shop"])
    benchmarks_ok = stats["benchmark_count"] >= 4

    has_cng_exposure = any(k in text for k in ["71", "72", "73%", "6.2 lakh", "6 lakh"]) or "cng market share" in text_lower

    checks = {
        "maruti_impact": maruti_ok,
        "strategies": strategies_ok,
        "peer_comparison": has_peer_comparison,
        "cash_flow": has_cash_flow,
        "company_metrics": has_company_metrics,
        "country_metrics": has_country_metrics,
        "energy_context": energy_ok,
        "objectives_table": has_objectives_table,
        "benchmarks": benchmarks_ok,
        "citations": citations_ok,
        "hard_stats": hard_stats_ok,
        "gas_crisis": has_gas_crisis,
        "cng_exposure_v3": has_cng_exposure,
    }
    score = sum(checks.values()) / len(checks) * 100
    passed = (
        score >= 70
        and maruti_ok
        and strategies_ok
        and (has_peer_comparison or has_cash_flow)
        and (has_company_metrics or has_country_metrics)
        and has_objectives_table
    )

    failures = []
    if not maruti_ok:
        failures.append("Maruti impact: missing Maruti Suzuki / automotive sector analysis.")
    if not strategies_ok:
        failures.append(f"Strategies: {stats['strategy_count']} found (need ≥4 mitigation strategies).")
    if not has_peer_comparison:
        failures.append("Peer comparison: missing comparison vs M&M, Tata, TVS, or Nomura ratings.")
    if not has_cash_flow:
        failures.append("Financial impact: missing cash flow, margin, or EBITDA analysis.")
    if not has_company_metrics:
        failures.append("Company metrics: missing company-level KPIs to monitor (inventory, working capital, etc.).")
    if not has_country_metrics:
        failures.append("Country metrics: missing country-level metrics (Brent, CAD, fuel demand).")
    if not energy_ok:
        failures.append("Energy context: missing oil/gas/shipping impact on Maruti.")
    if not has_objectives_table:
        failures.append("Structure: missing Objectives & Success Metrics table.")
    if not benchmarks_ok:
        failures.append(f"Benchmarks: {stats['benchmark_count']} found (need ≥4).")
    if not citations_ok:
        failures.append(f"Citations: {stats['citation_count']} URLs (need ≥1).")
    if not hard_stats_ok:
        missing = []
        if not has_maruti_volume:
            missing.append("Maruti volume")
        if not has_margin_figure:
            missing.append("margin/bps")
        if not has_elasticity:
            missing.append("elasticity")
        failures.append(f"Hard stats: {hard_stats_count}/5 present (need ≥3). Missing: {', '.join(missing[:3])}.")

    stats["hard_stats_present"] = hard_stats_count
    stats["cng_exposure_present"] = has_cng_exposure

    return {
        "score": round(score, 1),
        "checks": checks,
        "passed": passed,
        "stats": stats,
        "failure_summary": "; ".join(failures) if failures else "All checks passed.",
        "targets": {
            "strategy_count": stats["strategy_count"],
            "benchmark_count": stats["benchmark_count"],
            "citation_count": stats["citation_count"],
        },
    }


def append_archive(archive_path: Path, entry: dict) -> None:
    timestamp = datetime.utcnow().isoformat() + "Z"
    block = f"\n---\n[{timestamp}]\n{json.dumps(entry, indent=2)}\n"
    with open(archive_path, "a", encoding="utf-8") as f:
        f.write(block)


def _fetch_fresh_data() -> str:
    try:
        parent = Path(__file__).resolve().parent.parent
        if str(parent) not in sys.path:
            sys.path.insert(0, str(parent))
        from automation.fetch_data import run_fetch, format_for_prompt
        return format_for_prompt(run_fetch())
    except Exception as e:
        print("[EXECUTION] Fetch error:", e)
        return ""


def run_ideation(
    archive: str,
    refinements_pending: List[str],
    dry_run: bool,
    failure_summary: Optional[str] = None,
    failure_driven_refinements: Optional[List[str]] = None,
) -> dict:
    """V9: Ideation with failure-driven refinements when evaluation fails."""
    # V9: Prepend failure-driven refinements when evaluation failed
    effective_pending = list(refinements_pending)
    if failure_driven_refinements:
        for fr in failure_driven_refinements:
            if fr not in effective_pending:
                effective_pending.insert(0, fr)
    refinements_block = "\n".join(f"- {r}" for r in effective_pending) if effective_pending else ""

    if dry_run:
        return {
            "hypotheses": ["H1: Maruti mitigation strategies.", "H2: Peer comparison.", "H3: CNG exposure."],
            "refinements_pending": refinements_block or "First run.",
            "refinements_from_archive": "Review archive.",
        }
    api_key = os.environ.get("OPENAI_API_KEY")
    if not api_key:
        return {
            "hypotheses": ["H1: Maruti strategies.", "H2: Peer comparison.", "H3: CNG exposure."],
            "refinements_pending": refinements_block or "No prior.",
            "refinements_from_archive": "No LLM.",
        }
    try:
        from openai import OpenAI
        client = OpenAI(api_key=api_key)
        archive_truncated = archive[-5000:] if len(archive) > 5000 else archive
        user_content = f"Archive:\n{archive_truncated}\n"
        if failure_summary and "All checks passed" not in failure_summary:
            user_content += f"EVALUATION FAILED — address these gaps:\n{failure_summary}\n\n"
        if effective_pending:
            user_content += f"PRIORITY: Target at least one:\n{refinements_block}\n"
        else:
            user_content += "All prior refinements done. Propose NEW content: CNG exposure magnitude, underbody CNG, EV vs CNG cost, fuel tax, cash flow sensitivity, or peer comparison depth."
        response = client.chat.completions.create(
            model=os.environ.get("OPENAI_MODEL", "gpt-4o-mini"),
            messages=[
                {
                    "role": "system",
                    "content": "Output JSON: {\"hypotheses\": [\"H1: ...\", \"H2: ...\"], \"refinements_pending\": \"...\", \"refinements_from_archive\": \"...\"}. Target refinements when provided. When evaluation failed, prioritize addressing the failure gaps. Focus: Maruti Suzuki — mitigation strategies, peer positioning, cash flow/margin, company/country metrics, gas crisis, v3 CNG/EV data.",
                },
                {"role": "user", "content": user_content},
            ],
            temperature=0.5,
        )
        content = response.choices[0].message.content.strip()
        if "```" in content:
            content = content.split("```")[1]
            if content.startswith("json"):
                content = content[4:]
        parsed = json.loads(content)
        parsed["refinements_pending"] = refinements_block or parsed.get("refinements_pending", "")
        return parsed
    except Exception as e:
        print("[IDEATION] LLM error:", e)
        return {
            "hypotheses": ["H1: Maruti strategies.", "H2: Peer comparison.", "H3: CNG exposure."],
            "refinements_pending": refinements_block,
            "refinements_from_archive": str(e),
        }


def run_improvement(improve_cmd: str, archive_path: Path, report_path: Path) -> bool:
    if not improve_cmd:
        return False
    if "improve_report.py" in improve_cmd and "automation/" not in improve_cmd:
        improve_cmd = improve_cmd.replace("improve_report.py", "automation/improve_report.py")
    try:
        result = subprocess.run(
            improve_cmd, shell=True, cwd=BASE,
            env={**dict(os.environ), "ARCHIVE_PATH": str(archive_path), "REPORT_PATH": str(report_path)},
            timeout=300,
        )
        return result.returncode == 0
    except subprocess.TimeoutExpired:
        print("[IMPROVE] Command timed out")
        return False
    except Exception as e:
        print("[IMPROVE] Error:", e)
        return False


def _format_token_usage_section(usage: dict) -> str:
    pt = usage.get("prompt_tokens", 0)
    ct = usage.get("completion_tokens", 0)
    tt = usage.get("total_tokens", pt + ct)
    return f"""
## Token Usage

| Metric | Count |
|--------|-------|
| Prompt tokens | {pt:,} |
| Completion tokens | {ct:,} |
| Total tokens | {tt:,} |
"""


def _append_token_usage_section(report_text: str, usage: dict) -> str:
    if not usage or (usage.get("total_tokens", 0) == 0 and usage.get("prompt_tokens", 0) == 0):
        return report_text
    section = _format_token_usage_section(usage)
    footer_match = re.search(
        r"(\*Report generated using the Karpathy AutoResearch methodology\. Last updated:.*?\*)",
        report_text, re.I,
    )
    if footer_match:
        before = report_text[: footer_match.start()].rstrip()
        footer = report_text[footer_match.start() :]
        return before + "\n\n" + section.strip() + "\n\n---\n\n" + footer
    return report_text.rstrip() + "\n\n" + section.strip() + "\n"


# V9: Explicit structure hints for evaluator compatibility
STRUCTURE_HINTS = """
FORMAT FOR EVALUATOR COMPATIBILITY:
- Mitigation Strategies: Use numbered list, e.g. "1. **Strategy Name**" or "1. Strategy Name" (≥4 items).
- Metrics to Monitor: Use "Country-Level KPIs" and "Company-Level KPIs" subsections with numbered items, e.g. "1. **GDP Growth Rate**", "2. **CAD**" (≥4 each, or B1/B2 style).
- Do NOT remove or shorten existing sections. ADD new content. Preserve word count of strategies, peer comparison, and metrics.
"""


def _call_llm_improve(
    problem: str, archive: str, current_report: str, ideation: dict,
    refinements_pending: List[str], all_refinements_done: bool, fresh_data: str,
    standalone: bool, report_path: Path,
    retry_reason: Optional[str] = None,
    multi_refinement: bool = False,
) -> Tuple[Optional[str], Optional[dict]]:
    """V9: Extended prompt with structure hints, stronger additive instruction."""
    api_key = os.environ.get("OPENAI_API_KEY")
    if not api_key:
        return None, None
    try:
        from openai import OpenAI
        client = OpenAI(api_key=api_key)
    except ImportError:
        print("[EXECUTION] Install openai: pip install openai")
        return None, None

    archive_truncated = archive[-4000:] if len(archive) > 4000 else archive
    current_truncated = strip_artifact_blocks(current_report)
    current_truncated = current_truncated[-7000:] if len(current_truncated) > 7000 else current_truncated
    facts = read_facts()
    facts_block = f"\n## REQUIRED: maruti_energy_facts_v3.md — use these facts, cite sources\n{facts}\n" if facts else ""
    fresh_block = f"\n{fresh_data}\n" if fresh_data else ""

    # V9: Multi-refinement — take first 2 when multiple gaps
    refs_to_impl = refinements_pending[:2] if multi_refinement and len(refinements_pending) >= 2 else refinements_pending[:1]

    refinements_block = ""
    preservation_note = """
CRITICAL: ADD the new section(s). Do NOT remove or shorten existing sections. Preserve word count of strategies, peer comparison, cash flow, company metrics, gas crisis, v3 CNG/EV sections."""

    if standalone:
        refinements_block = """Improve the report. V9 MARUTI-CENTRIC + v3 DATA. Include:
1. Maruti Suzuki & automotive sector impact.
2. Mitigation strategies (≥4): format as "1. **Strategy Name**" — hedging, localisation, supply chain, product mix.
3. Peer positioning: Maruti vs M&M, Tata, TVS (Nomura ratings).
4. Financial impact: cash flows, margins, working capital, EBITDA.
5. Metrics to Monitor: Country-Level KPIs and Company-Level KPIs as numbered lists (≥4 each).
6. V3 DATA: CNG exposure (71–73%), underbody CNG, EV vs CNG cost, fuel tax, gas squeeze.
""" + STRUCTURE_HINTS
    elif refs_to_impl:
        templates = []
        for r in refs_to_impl:
            t = _refinement_to_template(r)
            templates.append(t if t else f"- {r}")
        refinements_block = f"""
ADD THESE SECTION(S) (implement up to {len(refs_to_impl)} refinements):
""" + "\n\n".join(templates) + f"""
{preservation_note}

After adding, REMOVE implemented items from "What to refine in next loop" in the Reflection section."""
    elif all_refinements_done:
        refinements_block = """
ADD NEW analytical content: (a) CNG exposure magnitude, (b) Underbody CNG, (c) EV vs CNG cost, (d) Cash flow sensitivity, (e) Deeper peer comparison.
""" + STRUCTURE_HINTS

    system_prompt = """You are an expert analyst. Output ONLY the improved markdown report.

CRITICAL: Output ONLY the report. End with Reflection section and footer "*Report generated using the Karpathy AutoResearch methodology. Last updated: March 2026.*"
Do NOT append MANDATORY, hypotheses, or meta-commentary.

V9 MARUTI-CENTRIC STRUCTURE:
1. Objective & Success Metrics
2. Maruti Suzuki & Automotive Sector — Impact
3. Mitigation Strategies (≥4) — format: "1. **Cost & Margin Defence**", "2. **Supply Chain Resilience**", etc.
4. Peer Positioning (Maruti vs M&M, Tata, TVS)
5. Financial Impact (cash flows, margins, EBITDA)
6. Macro Energy Context
7. Metrics to Monitor — Country-Level KPIs (numbered list ≥4) and Company-Level KPIs (numbered list ≥4)
8. Evaluation, Reflection
""" + STRUCTURE_HINTS + """
Use facts from maruti_energy_facts_v3.md. Cite sources."""

    retry_block = ""
    if retry_reason == "regression":
        retry_block = """
## REGRESSION DETECTED (retry)
The previous output REMOVED existing content. You MUST add the new content WITHOUT removing strategies, peer comparison, cash flow, company metrics, gas crisis, or v3 CNG/EV sections.

"""
    hypotheses_json = json.dumps(ideation.get("hypotheses", []), indent=2) if ideation else "[]"
    user_prompt = f"""{retry_block}## Problem
{problem}
{facts_block}
{fresh_block}
## Archive
{archive_truncated}

## Current Report
{current_truncated}
{refinements_block}

## Hypotheses (do NOT include in output)
{hypotheses_json}

Improve the report. V9: Keep Maruti-centric focus. Use v3 facts. Output complete markdown. End with Reflection and footer only."""

    try:
        print("[EXECUTION] Calling LLM (30–90s typical)...", flush=True)
        response = client.chat.completions.create(
            model=os.environ.get("OPENAI_MODEL", "gpt-4o-mini"),
            messages=[{"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}],
            temperature=0.3,
        )
        content = response.choices[0].message.content
        if content.startswith("```"):
            lines = content.split("\n")
            if lines[0].startswith("```"):
                lines = lines[1:]
            if lines and lines[-1].strip() == "```":
                lines = lines[:-1]
            content = "\n".join(lines)
        content = strip_artifact_blocks(content.strip())
        usage = {}
        if hasattr(response, "usage") and response.usage:
            u = response.usage
            usage = {
                "prompt_tokens": getattr(u, "prompt_tokens", 0) or 0,
                "completion_tokens": getattr(u, "completion_tokens", 0) or 0,
                "total_tokens": getattr(u, "total_tokens", 0) or 0,
            }
        return content, usage
    except Exception as e:
        print("[EXECUTION] LLM error:", e)
        return None, None


def _postprocess_reflection_removal(report_text: str, refinement_to_remove: str) -> str:
    match = re.search(
        r"((?:\*\*)?What to refine in next loop(?:\*\*)?[\s:]*\n)(.*?)(?=\n\n|\*\*[A-Z]|##\s|\Z)",
        report_text, re.I | re.S,
    )
    if not match:
        return report_text
    prefix, block, suffix = match.group(1), match.group(2), report_text[match.end() :]
    ref_lower = refinement_to_remove.lower()
    lines = [ln for ln in block.split("\n") if ln.strip()]
    kept = []
    for ln in lines:
        ln_stripped = ln.lstrip("-* ").strip()
        if ref_lower in ln_stripped.lower() or any(kw in ln_stripped.lower() for kw in _keywords_for_refinement(refinement_to_remove)):
            continue
        kept.append(ln)
    if len(kept) == len(lines):
        return report_text
    new_block = "\n".join(kept) if kept else "- (None — all refinements implemented.)"
    return report_text[: match.start()] + prefix + new_block + "\n\n" + suffix


def run_execution(
    ideation: dict, archive: str, refinements_pending: List[str], all_refinements_done: bool,
    dry_run: bool, skip_fetch: bool, iteration: int, version: int,
    archive_path: Path, report_path: Path, standalone: bool,
    multi_refinement: bool = False,
) -> Tuple[bool, Optional[dict], Optional[dict]]:
    """V9: Pre/post word-count diff; multi-refinement support."""
    if dry_run:
        print("[DRY-RUN] Would call LLM and write to", report_path)
        return False, None, None
    current_report = report_path.read_text(encoding="utf-8") if report_path.exists() else "# India Energy & Maruti Suzuki Report\n\n(Empty)"
    prev_word_count = len(strip_artifact_blocks(current_report).split()) if current_report.strip() else 0
    fresh_data = "" if skip_fetch else _fetch_fresh_data()
    if fresh_data:
        print("[EXECUTION] Fetched fresh data.")
    targeted_refs = refinements_pending[:2] if multi_refinement and len(refinements_pending) >= 2 else (refinements_pending[:1] if refinements_pending else [])
    targeted_ref = targeted_refs[0] if targeted_refs else None

    improved, usage = _call_llm_improve(
        read_problem(), archive, current_report, ideation,
        refinements_pending, all_refinements_done, fresh_data,
        standalone, report_path,
        retry_reason=None,
        multi_refinement=multi_refinement,
    )
    total_usage = dict(usage) if usage else {}
    meta = None
    if improved and current_report.strip():
        meta = run_meta_analysis(current_report, improved, targeted_ref)
        # V9: Pre/post word-count regression — retry if >10% drop
        new_word_count = len(strip_artifact_blocks(improved).split())
        if prev_word_count >= 400 and new_word_count < prev_word_count * 0.9:
            print("[META] Word count regression:", prev_word_count, "->", new_word_count, "— retrying with preservation")
            improved_retry, usage_retry = _call_llm_improve(
                read_problem(), archive, current_report, ideation,
                refinements_pending, all_refinements_done, fresh_data,
                standalone, report_path,
                retry_reason="regression",
                multi_refinement=multi_refinement,
            )
            if usage_retry:
                total_usage = {
                    "prompt_tokens": total_usage.get("prompt_tokens", 0) + usage_retry.get("prompt_tokens", 0),
                    "completion_tokens": total_usage.get("completion_tokens", 0) + usage_retry.get("completion_tokens", 0),
                    "total_tokens": total_usage.get("total_tokens", 0) + usage_retry.get("total_tokens", 0),
                }
            if improved_retry:
                new_wc_retry = len(strip_artifact_blocks(improved_retry).split())
                if new_wc_retry >= prev_word_count * 0.9:
                    improved, meta = improved_retry, run_meta_analysis(current_report, improved_retry, targeted_ref)
                    print("[META] Retry succeeded — word count preserved")
        if meta["regression_detected"] and targeted_ref:
            print("[META] Regression: subsections removed:", meta["subsections_removed"], "— retrying")
            improved_retry, usage_retry = _call_llm_improve(
                read_problem(), archive, current_report, ideation,
                refinements_pending, all_refinements_done, fresh_data,
                standalone, report_path,
                retry_reason="regression",
                multi_refinement=multi_refinement,
            )
            if usage_retry:
                total_usage = {
                    "prompt_tokens": total_usage.get("prompt_tokens", 0) + usage_retry.get("prompt_tokens", 0),
                    "completion_tokens": total_usage.get("completion_tokens", 0) + usage_retry.get("completion_tokens", 0),
                    "total_tokens": total_usage.get("total_tokens", 0) + usage_retry.get("total_tokens", 0),
                }
            if improved_retry:
                meta_retry = run_meta_analysis(current_report, improved_retry, targeted_ref)
                if not meta_retry["regression_detected"]:
                    improved, meta = improved_retry, meta_retry
                    print("[META] Retry succeeded — no regression")
        if meta and not meta["reflection_updated"] and targeted_ref:
            improved = _postprocess_reflection_removal(improved, targeted_ref)
            print("[META] Post-processed reflection: removed implemented refinement")
    if improved:
        improved = _append_token_usage_section(improved, total_usage)
        if REPORT_FOOTER not in improved:
            improved = improved.rstrip() + f"\n\n---\n\n{REPORT_FOOTER} March 2026.*\n"
        report_path.write_text(improved, encoding="utf-8")
        REPORTS_DIR.mkdir(parents=True, exist_ok=True)
        ts = datetime.utcnow().strftime("%Y-%m-%dT%H-%M-%SZ")
        suffix = "standalone" if standalone else f"v{version}_iter{iteration}"
        copy_path = REPORTS_DIR / f"REPORT_{suffix}_{ts}.md"
        copy_path.write_text(improved, encoding="utf-8")
        print(f"[EXECUTION] Report updated. Copy: reports/{copy_path.name}")
        if meta:
            print("[META]", "regression:", meta["regression_detected"], "| reflection_updated:", meta["reflection_updated"])
        if total_usage:
            print("[TOKENS]", total_usage.get("total_tokens", 0), "total")
        return True, meta, total_usage
    return False, None, None


def run_standalone(archive_path: Path, report_path: Path, skip_fetch: bool, dry_run: bool = False) -> int:
    if dry_run:
        print("[DRY-RUN] Would run one improve pass (standalone mode)")
        return 0
    if not os.environ.get("OPENAI_API_KEY"):
        print("Set OPENAI_API_KEY to use standalone mode.", file=sys.stderr)
        return 1
    archive = read_archive(archive_path)
    current_report = report_path.read_text(encoding="utf-8") if report_path.exists() else "# Report\n\n(Empty)"
    fresh_data = "" if skip_fetch else _fetch_fresh_data()
    ideation = {"hypotheses": []}
    improved, usage = _call_llm_improve(
        read_problem(), archive, current_report, ideation,
        [], False, fresh_data, True, report_path,
    )
    if improved:
        improved = _append_token_usage_section(improved, usage or {})
        if REPORT_FOOTER not in improved:
            improved = improved.rstrip() + f"\n\n---\n\n{REPORT_FOOTER} March 2026.*\n"
        report_path.write_text(improved, encoding="utf-8")
        REPORTS_DIR.mkdir(parents=True, exist_ok=True)
        ts = datetime.utcnow().strftime("%Y-%m-%dT%H-%M-%SZ")
        copy_path = REPORTS_DIR / f"REPORT_v9_standalone_{ts}.md"
        copy_path.write_text(improved, encoding="utf-8")
        print("Report updated successfully. Copy:", copy_path.name)
        return 0
    return 1


def _warm_start_if_needed(report_path: Path, archive_path: Path, version: int) -> bool:
    """Seed report from latest prior-version report when archive empty and report short."""
    archive = read_archive(archive_path)
    if archive.strip():
        return False
    current = report_path.read_text(encoding="utf-8") if report_path.exists() else ""
    if len(current.split()) >= 400:
        return False
    # Try prev version first, then any v2..v{version-1} (standalone or iter)
    for v in range(version - 1, 1, -1):
        candidates = list(REPORTS_DIR.glob(f"REPORT_v{v}_iter*.md")) + list(REPORTS_DIR.glob(f"REPORT_v{v}_standalone*.md"))
        if not candidates:
            continue

        def mtime(p: Path) -> float:
            return p.stat().st_mtime if p.exists() else 0

        best = max(candidates, key=mtime)
        seed = best.read_text(encoding="utf-8")
        report_path.write_text(seed, encoding="utf-8")
        print(f"[WARM START] Seeded from {best.name} (no prior archive)\n")
        return True
    return False


def run_loop(
    iterations: Optional[int], minutes: Optional[float], min_interval: float,
    dry_run: bool, improve_cmd: Optional[str], skip_fetch: bool, version: int,
    checkpoint_after: Optional[int] = None,
    checkpoint_on_pass: bool = False,
    meta_analysis_interval: int = 5,
) -> None:
    archive_path, report_path = _get_paths(version)
    archive = read_archive(archive_path)

    if not dry_run:
        if _warm_start_if_needed(report_path, archive_path, version):
            archive = read_archive(archive_path)

    if minutes is not None:
        end_time = time.monotonic() + (minutes * 60)
        iterations = 999_999
        print(f"=== Running V{version} for {minutes} minutes ===\n")
    else:
        end_time = None
        iterations = iterations or 1

    print(f"=== India Energy & Maruti Suzuki — Loop V{version} ===\n")
    print("V9: Fixed evaluator, failure-driven refinements, multi-refinement, structure hints, meta-analysis")
    print("Archive:", archive_path, "| Report:", report_path)
    if archive:
        print("Archive loaded:", len(archive), "chars\n")
    else:
        print("No prior archive.\n")

    i, passed_count, best_score = 0, 0, 0.0
    prev_eval_passed = False

    while i < iterations:
        if end_time is not None and time.monotonic() >= end_time:
            break
        i += 1
        print(f"--- Iteration {i} ---")

        current_report = report_path.read_text(encoding="utf-8") if report_path.exists() else ""
        all_refinements = extract_report_reflection(current_report)
        implemented, refinements_pending = detect_implemented_refinements(current_report, all_refinements)
        # V9: Relax all_refinements_done — evaluation failure means not done
        all_refinements_done = len(all_refinements) > 0 and len(refinements_pending) == 0
        # Will be overridden after evaluation if we have prior eval result

        # V9: Run meta-analysis periodically
        if not dry_run and meta_analysis_interval > 0 and i > 0 and i % meta_analysis_interval == 0:
            out_path = run_meta_analysis_script()
            if out_path:
                print("[META-ANALYSIS] Run complete:", out_path)
                # Append summary to archive context for next ideation (via archive)
                try:
                    summary = Path(out_path).read_text(encoding="utf-8")[:1500] if Path(out_path).exists() else ""
                    if summary:
                        archive += f"\n[Meta-analysis summary]\n{summary}\n"
                except Exception:
                    pass

        # V9: Deduplicate — if reflection stagnant, force different refinement
        first_pending = refinements_pending[0] if refinements_pending else None
        last_refs = extract_last_reflections_from_archive(archive, n=3)
        if first_pending and is_reflection_stagnant(first_pending, last_refs):
            print("[Reflection] Stagnant — forcing different refinement (escalate)")
            # Replace first with a generic "escalate" refinement
            refinements_pending = ["Add new analytical section: CNG exposure magnitude, underbody CNG, or EV vs CNG cost comparison."] + [r for r in refinements_pending if r != first_pending]

        if refinements_pending:
            print(f"[Reflection] Pending: {refinements_pending[0][:60]}...")
        if all_refinements_done:
            print("[Reflection] All done — stretch: add NEW analytical content")

        # V9: Failure-driven refinements (injected after eval from prev iter)
        failure_driven = []
        failure_summary_for_ideation = None
        if not prev_eval_passed and archive.strip():
            # Get last eval failure from archive
            entries = re.findall(r"---\s*\n\[.*?\]\s*\n(\{.*?\})", archive, re.S)
            for e in reversed(entries[-3:]):
                try:
                    d = json.loads(e)
                    ev = d.get("evaluation", {})
                    if ev and not ev.get("passed", True):
                        fs = ev.get("failure_summary", "")
                        if fs and "All checks passed" not in fs:
                            failure_summary_for_ideation = fs
                            failure_driven = failure_to_refinements(fs)
                            break
                except (json.JSONDecodeError, TypeError):
                    pass

        print("[1/4 Ideation]")
        ideation = run_ideation(
            archive, refinements_pending, dry_run,
            failure_summary=failure_summary_for_ideation,
            failure_driven_refinements=failure_driven if failure_driven else None,
        )
        print("  Hypotheses:", json.dumps(ideation.get("hypotheses", []))[:150], "...")

        # V9: Multi-refinement when multiple gaps (e.g. strategies + benchmarks both fail)
        multi_refinement = len(failure_driven) >= 2 or (len(refinements_pending) >= 2 and len(failure_driven) >= 1)

        print("[2/4 Execution]")
        updated, meta_analysis, token_usage = run_execution(
            ideation, archive, refinements_pending, all_refinements_done,
            dry_run, skip_fetch, i, version, archive_path, report_path, False,
            multi_refinement=multi_refinement,
        )

        print("[3/4 Evaluation]")
        eval_result = evaluate_report(report_path)
        score, passed = eval_result["score"], eval_result["passed"]
        prev_eval_passed = passed
        if passed:
            passed_count += 1
        best_score = max(best_score, score)
        # V9: Relax all_refinements_done when evaluation fails
        if not passed:
            all_refinements_done = False
        cng_stat = eval_result.get("stats", {}).get("cng_exposure_present", "?")
        print("Evaluation:", score, "%", "PASS" if passed else "FAIL", "| cng_v3:", cng_stat, "|", eval_result["failure_summary"][:60])

        # V9: Human-in-the-loop checkpoint
        if checkpoint_on_pass and passed:
            print("\n*** CHECKPOINT: Report passed. Pausing for human review. Press Enter to continue or Ctrl+C to stop. ***")
            try:
                input()
            except (KeyboardInterrupt, EOFError):
                print("Stopped by user.")
                break
        if checkpoint_after and i >= checkpoint_after:
            print(f"\n*** CHECKPOINT: Reached {i} iterations. Pausing for human review. Press Enter to continue or Ctrl+C to stop. ***")
            try:
                input()
            except (KeyboardInterrupt, EOFError):
                print("Stopped by user.")
                break

        print("[4/4 Archive]")
        fetched_snapshot = {}
        if FETCHED_PATH.exists():
            try:
                fd = json.loads(FETCHED_PATH.read_text(encoding="utf-8"))
                yf = fd.get("data", {}).get("yfinance", {})
                if yf:
                    fetched_snapshot = {
                        "brent": yf.get("brent", {}).get("value"),
                        "wti": yf.get("wti", {}).get("value"),
                        "maruti": yf.get("maruti", {}).get("value"),
                        "fetched_at": fd.get("timestamp"),
                    }
            except Exception:
                pass
        entry = {
            "iteration": i,
            "timestamp": datetime.utcnow().isoformat() + "Z",
            "fetched_data": fetched_snapshot,
            "ideation": ideation,
            "refinements_implemented": implemented,
            "refinements_pending": refinements_pending,
            "all_refinements_done": all_refinements_done,
            "evaluation": eval_result,
            "report_updated": updated,
            "meta_analysis": meta_analysis,
            "token_usage": token_usage,
        }
        if not dry_run:
            append_archive(archive_path, entry)
            if not passed and improve_cmd:
                if run_improvement(improve_cmd, archive_path, report_path):
                    archive = read_archive(archive_path)
        archive += json.dumps(entry) + "\n"

        if end_time is not None and time.monotonic() < end_time:
            sleep_time = min(min_interval, end_time - time.monotonic())
            if sleep_time > 0:
                print(f"  Sleeping {sleep_time:.0f}s...")
                time.sleep(sleep_time)

    print(f"\n=== Loop V{version} complete ===\n  Iterations: {i} | Passed: {passed_count} | Best: {best_score}%")


def main() -> int:
    parser = argparse.ArgumentParser(description="India Energy & Maruti Suzuki — Loop V9 (effectiveness report recommendations)")
    parser.add_argument("--iterations", "-n", type=int, help="Number of iterations (default: 1)")
    parser.add_argument("--minutes", "-m", type=float, help="Run for N minutes")
    parser.add_argument("--min-interval", type=float, default=60, help="Seconds between iterations (default: 60)")
    parser.add_argument("--dry-run", action="store_true", help="Skip writes and LLM")
    parser.add_argument("--improve-cmd", type=str, help="Command when evaluation fails")
    parser.add_argument("--skip-fetch", action="store_true", help="Skip fetching fresh data")
    parser.add_argument("--version", "-v", type=int, default=9, help="Version for archive path (default: 9)")
    parser.add_argument("--standalone", action="store_true", help="One improve pass")
    parser.add_argument("--checkpoint-after", type=int, help="Pause for human review after N iterations")
    parser.add_argument("--checkpoint-on-pass", action="store_true", help="Pause for human review on first pass")
    parser.add_argument("--meta-analysis-interval", type=int, default=5, help="Run meta-analysis every N iters (0=off, default: 5)")
    args = parser.parse_args()

    archive_path, report_path = _get_paths(args.version)

    if args.standalone:
        return run_standalone(archive_path, report_path, args.skip_fetch, args.dry_run)
    run_loop(
        args.iterations, args.minutes, args.min_interval,
        args.dry_run, args.improve_cmd, args.skip_fetch, args.version,
        checkpoint_after=args.checkpoint_after,
        checkpoint_on_pass=args.checkpoint_on_pass,
        meta_analysis_interval=args.meta_analysis_interval,
    )
    return 0


if __name__ == "__main__":
    sys.exit(main())

Report generated from examination of india_karpathy codebase, docs, reports, and effectiveness analysis.