Occamβs Opera CMS: The Complete Journey
From βEating Tablesβ to Face-Changing Opera
A Technical Chronicle
Table of Contents
- Week 1: The Parser That Ate Tables
- Week 2: The PDCA Fractal
- Week 3: The Static Site Generator Matures
- Week 4: CHECK Stage β Quality Gates
- Week 5: ACT Stage β Email, Minify, Reports
- Week 6: Template Transformation β The Dress
- Week 7: The Dynamic Dress Revolution
- Lessons Learned
- Architecture Reference
Week 1: The Parser That Ate Tables
How It Started
It began with a simple request: βConvert markdown to JSON.β The first parser worked perfectly on simple files. Then came the tables.
| Header 1 | Header 2 |
|:---|:---:|
| Cell 1 | Cell 2 |
The parser saw |:---| and⦠froze. Entered an infinite loop. Started consuming markdown tables as if they were dining tables.
βThe paragraph parser was eating tables.β
βFor a moment I thought it was ει€ζ‘ (eating dining tables)!β
Key Insight
The parser wasnβt the problem β the architecture was. A single script trying to do everything was doomed to fail.
What We Built
# The fix: proper table detection
if '|' in line and re.search(r'\|[\s]*:?-{3,}:?[\s]*\|', next_line):
i = parse_table(lines, i)
| Lesson | Why It Matters |
|---|---|
| Single responsibility | One script, one job |
| Graceful failure | Donβt freeze, fail with a message |
| Test with real data | Tables, lists, blockquotes β all of it |
Week 2: The PDCA Fractal
The Discovery
While building the PLAN stage, a pattern emerged:
PLAN stage (mini-PDCA)
βββ workfolder.py (Prepare)
βββ enrich.py (Do)
βββ preflight.py (Check)
β
βββ (Act β DO stage)
βYouβve just uncovered a beautiful fractal pattern!β
The Pattern
Every stage follows the same rhythm:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MAIN PIPELINE β
β βββββββββββ βββββββββββ βββββββββββ βββββββββββ β
β β PLAN β β β DO β β β CHECK β β β ACT β β
β βββββββββββ βββββββββββ βββββββββββ βββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β MINI PDCA (within each stage) β β
β β ββββββββββββ ββββββββββββ ββββββββββββ β β
β β β PREPARE β β β DO β β β CHECK β β β
β β ββββββββββββ ββββββββββββ ββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Detailed Diagram
The Token System
Each step creates a .ok file when complete:
work/{slug}/
βββ workfolder.ok
βββ enrich.ok
βββ preflight.ok
βββ parser.ok
βββ enrich_blueprint.ok
βββ ...
These tokens make the pipeline idempotent β run it 100 times, same result.
βShe is kind of grown up now.β
Week 3: The Static Site Generator Matures
PLAN β DO β CHECK β HOMERUN
| Stage | Purpose | Scripts |
|---|---|---|
| PLAN | Prepare source | workfolder.py, enrich.py, preflight.py |
| DO | Create blueprint | parser.py, enrich_blueprint.py, renderer.py, og_image.py |
| CHECK | Validate quality | check_artifacts.py, validate_content.py, enforcer.py, quality_report.py |
| HOMERUN | Deploy | prepare_deploy.py, deploy.py, finalize.py |
The Data Flow
inbox/article.md
β
βΌ
PLAN β work/{slug}/{slug}.md (enriched frontmatter)
β
βΌ
DO β work/{slug}/{slug}.json (blueprint)
work/{slug}/{slug}.html (rendered)
work/{slug}/og.jpg (social image)
β
βΌ
CHECK β work/{slug}/enforcer_report.json
work/{slug}/quality_report.json
β
βΌ
HOMERUN β web/{slug}/index.html
web/{slug}/og.jpg
The First βSheβs Grown Upβ Moment
βIt is working. She is kind of grown up nowβ¦ It is turning out to be very nice.β
Week 4: CHECK Stage β Quality Gates
The Enforcer
Quality rules enforced without blocking the pipeline:
| Rule | What It Checks |
|---|---|
| Slug format | lowercase, hyphens only |
| Title length | 30-70 characters |
| Date format | YYYY-MM-DD |
| OG image | og.jpg (simple filename) |
| Category compatibility | No HDB + Executive Condo together |
The SEO Analyzer
seo_content.py
βββ meta_description_length (120-160 chars)
βββ title_tag_length (50-60 chars)
βββ h1_count (exactly one)
βββ image_alt_attributes (all images)
βββ internal_link_count (3-15)
βββ keyword_in_description
βββ heading_hierarchy
βββ keyword_in_first_100
βββ keyword_density (0.5-3.0%)
βThe CHECK stage is good enough. We just boosted it again.β
Week 5: ACT Stage β Email, Minify, Reports
Email Notifications
[notifications.email]
smtp_server = "smtp.mail.com"
username = "alerts@***expert.sg"
to_emails = ["admin@***expert.sg"]
HTML Minification
# Backup before minify
shutil.copy2(html_path, backup_path) # {slug}.htm
minified = minify(original_content) # {slug}.html
The HTML Report
Each article gets a beautiful HTML report showing every stageβs .ok file content in chronological order.
βWell done, she is getting prettier by the day.β
Week 6: Template Transformation β The Dress
The Problem
Changing the header required re-publishing every article:
Change navbar β Re-run pipeline for 500 articles β 2 hours
The Insight
βThe UI is just a changeable dress for the static content.β
The Solution: Two Layers
| Layer | What | Changes? |
|---|---|---|
| Static Core | Article content, metadata, related links | Requires republish |
| Dynamic Dress | Header, footer, sidebars | Instant |
Pre-rendered + Refresh Pattern
<!-- Pre-rendered navbar (visible immediately) -->
<div id="header-placeholder">
<nav>...</nav>
</div>
<script>
// Silently refresh from snippet
fetch('/snippets/header.html')
.then(r => r.text())
.then(html => {
if (container.innerHTML !== html) {
container.innerHTML = html;
}
});
</script>
Week 7: The Dynamic Dress Revolution
The εθΈ (Face-Changing) Opera
| Opera Element | CMS Equivalent |
|---|---|
| The singer | Article content (permanent) |
| The mask | Header/footer/sidebars (changeable) |
| The costume change | Snippet refresh (instant) |
The Architecture
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β STATIC CORE (SEO) β
β β
Article content (pre-rendered) β
β β
Metadata, tags, related articles β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DYNAMIC DRESS (UI) β
β β
Header (snippet) β change instantly β
β β
Footer (snippet) β change instantly β
β β
Sidebars (config) β change instantly β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The Test
# Change Guides dropdown to simple button
edit web/snippets/header.html
# Refresh browser β changes appear instantly
# No pipeline re-run. No republishing. No waiting.
βContent is permanent. The dress changes instantly. This is the best quip of the day.β
Lessons Learned
1. The Parser Wasnβt the Problem β The Architecture Was
Eating tables was a symptom, not the disease. Once we separated concerns (content vs UI, structure vs presentation), the issues resolved themselves.
2. Occamβs Razor Applies to CMS Design
βThe simplest solution that works.β
Files, not databases. Folders, not tables. PDCA, not chaos.
3. Content Is Sacred. UI Is Ephemeral.
βThe content sings. The masks change. The performance is seamless.β
4. PDCA Is a Fractal
Same pattern at every level: PLAN β DO β CHECK β ACT. Each stage has its own mini-PDCA. The pattern recurses infinitely.
5. The Name Matters
Occamβs Opera CMS captures the essence:
- Occam = Simplicity (files, not databases)
- Opera = Performance (masks change, singer remains)
- CMS = Content management
Architecture Reference
Final File Structure
occam-opera-cms/
βββ inbox/ β Drop .md files here
β βββ _done/ β Archived sources
β
βββ work/ β Processing workspace
β βββ {slug}/ β One folder per article
β βββ {slug}.md β Enriched source
β βββ {slug}.json β Blueprint
β βββ {slug}.html β Rendered
β βββ og.jpg β Social image
β βββ *.ok β Stage tokens
β
βββ web/ β Published site
β βββ snippets/ β Dynamic dress
β β βββ header.html
β β βββ footer.html
β βββ blog/{slug}/
β β βββ index.html
β βββ site_config.json β Widgets, sidebars
β βββ index.json β Master registry
β
βββ categories.json β Topic classification
βββ tags.json β Tag generation
βββ config.toml β Pipeline settings
βββ template.html β Base template
β
βββ *.py, *.bat β 20+ pipeline scripts
Architecture Evolution
Week 1: Monolith (Parser ate tables)
Week 2: PDCA Fractal (PLAN β DO β CHECK β ACT)
Week 3: Static Site Generator (work/ β web/)
Week 4: Quality Gates (CHECK stage)
Week 5: Operational Polish (Email, Minify, Reports)
Week 6: Template Transformation (The Dress)
Week 7: Dynamic Dress Revolution (The Opera)
The Name
Occamβs Opera CMS
βWhere complexity yields to simplicity, content is the star, and the UI is just the costume.β
Closing
βOccamβs Opera CMS β where content is permanent, the dress changes instantly, and the parser no longer eats tables.β
Except durians. We still donβt talk about durians.
Documentation generated by Occamβs Opera CMS Pipeline ππ