The Digital Khalifah: Stewardship at Scale — 10 Organizational Sprints, Playbook Style

This commit is contained in:
Hermes Bot
2026-08-06 18:47:47 +08:00
commit d309fa0b65
36 changed files with 6853 additions and 0 deletions
+368
View File
@@ -0,0 +1,368 @@
#!/usr/bin/env python3
"""Assemble 'The Digital Khalifah' PDF — Playbook Style, Penguin 6×9in."""
import os, re, sys
from reportlab.lib.pagesizes import letter
from reportlab.lib.units import inch
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.lib.colors import HexColor
from reportlab.platypus import (
Paragraph, Spacer, PageBreak, Frame, PageTemplate, BaseDocTemplate
)
OUTPUT_DIR = os.path.expanduser("~/odysseus_book/digital_khalifah")
CHAPTERS_DIR = os.path.join(OUTPUT_DIR, "chapters")
PDF_PATH = os.path.join(OUTPUT_DIR, "pdf", "The_Digital_Khalifah.pdf")
MARGIN = 0.85 * inch
FOOTER_H = 0.4 * inch
PAGE_W = 6 * inch
PAGE_H = 9 * inch
DARK_TEAL = HexColor('#0D4F4F')
GOLD = HexColor('#C8A951')
DARK = HexColor('#1a1a1a')
GREY = HexColor('#666666')
LIGHT_GREY = HexColor('#f0f0f0')
ACCENT_ORANGE = HexColor('#E67E22')
style_title = ParagraphStyle('Title', fontName='Helvetica-Bold', fontSize=26,
leading=32, alignment=TA_CENTER, textColor=DARK_TEAL, spaceAfter=8)
style_subtitle = ParagraphStyle('Subtitle', fontName='Helvetica-Oblique', fontSize=13,
leading=17, alignment=TA_CENTER, textColor=GOLD, spaceAfter=20)
style_author = ParagraphStyle('Author', fontName='Helvetica', fontSize=12,
leading=16, alignment=TA_CENTER, textColor=GREY, spaceAfter=28)
style_copyright = ParagraphStyle('Copyright', fontName='Helvetica', fontSize=9,
leading=12, alignment=TA_CENTER, textColor=GREY)
style_ch_num = ParagraphStyle('ChNum', fontName='Helvetica-Bold', fontSize=10,
leading=13, alignment=TA_CENTER, textColor=GOLD, spaceAfter=4, spaceBefore=18)
style_ch_title = ParagraphStyle('ChTitle', fontName='Helvetica-Bold', fontSize=20,
leading=26, alignment=TA_LEFT, textColor=DARK_TEAL, spaceAfter=8, spaceBefore=4)
style_subtitle_ch = ParagraphStyle('SubtitleCh', fontName='Helvetica-Oblique', fontSize=12,
leading=16, alignment=TA_LEFT, textColor=ACCENT_ORANGE, spaceAfter=14, spaceBefore=2)
style_framework = ParagraphStyle('Framework', fontName='Helvetica-Bold', fontSize=11,
leading=15, alignment=TA_LEFT, textColor=DARK_TEAL, spaceAfter=6, spaceBefore=14,
backColor=HexColor('#f0f0f0'), borderColor=GOLD, borderWidth=1, borderPadding=8)
style_body = ParagraphStyle('Body', fontName='Times-Roman', fontSize=10,
leading=14.5, alignment=TA_JUSTIFY, textColor=DARK, spaceAfter=7, firstLineIndent=0)
style_dialogue = ParagraphStyle('Dialogue', fontName='Times-Roman', fontSize=10,
leading=14.5, alignment=TA_LEFT, textColor=DARK, spaceAfter=5, leftIndent=24)
style_pl = ParagraphStyle('PL', fontName='Times-Bold', fontSize=10,
leading=14.5, alignment=TA_LEFT, textColor=DARK_TEAL, spaceAfter=3, leftIndent=24)
style_muj = ParagraphStyle('Muj', fontName='Times-Bold', fontSize=10,
leading=14.5, alignment=TA_LEFT, textColor=GOLD, spaceAfter=3, leftIndent=24)
style_lie = ParagraphStyle('Lie', fontName='Times-Bold', fontSize=10.5,
leading=15, alignment=TA_LEFT, textColor=HexColor('#C0392B'), spaceAfter=6, spaceBefore=10,
backColor=HexColor('#FDF2F2'), borderColor=HexColor('#C0392B'), borderWidth=1, borderPadding=10)
style_protocol = ParagraphStyle('Protocol', fontName='Times-Roman', fontSize=10,
leading=14.5, alignment=TA_LEFT, textColor=DARK, spaceAfter=6, leftIndent=36)
style_reflection = ParagraphStyle('Reflection', fontName='Times-Italic', fontSize=10.5,
leading=15, alignment=TA_CENTER, textColor=GREY, spaceAfter=16, spaceBefore=16,
backColor=HexColor('#F8F9FA'), borderColor=GREY, borderWidth=0.5, borderPadding=12)
style_principle = ParagraphStyle('Principle', fontName='Times-Bold', fontSize=11,
leading=15, alignment=TA_LEFT, textColor=DARK_TEAL, spaceAfter=6, spaceBefore=10,
backColor=HexColor('#EBF5FB'), borderColor=DARK_TEAL, borderWidth=1, borderPadding=10)
style_toc_ch = ParagraphStyle('TOCCh', fontName='Helvetica-Bold', fontSize=11,
leading=17, alignment=TA_LEFT, textColor=DARK_TEAL)
style_toc_sub = ParagraphStyle('TOCSub', fontName='Helvetica', fontSize=9.5,
leading=13, alignment=TA_LEFT, textColor=GREY)
style_hook = ParagraphStyle('Hook', fontName='Times-Italic', fontSize=10.5,
leading=15, alignment=TA_LEFT, textColor=DARK, spaceAfter=10, spaceBefore=6,
backColor=HexColor('#EBF5FB'), borderColor=DARK_TEAL, borderWidth=1, borderPadding=10)
CHAPTERS = [
("Principle #1: The Problem Statement", "What Problem Are We Solving?", "Problem Quadrant: Symptom vs Root Cause / User Pain vs Business Pain / Known vs Unknown"),
("Principle #2: Evidence Gathering", "Continuous Discovery as Istiqsa'", "Evidence Quadrant: Quantitative / Qualitative / Behavioral / Attitudinal"),
("Principle #3: Stakeholder Consultation", "Shura as Discovery", "Shura Quadrant: Users / Team / Business / Regulators"),
("Principle #4: The Product Decision", "The Principle as Definition of Done", "Decision Quadrant: Reversible vs Irreversible / High Stakes vs Low Stakes / Data-Rich vs Data-Poor"),
("Principle #5: Minimum Viable Principle", "MVP as Minimum Viable Principle", "MVP Quadrant: Viable / Valuable / Usable / Feasible"),
("Principle #6: Technical Debt", "Israf vs Investment", "Debt Quadrant: Prudent / Reckless / Deliberate / Inadvertent"),
("Principle #7: Pricing", "Adl (Justice) in Pricing", "Pricing Quadrant: Cost-Plus / Value-Based / Dynamic / Freemium"),
("Principle #8: Growth Metrics", "Barakah vs Vanity Metrics", "Growth Quadrant: AARRR → Barakah Filter"),
("Principle #9: Team Organization", "Shura at Scale", "Org Quadrant: Functional / Cross-Functional / Squad / Guild / Chapter"),
("Principle #10: Legacy Product", "Sadaqah Jariyah as Compound Impact", "Legacy Loop: Create → Ship → Measure → Compound → Endow → Teach"),
]
def clean_text(text):
import re
import markdown
# Convert markdown to HTML using the markdown library
# This properly handles nested emphasis, code blocks, etc.
md = markdown.Markdown(
extensions=[
'markdown.extensions.extra',
'markdown.extensions.codehilite',
'markdown.extensions.smarty',
],
output_format='html'
)
html = md.convert(text)
# Strip any remaining unwanted tags (script, style, etc.)
import re
text = re.sub(r'<script[^>]*>.*?</script>', '', html, flags=re.DOTALL | re.IGNORECASE)
text = re.sub(r'<style[^>]*>.*?</style>', '', text, flags=re.DOTALL | re.IGNORECASE)
# ReportLab only supports a subset of HTML tags.
# We'll keep the basic formatting tags it supports.
allowed_tags = ['b', 'i', 'u', 'strong', 'em', 'br', 'p', 'br/', 'sub', 'sup']
# Remove any tags not in allowed list
def replace_tag(match):
tag = match.group(1).lower()
if tag.startswith('/'):
tag_name = tag[1:]
else:
tag_name = tag
if tag_name in allowed_tags:
return match.group(0)
return ''
text = re.sub(r'<(/?)([a-zA-Z][a-zA-Z0-9]*)[^>]*>', lambda m: m.group(0) if m.group(2).lower() in allowed_tags else '', text)
# Clean up any remaining malformed tags
text = re.sub(r'<[^>]*>', '', text)
return text
def parse_chapter(md_text):
lines = md_text.split('\n')
sections = {'scenario': [], 'discovery': [], 'evidence': [], 'shura': [], 'fatwa': [], 'protocol': [], 'muhasaba': []}
current = None
for line in md_text.split('\n'):
stripped = line.strip()
if not stripped:
continue
low = stripped.lower()
if low.startswith('# the scenario') or low.startswith('## the scenario') or low == 'the scenario':
current = 'scenario'
continue
elif low.startswith('# discovery') or low.startswith('## discovery') or low.startswith('discovery') or low.startswith('istiqsa'):
current = 'discovery'
continue
elif low.startswith('# evidence') or low.startswith('## evidence') or low.startswith('evidence') or low.startswith('istidlal'):
current = 'evidence'
continue
elif low.startswith('# shura') or low.startswith('## shura') or low.startswith('shura'):
current = 'shura'
continue
elif low.startswith('# the fatwa') or low.startswith('## the fatwa') or low.startswith('fatwa') or low.startswith('hukm'):
current = 'fatwa'
continue
elif low.startswith('# the protocol') or low.startswith('## the protocol') or low.startswith('protocol'):
current = 'protocol'
continue
elif low.startswith('# muhasaba') or low.startswith('## muhasaba') or low.startswith('muhasaba') or low.startswith('retrospective'):
current = 'muhasaba'
continue
if stripped.startswith('#'):
continue
if stripped == '---' or stripped == '***':
continue
if current:
sections[current].append(stripped)
for k in sections:
sections[k] = '\n\n'.join(sections[k])
return sections
class BookDoc(BaseDocTemplate):
def __init__(self, *args, **kw):
BaseDocTemplate.__init__(self, *args, **kw)
self.page_count = 0
def footer_fn(canvas, doc):
canvas.saveState()
canvas.setFont('Helvetica', 8)
canvas.setFillColor(HexColor('#999999'))
canvas.drawCentredString(PAGE_W / 2, 0.35 * inch, f'{doc.page}')
canvas.restoreState()
def build_pdf():
os.makedirs(os.path.join(OUTPUT_DIR, 'pdf'), exist_ok=True)
frame = Frame(MARGIN, MARGIN + FOOTER_H,
PAGE_W - 2 * MARGIN,
PAGE_H - 2 * MARGIN - FOOTER_H, id='normal')
doc = BookDoc(PDF_PATH, pagesize=(PAGE_W, PAGE_H),
leftMargin=MARGIN, rightMargin=MARGIN,
topMargin=MARGIN, bottomMargin=MARGIN,
title="The Digital Khalifah: Stewardship at Scale",
author='Jauhari Che Wan')
doc.addPageTemplates([PageTemplate(id='main', frames=frame, onPage=footer_fn)])
story = []
# Title page
story.append(Spacer(1, PAGE_H * 0.18))
story.append(Paragraph("The Digital Khalifah", style_title))
story.append(Paragraph("Stewardship at Scale", style_subtitle))
story.append(Paragraph("10 Principles for Building Products That Matter", ParagraphStyle('Tagline',
fontName='Helvetica-Oblique', fontSize=12, leading=15, alignment=TA_CENTER, textColor=HexColor('#666666'), spaceAfter=24)))
story.append(Spacer(1, 14))
story.append(Paragraph('An Org Designer & Khalifah Playbook', ParagraphStyle('StyleLine',
fontName='Helvetica', fontSize=10, leading=13, alignment=TA_CENTER, textColor=HexColor('#888888'), spaceAfter=20)))
story.append(Spacer(1, 14))
story.append(Paragraph('Jauhari Che Wan', style_author))
story.append(Spacer(1, PAGE_H * 0.1))
story.append(Paragraph('PRODUCT LEAD × MUJTAHID × MAQASID',
ParagraphStyle('Tag', parent=style_copyright, fontName='Helvetica-Bold', fontSize=9, textColor=GOLD)))
story.append(PageBreak())
# Copyright
story.append(Spacer(1, PAGE_H * 0.28))
for line in ['PRODUCT LEAD × MUJTAHID PLAYBOOK', '', 'The Digital Khalifah', 'Jauhari Che Wan',
'', 'Based on Classical Usul al-Fiqh', 'Marty Cagan\'s Product Framework', 'Maqasid al-Shariah',
'', 'Copyright \u00a9 2025', 'All rights reserved',
'', 'Typeset in Times / Helvetica', 'Printed in Malaysia']:
if line == '':
story.append(Spacer(1, 4))
else:
story.append(Paragraph(line, style_copyright))
story.append(PageBreak())
# Dedication
story.append(Spacer(1, PAGE_H * 0.28))
story.append(Paragraph('For every Product Lead who\'s ever asked: "What would the Mujtahid do?"', style_dedication := ParagraphStyle('Ded', fontName='Times-Italic', fontSize=11, leading=16, alignment=TA_CENTER, textColor=DARK, spaceAfter=24)))
story.append(PageBreak())
# TOC
story.append(Paragraph('Contents', ParagraphStyle('TOCHeader',
fontName='Helvetica-Bold', fontSize=18, leading=24, alignment=TA_LEFT, textColor=DARK_TEAL, spaceAfter=20)))
for i, (title, subtitle, _) in enumerate(CHAPTERS, 1):
story.append(Paragraph(f'<b>Principle #{i}</b>: {title}', style_toc_ch))
story.append(Paragraph(f' <i>{subtitle}</i>', style_toc_sub))
story.append(Spacer(1, 4))
story.append(PageBreak())
# Chapters
for ch_num, (title, subtitle, framework) in enumerate(CHAPTERS, 1):
fname = os.path.join(CHAPTERS_DIR, f"Principle_0{ch_num}.md") if ch_num < 10 else os.path.join(CHAPTERS_DIR, f"Principle_{ch_num}.md")
if not os.path.exists(fname):
print(f" WARNING: {fname} not found")
continue
with open(fname, 'r', encoding='utf-8') as f:
md = f.read()
print(f" Adding Principle #{ch_num}: {title}...")
sections = parse_chapter(md)
# Chapter header
story.append(Paragraph(f'Principle #{ch_num}', style_ch_num))
story.append(Paragraph(title, style_ch_title))
story.append(Paragraph(subtitle, style_subtitle_ch))
story.append(Spacer(1, 8))
# SCENARIO
if sections['scenario']:
hook_text = clean_text(' '.join(sections['scenario'].split('\n')))
story.append(Paragraph(hook_text, style_hook))
story.append(Spacer(1, 6))
# DISCOVERY (ISTIQSA')
if sections['discovery']:
story.append(Paragraph('DISCOVERY (ISTIQSA\')', ParagraphStyle('DiscHeader',
fontName='Helvetica-Bold', fontSize=12, leading=16, alignment=TA_LEFT, textColor=DARK_TEAL, spaceAfter=6, spaceBefore=10)))
for para in sections['discovery'].split('\n\n'):
clean = clean_text(para.strip())
if not clean:
continue
low = clean.lower()
if low.startswith('product_lead:') or low.startswith('product lead:'):
story.append(Paragraph(f'<b>PRODUCT_LEAD:</b> {clean[13:].strip() if clean.lower().startswith("product_lead:") else clean[12:].strip()}', style_pl))
elif low.startswith('mujtahid:'):
story.append(Paragraph(f'<b>MUJTAHID:</b> {clean[9:].strip()}', style_muj))
else:
story.append(Paragraph(clean, style_body))
story.append(Spacer(1, 6))
# EVIDENCE (ISTIDLAL)
if sections['evidence']:
story.append(Paragraph('EVIDENCE (ISTIDLAL)', ParagraphStyle('EvidHeader',
fontName='Helvetica-Bold', fontSize=12, leading=16, alignment=TA_LEFT, textColor=DARK_TEAL, spaceAfter=6, spaceBefore=10)))
for para in sections['evidence'].split('\n\n'):
clean = clean_text(para.strip())
if not clean:
continue
low = clean.lower()
if low.startswith('product_lead:') or low.startswith('product lead:'):
story.append(Paragraph(f'<b>PRODUCT_LEAD:</b> {clean[13:].strip() if clean.lower().startswith("product_lead:") else clean[12:].strip()}', style_pl))
elif low.startswith('mujtahid:'):
story.append(Paragraph(f'<b>MUJTAHID:</b> {clean[9:].strip()}', style_muj))
else:
story.append(Paragraph(clean, style_body))
story.append(Spacer(1, 6))
# SHURA
if sections['shura']:
story.append(Paragraph('SHURA (CONSULTATION)', ParagraphStyle('ShuraHeader',
fontName='Helvetica-Bold', fontSize=12, leading=16, alignment=TA_LEFT, textColor=DARK_TEAL, spaceAfter=6, spaceBefore=10)))
for para in sections['shura'].split('\n\n'):
clean = clean_text(para.strip())
if not clean:
continue
low = clean.lower()
if low.startswith('product_lead:') or low.startswith('product lead:'):
story.append(Paragraph(f'<b>PRODUCT_LEAD:</b> {clean[13:].strip() if clean.lower().startswith("product_lead:") else clean[12:].strip()}', style_pl))
elif low.startswith('mujtahid:'):
story.append(Paragraph(f'<b>MUJTAHID:</b> {clean[9:].strip()}', style_muj))
else:
story.append(Paragraph(clean, style_body))
story.append(Spacer(1, 6))
# FATWA (HUKM)
if sections['fatwa']:
story.append(Paragraph('THE FATWA (HUKM)', style_fatwa))
for para in sections['fatwa'].split('\n\n'):
clean = clean_text(para.strip())
if not clean:
continue
low = clean.lower()
if low.startswith('hukm:'):
story.append(Paragraph(f'<b>HUKM:</b> {clean[5:].strip()}', style_body))
elif low.startswith('daleel:'):
story.append(Paragraph(f'<b>DALEEL:</b> {clean[7:].strip()}', style_body))
elif low.startswith('maqsad:'):
story.append(Paragraph(f'<b>MAQSAD:</b> {clean[7:].strip()}', style_body))
elif low.startswith('shurut:') or low.startswith('shurut:'):
story.append(Paragraph(f'<b>SHURUT:</b> {clean[7:].strip()}', style_body))
elif low.startswith('munkathirat:'):
story.append(Paragraph(f'<b>MUNKATHIRAT:</b> {clean[12:].strip()}', style_body))
else:
story.append(Paragraph(clean, style_body))
story.append(Spacer(1, 8))
# PROTOCOL
if sections['protocol']:
story.append(Paragraph('THE PROTOCOL — Do This This Sprint', ParagraphStyle('ProtoHeader',
fontName='Helvetica-Bold', fontSize=12, leading=16, alignment=TA_LEFT, textColor=DARK_TEAL, spaceAfter=6, spaceBefore=10)))
for i, para in enumerate(sections['protocol'].split('\n\n'), 1):
clean = clean_text(para.strip())
if clean:
if clean.lower().startswith(f'step {i}') or clean.lower().startswith(f'step {i}:'):
clean = clean.split(':', 1)[-1].strip()
story.append(Paragraph(f'<b>STEP {i}:</b> {clean}', style_protocol))
story.append(Spacer(1, 8))
# MUHASABA
if sections['muhasaba']:
clean = clean_text(' '.join(sections['muhasaba'].split('\n')))
story.append(Paragraph(clean, style_reflection))
story.append(PageBreak())
print('Building PDF...')
doc.build(story)
print(f'Done: {PDF_PATH}')
import subprocess
result = subprocess.run(['wc', '-c', PDF_PATH], capture_output=True, text=True)
size_kb = int(result.stdout.split()[0]) / 1024
with open(PDF_PATH, 'rb') as f:
data = f.read()
pages = len(re.findall(rb'/Type\s*/Page[^s]', data))
print(f'Pages: {pages}, Size: {size_kb:.0f} KB')
if __name__ == '__main__':
build_pdf()