Occam’s Opera CMS: The Complete Journey

From β€œEating Tables” to Face-Changing Opera

A Technical Chronicle


Table of Contents

  1. Week 1: The Parser That Ate Tables
  2. Week 2: The PDCA Fractal
  3. Week 3: The Static Site Generator Matures
  4. Week 4: CHECK Stage β€” Quality Gates
  5. Week 5: ACT Stage β€” Email, Minify, Reports
  6. Week 6: Template Transformation β€” The Dress
  7. Week 7: The Dynamic Dress Revolution
  8. Lessons Learned
  9. 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

Refined System 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:


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 πŸŽ­πŸš€