Files
digital-mujtahid/generate.py
T

170 lines
9.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Generate "The Digital Mujtahid: Product Thinking from First Principles"
Playbook Style — Fatwa-Driven Product Development
Dual Mentors: PRODUCT_LEAD (Marty Cagan style) + MUJTAHID (Classical Usul al-Fiqh)
10 Fatwas. Each = Discovery → Evidence → Shura → Fatwa → Ship → Measure
Two-part generation per chapter to bypass token limits.
"""
import json, os, sys, time, urllib.request, urllib.error
OUTPUT_DIR = os.path.expanduser("~/odysseus_book/digital_mujtahid/chapters")
API_URL = "https://opencode.ai/zen/v1/chat/completions"
API_KEY = "sk-jl41MVxfeWRY4ElO7cWXw8ZM6KN5r0S1DG3vK2i1SaUsVdj13ilSxyRdIQLz1Csy"
# Load manifest
with open(os.path.join(os.path.dirname(__file__), "manifest.json")) as f:
MANIFEST = json.load(f)
CHAPTER_SPECS = MANIFEST["chapters"]
SYSTEM_PROMPT = f"""You are a master writer creating a PRODUCT PLAYBOOK in the style of Marty Cagan's "Inspired" and "Empowered" fused with CLASSICAL USUL AL-FIQH methodology.
DUAL MENTORS:
1. **PRODUCT_LEAD** — Senior Product Lead (Marty Cagan / Teresa Torres style). Voice: "Discovery over delivery. Outcomes over output. Talk to users weekly. Ship to learn." Frameworks: Continuous Discovery, Jobs-to-be-Done, OKRs, North Star Metric, Opportunity Solution Trees.
2. **MUJTAHID** — Classical Mujtahid (Usul al-Fiqh master). Voice: "What is the hukm? What is the daleel? What is the maqsad? What are the shurut?" Frameworks: Usul al-Fiqh (Qiyas, Istihsan, Maslaha, Sad al-Dhara'i, Istishab), Maqasid al-Shariah (Hifz al-Din, Nafs, Aql, Mal, Nasl), Qawaid Fiqhiyyah.
PLAYBOOK FORMAT — EACH CHAPTER = 1 FATWA (PRODUCT DECISION):
Each chapter follows the FATWA METHODOLOGY:
1. **DISCOVERY** (Istiqsa' / Continuous Discovery) — The problem space exploration
2. **EVIDENCE** (Istidlal / Continuous Discovery) — Gathering daleel (evidence)
3. **SHURA** (Consultation / Stakeholder Voice) — Gathering input
3. **FATWA** (Hukm / Decision) — The product decision (Definition of Done)
4. **SHIP** (Tanfidh / Execution) — MVP, Rollout, Rollback
5. **MEASURE** (Muhasaba / Analytics) — Retrospective, Metrics, Iterate
WRITING STYLE — PLAYBOOK FORMAT (Marty Cagan × Classical Usul al-Fiqh):
1. **THE SCENARIO** (100 words): Real product situation. Concrete. Visceral. "You're the PM at..."
2. **DISCOVERY (ISTIQSA')** (300 words): PRODUCT_LEAD runs Continuous Discovery. MUJTAHID runs Istiqsa'. Both ask: "What is the real problem?" Frameworks: Opportunity Solution Tree, Maqasid analysis.
3. **EVIDENCE (ISTIDLAL)** (300 words): Gathering daleel. PRODUCT_LEAD: Quantitative + Qualitative. MUJTAHID: Daleel types (Qati, Zanni, Qati al-Thubut, etc.). Frameworks: Opportunity Solution Tree, Istidlal hierarchy.
4. **SHURA** (200 words): Consultation. PRODUCT_LEAD: Stakeholder interviews, user interviews. MUJTAHID: Shura methodology, weighting input. Frameworks: Shura methodology, Stakeholder mapping.
5. **THE FATWA (HUKM)** (300 words): The Decision. Format: Hukm (Ruling), Daleel (Evidence summary), Maqsad (Purpose/Outcome), Shurut (Conditions/Constraints), Munkathirat (Nullifiers/Rollback triggers). This IS the Definition of Done.
6. **THE PROTOCOL** (200 words): Exactly 3 steps. Actionable THIS SPRINT. "STEP 1: ... STEP 2: ... STEP 3: ..." Marty Cagan's "Do this Monday."
7. **RETROSPECTIVE (MUHASABA)** (150 words): One piercing question. "What did we learn? What would we change?"
VOICE RULES:
- Playbook tone: Practical. Actionable. No fluff. "Do this Monday."
- Dual mentor labels: "PRODUCT_LEAD:" / "MUJTAHID:" — every exchange labeled.
- Frameworks described visually: "Draw four boxes. Label them..."
- Fatwa format: Hukm / Daleel / Maqsad / Shurut / Munkathirat — use these exact terms.
- Maqasid references: Hifz al-Din, Hifz al-Nafs, Hifz al-Aql, Hifz al-Mal, Hifz al-Nasl.
- Usul terms: Hukm, Daleel, Maqsad, Shart, Munkathir, Qiyas, Istihsan, Maslaha, Sad al-Dhara'i, Istishab.
- Product terms: PRD, MVP, OKR, North Star, Opportunity Solution Tree, Continuous Discovery, JTBD, Definition of Done, Rollback, Rollout.
- NO academic tone. NO flowery prose. Punchy. Practical. Provocative.
- Audience: Muslim product managers, founders, tech leads building products with Islamic first principles.
TARGET: ~2,000 words per chapter (Scenario 100 + Discovery 300 + Evidence 300 + Shura 200 + Fatwa 300 + Protocol 200 + Muhasaba 150 = ~2,000)
"""
# Build prompts from manifest
CHAPTER_SPECS = MANIFEST["chapters"]
CHAPTER_PROMPTS_PART1 = {}
CHAPTER_PROMPTS_PART2 = {}
for spec in MANIFEST["chapters"]:
cid = spec["id"]
CHAPTER_PROMPTS_PART1[cid] = f"""Write the FIRST HALF of Fatwa #{cid}: "{spec['title']}" — {spec['subtitle']}
FRAMEWORK: {spec['framework']}
MAQSAD: {spec['subtitle'].split('→')[1].strip() if '→' in spec['subtitle'] else 'Preservation of Purpose'}
PART 1 STRUCTURE (target ~1,200 words):
1. THE SCENARIO (~100 words):
"You're the Product Lead at a growing fintech startup. Your CEO asks: 'Why are we building this feature?' You open your notebook. The Mujtahid in you asks: 'What is the hukm? What is the maqsad?'"
2. DISCOVERY (ISTIQSA') (~300 words):
PRODUCT_LEAD runs Continuous Discovery. MUJTAHID runs Istiqsa'.
PRODUCT_LEAD: [Continuous Discovery framing — Opportunity Solution Tree, JTBD]
MUJTAHID: [Istiqsa' framing — Maqasid analysis, defining the problem space]
Use this discovery prompt as inspiration: {spec['discovery_prompt']}
3. EVIDENCE (ISTIDLAL) (~300 words):
Gathering daleel. PRODUCT_LEAD: Quantitative + Qualitative (metrics, interviews, analytics). MUJTAHID: Daleel hierarchy (Qati/Zanni, Qati al-Thubut/Qati al-Dalala).
Use this evidence prompt as inspiration: {spec['evidence_prompt']}
4. SHURA (~200 words):
Consultation. PRODUCT_LEAD: Stakeholder interviews, user interviews, cross-functional sync. MUJTAHID: Shura methodology, weighting input, recording dissent.
Use this shura prompt as inspiration: {spec.get('shura_prompt', 'How do you consult stakeholders? How does a Mujtahid consult?')}
OUTPUT ONLY PART 1. No Fatwa. No Protocol. No Muhasaba. End after Shura section."""
CHAPTER_PROMPTS_PART2[cid] = f"""Write the SECOND HALF of Fatwa #{spec['id']}: "{spec['title']}" — {spec['subtitle']}
FRAMEWORK: {spec['framework']}
MAQSAD: {spec['subtitle'].split('→')[1].strip() if '→' in spec['subtitle'] else 'Preservation of Purpose'}
PART 2 STRUCTURE (target ~1,000 words):
5. THE FATWA (HUKM) (~300 words):
The Decision. Format exactly:
**HUKM:** [One-sentence ruling: "We will build X / We will not build Y / We will ship Z with conditions"]
**DALEEL:** [Evidence summary — 2-3 sentences summarizing key evidence]
**MAQSAD:** [Purpose/Outcome — which Maqasid is served: Hifz al-Din/Nafs/Aql/Mal/Nasl]
**SHURUT:** [Conditions/Constraints — 3-4 bullet points: success metrics, rollback triggers, constraints]
**MUNKATHIRAT:** [Nullifiers/Rollback triggers — 2-3 bullet points: what invalidates this fatwa]
4. THE PROTOCOL (~200 words):
Exactly 3 steps. Numbered. Actionable THIS SPRINT.
Format:
STEP 1: [Specific action with timeframe]
STEP 2: [Specific action with timeframe]
STEP 3: [Specific action with timeframe]
5. MUHASABA (RETROSPECTIVE) (~150 words):
One piercing question. Start with "What..." or "Where..." or "When..."
Make it uncomfortable. Make it actionable.
OUTPUT ONLY PART 2. No Scenario. No Discovery. No Evidence. No Shura. Start with '## THE FATWA (HUKM)'."""
def call_api(prompt, max_tokens=2800):
data = {
"model": "deepseek-v4-flash",
"messages": [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": prompt}
],
"temperature": 0.85,
"max_tokens": max_tokens
}
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
"User-Agent": "curl/7.88.1"
}
req = urllib.request.Request(API_URL, data=json.dumps(data).encode(), headers=headers)
with urllib.request.urlopen(req, timeout=90) as resp:
return json.loads(resp.read())["choices"][0]["message"]["content"]
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Generate Digital Mujtahid Fatwas (two-part)")
parser.add_argument("chapter", nargs="?", type=int, help="Chapter number (1-10)")
parser.add_argument("--part", type=int, choices=[1, 2], help="Part 1 or 2")
args = parser.parse_args()
if not os.path.exists(OUTPUT_DIR):
os.makedirs(OUTPUT_DIR)
chapters = [args.chapter] if args.chapter else list(range(1, 11))
parts = [args.part] if args.part else [1, 2]
for chap_num in chapters:
for part_num in parts:
print(f"\n🔄 Fatwa #{chap_num} Part {part_num}...")
prompt = CHAPTER_PROMPTS_PART1[chap_num] if part_num == 1 else CHAPTER_PROMPTS_PART2[chap_num]
try:
content = call_api(prompt, max_tokens=3000)
fname = os.path.join(OUTPUT_DIR, f"Fatwa_{chap_num:02d}_Part{part_num}.md")
with open(fname, "w", encoding="utf-8") as f:
f.write(content)
words = len(content.split())
print(f"✅ Fatwa #{chap_num} Part {part_num} saved — {words} words")
except Exception as e:
print(f"❌ Error: {e}")
time.sleep(3)
print("\n✅ Generation complete!")