Sandbox/Version demo: Difference between revisions
From OHC Network Wiki
More languages
More actions
Content deleted Content added
OHC identity seed |
OHC identity seed |
||
| Line 7: | Line 7: | ||
}} |
}} |
||
{{Info|title=Sandbox |
{{Info|title=Sandbox demonstration|1=This page demonstrates the wiki's '''copy-on-write versioning + Cargo/SMW stack''' as a replacement for Docusaurus's versioned documentation model. Use the '''version dropdown''' in the badge strip above to navigate between versions — or read on for the full comparison.}} |
||
== The problem with Docusaurus versioning == |
|||
== Notification system (v4.0 — current) == |
|||
When you run <code>npm run docusaurus docs:version 3.1</code>, Docusaurus copies the '''entire''' <code>docs/</code> tree into <code>versioned_docs/version-3.1/</code>. For the CARE documentation: |
|||
The notification system handles all outbound communication from the platform to users, providers, and external systems. |
|||
* 98 pages per version |
|||
=== Channels === |
|||
* 2 versions (3.0, 3.1) = '''196 files''' committed to git |
|||
* Actual content difference between 3.0 and 3.1: '''zero bytes''' |
|||
Every edit to the "latest" docs must be manually backported (or not) to older versions. There's no query for "what changed" — you <code>diff</code> two directories. Navigation is hard-coded in <code>sidebars.js</code> files that must also be duplicated. |
|||
== How this wiki solves it == |
|||
=== Copy-on-write: fork only what diverges === |
|||
{| class="wikitable" |
{| class="wikitable" |
||
! Principle !! Implementation |
|||
! Channel !! Added in !! v4.0 changes |
|||
|- |
|||
| Email || 1.0 || MJML templates + AMP for interactive emails |
|||
|- |
|- |
||
| '''Canonical = latest''' || You're reading it. One page, no prefixes, no duplication. |
|||
| In-app || 2.0 || Unified inbox with thread grouping |
|||
|- |
|- |
||
| '''Fork on divergence''' || When content changes between releases, the old text is snapshotted to a subpage (<code>Page/3.0</code>) marked <code>status=frozen</code>. |
|||
| SMS || 2.0 || Smart routing (cost-optimized per region) |
|||
|- |
|- |
||
| '''Implicit version spanning''' || A page with <code>IntroducedIn=3.0</code> and empty <code>DeprecatedIn</code> is valid for 3.0, 3.1, 3.2, … — it doesn't need to be copied. |
|||
| Push (mobile) || 3.0 || Rich media attachments, action buttons |
|||
|- |
|- |
||
| '''__NOINDEX__ on forks''' || Search engines only see the latest. No duplicate-content penalties. |
|||
| Webhook || 3.0 || Now supports HMAC signing + retry policies |
|||
|- |
|||
| WhatsApp || '''4.0''' || Via official Business API |
|||
|- |
|||
| Slack/Teams || '''4.0''' || Org-configurable bot integrations |
|||
|} |
|} |
||
=== Global version dropdown (like Docusaurus navbar) === |
|||
=== Scheduling & orchestration === |
|||
The dropdown in the badge strip above is injected by <code>MediaWiki:Common.js</code> on every page with <code><nowiki>{{Doc header}}</nowiki></code>. It: |
|||
New in v4.0 — notifications can be scheduled, cancelled, and A/B tested: |
|||
# '''Persists''' your version choice in <code>localStorage</code> |
|||
<syntaxhighlight lang="json"> |
|||
# '''Auto-redirects''' on page load: if you've selected "3.0" and you navigate to a page that has a <code>/3.0</code> fork, you land there automatically |
|||
{ |
|||
# '''Stays''' on the canonical page with a toast notification when no fork exists (because the content is unchanged) |
|||
"template": "appointment_reminder", |
|||
# '''Works across the entire wiki''' — one global context, same as Docusaurus's URL-prefix approach |
|||
"recipients": ["patient:uuid-123"], |
|||
"channels": ["email", "whatsapp"], |
|||
=== Cargo: structured version metadata === |
|||
"schedule": { |
|||
"send_at": "2026-07-10T08:00:00Z", |
|||
Every page declares its version range in machine-readable Cargo fields. This enables: |
|||
"cancel_if": "appointment.status == 'cancelled'" |
|||
}, |
|||
'''Live query — all versions of this demo page:''' |
|||
"variants": { |
|||
"control": { "subject": "Appointment tomorrow" }, |
|||
{{#cargo_query:tables=Docs |
|||
"test_a": { "subject": "See you tomorrow, {{patient.first_name}}!" } |
|||
|fields=_pageName=Page,IntroducedIn=Since,DeprecatedIn=Until,Status |
|||
} |
|||
|where=_pageName LIKE 'Sandbox/Version demo%' |
|||
} |
|||
|order by=IntroducedIn |
|||
</syntaxhighlight> |
|||
|format=table |
|||
}} |
|||
'''What would "what's new in 4.0?" look like?''' |
|||
{{#cargo_query:tables=Docs |
|||
|fields=_pageName=Page,DocType=Type,Domain |
|||
|where=IntroducedIn='4.0' AND Status='current' |
|||
|format=table |
|||
|default=''(No real CARE pages are tagged 4.0 — this query would populate automatically when 4.0 ships.)'' |
|||
}} |
|||
In Docusaurus, answering "what changed in 3.1" requires diffing two directories of 98 files. Here it's one Cargo query. |
|||
=== SMW: automatic relationship graphs === |
|||
The <code><nowiki>{{Related}}</nowiki></code> template at the bottom of every doc page uses Semantic MediaWiki inverse queries. If page A declares <code>reference=Page B</code>, then page B's Related section '''automatically''' lists page A — no manual maintenance, no stale cross-references. Docusaurus has <code>onBrokenLinks: throw</code> at build time; we have '''live bidirectional links''' that never go stale because they're computed at render time. |
|||
=== Per-organization configuration === |
|||
== Head-to-head comparison == |
|||
Organizations can now override global channel settings: |
|||
{| class="wikitable" |
{| class="wikitable" |
||
! |
! Dimension !! Docusaurus !! This wiki |
||
|- |
|- |
||
| '''Storage per version''' || Full tree copy (O(n × versions)) || Fork only diverged pages (O(changed)) |
|||
| Enabled channels || Per org || Which channels are active |
|||
|- |
|- |
||
| '''4 versions, 100 pages, 5 changes per version''' || '''400 files''' || '''115 pages''' (100 canonical + 15 forks) |
|||
| Sender identity || Per org || From address, WhatsApp number, bot name |
|||
|- |
|- |
||
| '''Our actual case''' (98 pages, 3.0 ≡ 3.1) || 196 files (all identical) || '''98 pages''' (zero forks needed) |
|||
| Quiet hours || Per org || Suppress non-urgent sends during configured windows |
|||
|- |
|- |
||
| '''Global version dropdown''' || Built-in (navbar) || ✅ <code>MediaWiki:Common.js</code> (localStorage + auto-redirect) |
|||
| Fallback chain || Per org || e.g. try Push → SMS → Email in order |
|||
|- |
|||
| '''Version persistence''' || URL path prefix (<code>/3.0/docs/...</code>) || localStorage (survives navigation, no URL noise) |
|||
|- |
|||
| '''"What changed in X?"''' || <code>diff -r</code> two directories || One Cargo query: <code>WHERE IntroducedIn='X'</code> |
|||
|- |
|||
| '''Cross-references''' || Relative paths; break silently without CI || SMW inverse queries; always current, never stale |
|||
|- |
|||
| '''Navigation/sidebar''' || Hand-maintained <code>sidebars.js</code> (duplicated per version) || Cargo query: <code>WHERE DocType='concept' ORDER BY Domain, SidebarOrder</code> |
|||
|- |
|||
| '''Broken link detection''' || Build-time (<code>onBrokenLinks: throw</code>) || '''Impossible by design''' — links are queries, not hard paths |
|||
|- |
|||
| '''Searchability of metadata''' || None (frontmatter is build-only) || Full: <code>Special:CargoTables</code>, <code>Special:Ask</code>, API queries |
|||
|- |
|||
| '''Edit workflow''' || PR → merge → rebuild → deploy || Edit in browser (or push.py) → live in seconds |
|||
|- |
|||
| '''i18n''' || Separate <code>i18n/</code> tree per locale || Translate extension: per-paragraph, tracks staleness |
|||
|- |
|||
| '''Release process''' || <code>npm run docs:version X</code> (copies everything) || Fork only changed pages + update one JS line |
|||
|- |
|||
| '''Structured field data''' || Markdown tables (not queryable) || Cargo <code>ModelField</code> table: 500 fields across 35 models, queryable |
|||
|- |
|||
| '''Glossary''' || None || Lingo extension: hover-definitions site-wide from one page |
|||
|} |
|} |
||
== The version demo in action == |
|||
=== Architecture (v4.0) === |
|||
This page has '''three frozen forks''' showing progressive evolution: |
|||
<syntaxhighlight lang="text"> |
|||
Action / Scheduler / API |
|||
* [[Sandbox/Version demo/1.0]] — email only, sync, 2 config variables |
|||
↓ |
|||
* [[Sandbox/Version demo/2.0]] — async + multi-channel + preferences |
|||
Orchestrator (evaluates schedule, cancel_if, variant selection) |
|||
* [[Sandbox/Version demo/3.0]] — templates + tracking + push + batching + live Cargo table |
|||
↓ |
|||
Fan-out (per recipient × channel, respects fallback chain) |
|||
Each fork only stores '''its own content''' — not copies of every other page in the wiki. The version dropdown lets you navigate between them exactly like Docusaurus's version picker, but without the storage explosion. |
|||
↓ |
|||
Queue (Redis Streams, partitioned by priority) |
|||
↓ |
|||
Workers (channel-specific adapters, batch-aware) |
|||
↓ |
|||
Delivery tracker (status, attempts, receipts, analytics) |
|||
</syntaxhighlight> |
|||
== Storage comparison (this demo) == |
|||
== Evolution summary == |
|||
{| class="wikitable" |
{| class="wikitable" |
||
! |
! Versions !! Docusaurus model !! Wiki model !! Savings |
||
|- |
|- |
||
| 1.0 || |
| 1.0 || 1 full copy || 1 page (canonical at the time) || — |
||
|- |
|- |
||
| 2.0 || 2 full copies || 1 canonical + 1 fork || 50% |
|||
| 2.0 || Async, SMS, in-app, preferences || copy entire tree = '''2''' total || [[Sandbox/Version demo/2.0|1 frozen fork]] |
|||
|- |
|- |
||
| 3.0 || 3 full copies || 1 canonical + 2 forks || 67% |
|||
| 3.0 || Templates, push, webhooks, tracking, batching || copy entire tree = '''3''' total || [[Sandbox/Version demo/3.0|1 frozen fork]] |
|||
|- |
|- |
||
| 4.0 || 4 full copies || 1 canonical + 3 forks || 75% |
|||
| 4.0 || WhatsApp, Slack, scheduling, A/B, per-org config || copy entire tree = '''4''' total || '''1 canonical page''' (you're reading it) |
|||
|- |
|- |
||
! colspan="2" | |
! colspan="2" | At scale: 100 pages × 10 versions !! !! |
||
|- |
|||
| (assuming 10% changes per release) || '''1000 files''' || '''190 pages''' (100 + 90 forks) || '''81%''' |
|||
|} |
|} |
||
== Release checklist (vs Docusaurus) == |
|||
{{Note|1=In a real doc site with 100 pages, Docusaurus would store '''400 files''' for 4 versions (most identical). This wiki stores '''103 pages''' — the 100 canonical pages plus only the 3 that actually diverged.}} |
|||
=== Docusaurus === |
|||
== How the versioning works == |
|||
<syntaxhighlight lang="bash"> |
|||
# '''Canonical page = latest.''' You are reading it. |
|||
# 1. Copy entire tree (even unchanged pages) |
|||
# '''Fork only when content changes.''' Unchanged pages span all versions implicitly (their <code>IntroducedIn</code> field covers the full range). |
|||
npm run docusaurus docs:version 4.1 |
|||
# '''Version switcher''' (the pill strip below the title ↑) is auto-generated: <code><nowiki>{{Version switcher}}</nowiki></code> queries Cargo for subpages with <code>Status=frozen</code>. |
|||
# '''Frozen banner''' on old pages links readers back to this canonical page. |
|||
# '''"What changed in version X?"''' is a single Cargo query: <code><nowiki>{{#cargo_query: tables=Docs | where=IntroducedIn='4.0'}}</nowiki></code> |
|||
# 2. Update versions.json |
|||
# 3. Duplicate sidebars.js |
|||
# 4. Rebuild and deploy |
|||
npm run build && npm run deploy |
|||
# Total: hundreds of files touched, full CI/CD cycle |
|||
{{#cargo_query:tables=Docs |
|||
</syntaxhighlight> |
|||
|fields=_pageName=Page,IntroducedIn=Introduced,DeprecatedIn=Deprecated,Status |
|||
|where=_pageName LIKE 'Sandbox/Version demo%' |
|||
=== This wiki === |
|||
|order by=IntroducedIn |
|||
|format=table |
|||
<syntaxhighlight lang="bash"> |
|||
}} |
|||
# 1. For each page that ACTUALLY CHANGED: |
|||
# - Copy current content to Page/4.0 subpage (mark frozen) |
|||
# - Update canonical page with new content |
|||
# 2. Update one line in MediaWiki:Common.js: |
|||
# KNOWN_VERSIONS = [ '4.1', '4.0', '3.1', ... ]; |
|||
# 3. Done. No build step. No deploy. Live immediately. |
|||
</syntaxhighlight> |
|||
== Try it yourself == |
|||
# '''Use the dropdown above''' — select "3.0" and you'll be taken to [[Sandbox/Version demo/3.0|the frozen 3.0 page]]. |
|||
# '''Navigate to another page''' (e.g. [[Concepts/Patient]]) — the dropdown shows your stored preference. Since Patient has no fork, you see a toast: "This page is unchanged in CARE 3.0". |
|||
# '''Select "4.0 (latest)"''' to return to the canonical view everywhere. |
|||
[[Category:Sandbox]] |
[[Category:Sandbox]] |
||