Merge branch 'worktree-iso-phase-1' into macros

# Conflicts:
#	shared/sx/helpers.py
#	shared/sx/pages.py
#	sx/sx/nav-data.sx
#	sx/sx/plans.sx
#	sx/sxc/pages/docs.sx
This commit is contained in:
2026-03-07 08:38:32 +00:00
6 changed files with 308 additions and 9 deletions

View File

@@ -463,11 +463,12 @@ def components_for_request(source: str = "",
Reads the ``SX-Components`` header (comma-separated component names
like ``~card,~nav-item``) and returns only the definitions the client
is missing. If *source* is provided, only sends components needed
for that source (plus transitive deps).
for that source (plus transitive deps). If the header is absent,
returns all needed defs.
*extra_names* — additional component names to include beyond what
*source* references. Used by defpage to send components the page's
content expression needs for client-side routing.
*extra_names* — additional component names (``~foo``) to include
beyond what *source* references. Used by ``execute_page`` to send
components the page's content expression needs for client-side routing.
"""
from quart import request
from .jinja_bridge import _COMPONENT_ENV

View File

@@ -282,7 +282,7 @@ async def execute_page(
# Compute content expression deps so the server sends component
# definitions the client needs for future client-side routing
extra_deps: set[str] | None = None
if page_def.content_expr is not None:
if page_def.content_expr is not None and page_def.data_expr is None:
from .deps import components_needed
from .parser import serialize
try:

View File

@@ -8,12 +8,22 @@ from __future__ import annotations
import re
def _escape(text: str) -> str:
"""Escape a token for embedding in an SX string literal."""
return (text
.replace("\\", "\\\\")
.replace('"', '\\"')
.replace("\n", "\\n")
.replace("\t", "\\t")
.replace("\r", "\\r"))
def highlight_sx(code: str) -> str:
"""Highlight s-expression source code as sx with Tailwind spans."""
tokens = _tokenize_sx(code)
parts = []
for kind, text in tokens:
escaped = text.replace("\\", "\\\\").replace('"', '\\"')
escaped = _escape(text)
if kind == "comment":
parts.append(f'(span :class "text-stone-400 italic" "{escaped}")')
elif kind == "string":
@@ -94,7 +104,7 @@ def highlight_python(code: str) -> str:
tokens = _tokenize_python(code)
parts = []
for kind, text in tokens:
escaped = text.replace("\\", "\\\\").replace('"', '\\"')
escaped = _escape(text)
if kind == "comment":
parts.append(f'(span :class "text-stone-400 italic" "{escaped}")')
elif kind == "string":
@@ -176,7 +186,7 @@ def highlight_bash(code: str) -> str:
tokens = _tokenize_bash(code)
parts = []
for kind, text in tokens:
escaped = text.replace("\\", "\\\\").replace('"', '\\"')
escaped = _escape(text)
if kind == "comment":
parts.append(f'(span :class "text-stone-400 italic" "{escaped}")')
elif kind == "string":

View File

@@ -111,6 +111,8 @@
(dict :label "Async IO" :href "/isomorphism/async-io")))
(define plans-nav-items (list
(dict :label "Status" :href "/plans/status"
:summary "Audit of all plans — what's done, what's in progress, and what remains.")
(dict :label "Reader Macros" :href "/plans/reader-macros"
:summary "Extensible parse-time transformations via # dispatch — datum comments, raw strings, and quote shorthand.")
(dict :label "SX-Activity" :href "/plans/sx-activity"
@@ -118,7 +120,13 @@
(dict :label "Predictive Prefetching" :href "/plans/predictive-prefetch"
:summary "Prefetch missing component definitions before the user clicks — hover a link, fetch its deps, navigate client-side.")
(dict :label "Content-Addressed Components" :href "/plans/content-addressed-components"
:summary "Components identified by CID, stored on IPFS, fetched from anywhere. Canonical serialization, content verification, federated sharing.")))
:summary "Components identified by CID, stored on IPFS, fetched from anywhere. Canonical serialization, content verification, federated sharing.")
(dict :label "Fragment Protocol" :href "/plans/fragment-protocol"
:summary "Structured sexp request/response for cross-service component transfer.")
(dict :label "Glue Decoupling" :href "/plans/glue-decoupling"
:summary "Eliminate all cross-app model imports via glue service layer.")
(dict :label "Social Sharing" :href "/plans/social-sharing"
:summary "OAuth-based sharing to Facebook, Instagram, Threads, Twitter/X, LinkedIn, and Mastodon.")))
(define bootstrappers-nav-items (list
(dict :label "Overview" :href "/bootstrappers/")

View File

@@ -1264,6 +1264,282 @@
". It extends Phase 3 by making more navigations go client-side without needing any IO bridge — purely by ensuring component definitions are available before they're needed.")
(div :class "rounded border border-amber-200 bg-amber-50 p-3 mt-2"
(p :class "text-amber-800 text-sm" (strong "Depends on: ") "Phase 3 (client-side routing with deps checking). No dependency on Phase 4.")))))
;; ---------------------------------------------------------------------------
;; Plan Status Overview
;; ---------------------------------------------------------------------------
(defcomp ~plan-status-content ()
(~doc-page :title "Plan Status"
(p :class "text-lg text-stone-600 mb-6"
"Audit of all plans across the SX language and Rose Ash platform. Last updated March 2026.")
;; -----------------------------------------------------------------------
;; Completed
;; -----------------------------------------------------------------------
(~doc-section :title "Completed" :id "completed"
(div :class "space-y-4"
(div :class "rounded border border-green-200 bg-green-50 p-4"
(div :class "flex items-center gap-2 mb-1"
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
(span :class "font-semibold text-stone-800" "Split Cart into Microservices"))
(p :class "text-sm text-stone-600" "Cart decomposed into 4 services: relations (internal, owns ContainerRelation), likes (internal, unified generic likes), orders (public, owns Order/OrderItem + SumUp checkout), and cart (thin CartItem CRUD). All three new services deployed with dedicated databases."))
(div :class "rounded border border-green-200 bg-green-50 p-4"
(div :class "flex items-center gap-2 mb-1"
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
(span :class "font-semibold text-stone-800" "Ticket Purchase Through Cart"))
(p :class "text-sm text-stone-600" "Tickets flow through the cart like products: state=pending in cart, reserved at checkout, confirmed on payment. TicketDTO, CartSummaryDTO with ticket_count/ticket_total, CalendarService protocol methods all implemented."))
(div :class "rounded border border-green-200 bg-green-50 p-4"
(div :class "flex items-center gap-2 mb-1"
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
(span :class "font-semibold text-stone-800" "Ticket UX Improvements"))
(p :class "text-sm text-stone-600" "+/- quantity buttons on entry pages and cart. Tickets grouped by event in cart display. Adjust quantity route, sold/basket counts, matching product card UX pattern."))
(div :class "rounded border border-green-200 bg-green-50 p-4"
(div :class "flex items-center gap-2 mb-1"
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
(a :href "/isomorphism/" :class "font-semibold text-green-800 underline" "Isomorphic Phase 1: Dependency Analysis"))
(p :class "text-sm text-stone-600" "Per-page component bundles via deps.sx. Transitive closure, scan-refs, components-needed, page-css-classes. 15 tests, bootstrapped to both hosts."))
(div :class "rounded border border-green-200 bg-green-50 p-4"
(div :class "flex items-center gap-2 mb-1"
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
(a :href "/isomorphism/" :class "font-semibold text-green-800 underline" "Isomorphic Phase 2: IO Detection"))
(p :class "text-sm text-stone-600" "Automatic IO classification. scan-io-refs, transitive-io-refs, compute-all-io-refs. Server expands IO components, serializes pure ones for client."))
(div :class "rounded border border-green-200 bg-green-50 p-4"
(div :class "flex items-center gap-2 mb-1"
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
(a :href "/isomorphism/" :class "font-semibold text-green-800 underline" "Isomorphic Phase 3: Client-Side Routing"))
(p :class "text-sm text-stone-600" "router.sx spec, page registry via <script type=\"text/sx-pages\">, client route matching, try-first/fallback to server. Pure pages render without server roundtrips."))))
;; -----------------------------------------------------------------------
;; In Progress / Partial
;; -----------------------------------------------------------------------
(~doc-section :title "In Progress" :id "in-progress"
(div :class "space-y-4"
(div :class "rounded border border-amber-200 bg-amber-50 p-4"
(div :class "flex items-center gap-2 mb-1"
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-amber-600 text-white uppercase" "Partial")
(a :href "/plans/fragment-protocol" :class "font-semibold text-amber-900 underline" "Fragment Protocol"))
(p :class "text-sm text-stone-600" "Fragment GET infrastructure works. The planned POST/sexp structured protocol for transferring component definitions between services is not yet implemented. Fragment endpoints still use legacy GET + X-Fragment-Request headers."))
(div :class "rounded border border-amber-200 bg-amber-50 p-4"
(div :class "flex items-center gap-2 mb-1"
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-amber-600 text-white uppercase" "Partial")
(a :href "/isomorphism/" :class "font-semibold text-amber-900 underline" "Isomorphic Phase 4: Client Async & IO Bridge"))
(p :class "text-sm text-stone-600" "Some async evaluation infrastructure exists (helpers.py, async_eval.py). The io-bridge.sx spec file for client-side IO primitives (query -> REST, frag -> fetch) does not exist yet."))))
;; -----------------------------------------------------------------------
;; Not Started
;; -----------------------------------------------------------------------
(~doc-section :title "Not Started" :id "not-started"
(div :class "space-y-4"
(div :class "rounded border border-stone-200 bg-stone-50 p-4"
(div :class "flex items-center gap-2 mb-1"
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-stone-500 text-white uppercase" "Not Started")
(a :href "/plans/reader-macros" :class "font-semibold text-stone-800 underline" "Reader Macros"))
(p :class "text-sm text-stone-600" "# dispatch character for datum comments (#;), raw strings (#|...|), and quote shorthand (#'). Fully designed but no implementation in parser.sx or parser.py.")
(p :class "text-sm text-stone-500 mt-1" "Remaining: spec in parser.sx, Python in parser.py, rebootstrap both targets."))
(div :class "rounded border border-stone-200 bg-stone-50 p-4"
(div :class "flex items-center gap-2 mb-1"
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-stone-500 text-white uppercase" "Not Started")
(a :href "/plans/sx-activity" :class "font-semibold text-stone-800 underline" "SX-Activity"))
(p :class "text-sm text-stone-600" "Federated SX over ActivityPub — 6 phases from SX wire format for activities to the evaluable web on IPFS. Existing AP infrastructure provides the foundation but no SX-specific federation code exists.")
(p :class "text-sm text-stone-500 mt-1" "Remaining: shared/sx/activity.py (SX<->JSON-LD), shared/sx/ipfs.py, shared/sx/ref/ipfs-resolve.sx, shared/sx/registry.py, shared/sx/anchor.py."))
(div :class "rounded border border-stone-200 bg-stone-50 p-4"
(div :class "flex items-center gap-2 mb-1"
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-stone-500 text-white uppercase" "Not Started")
(a :href "/plans/glue-decoupling" :class "font-semibold text-stone-800 underline" "Cross-App Decoupling via Glue"))
(p :class "text-sm text-stone-600" "Eliminate all cross-app model imports by routing through a glue service layer. No glue/ directory exists. Apps are currently decoupled via HTTP interfaces and DTOs instead.")
(p :class "text-sm text-stone-500 mt-1" "Remaining: glue/services/ for pages, page_config, calendars, marketplaces, cart_items, products, post_associations. 25+ cross-app imports to eliminate."))
(div :class "rounded border border-stone-200 bg-stone-50 p-4"
(div :class "flex items-center gap-2 mb-1"
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-stone-500 text-white uppercase" "Not Started")
(a :href "/plans/social-sharing" :class "font-semibold text-stone-800 underline" "Social Network Sharing"))
(p :class "text-sm text-stone-600" "OAuth-based sharing to Facebook, Instagram, Threads, Twitter/X, LinkedIn, and Mastodon via the account service. No models, blueprints, or platform clients created.")
(p :class "text-sm text-stone-500 mt-1" "Remaining: SocialConnection model, social_crypto.py, platform OAuth clients (6), account/bp/social/ blueprint, share button fragment."))
(div :class "rounded border border-stone-200 bg-stone-50 p-4"
(div :class "flex items-center gap-2 mb-1"
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-stone-500 text-white uppercase" "Not Started")
(a :href "/isomorphism/" :class "font-semibold text-stone-800 underline" "Isomorphic Phase 5: Streaming & Suspense"))
(p :class "text-sm text-stone-600" "Server streams partially-evaluated SX as IO resolves. Client renders available subtrees immediately, fills in suspended parts. No suspense.sx spec or chunked transfer implementation.")
(p :class "text-sm text-stone-500 mt-1" "Depends on: Phase 4 (client async)."))
(div :class "rounded border border-stone-200 bg-stone-50 p-4"
(div :class "flex items-center gap-2 mb-1"
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-stone-500 text-white uppercase" "Not Started")
(a :href "/isomorphism/" :class "font-semibold text-stone-800 underline" "Isomorphic Phase 6: Full Isomorphism"))
(p :class "text-sm text-stone-600" "Runtime boundary optimizer, affinity annotations, offline data layer via Service Worker + IndexedDB, isomorphic testing harness.")
(p :class "text-sm text-stone-500 mt-1" "Depends on: all previous phases."))))))
;; ---------------------------------------------------------------------------
;; Fragment Protocol
;; ---------------------------------------------------------------------------
(defcomp ~plan-fragment-protocol-content ()
(~doc-page :title "Fragment Protocol"
(~doc-section :title "Context" :id "context"
(p "Fragment endpoints return raw sexp source (e.g., " (code "(~blog-nav-wrapper :items ...)") "). The consuming service embeds this in its page sexp, which the client evaluates. But service-specific components like " (code "~blog-nav-wrapper") " are only in that service's component env — not in the consumer's. So the consumer's " (code "client_components_tag()") " never sends them to the client, causing \"Unknown component\" errors.")
(p "The fix: transfer component definitions alongside fragments. Services tell the provider what they already have; the provider sends only what's missing."))
(~doc-section :title "What exists" :id "exists"
(div :class "rounded border border-green-200 bg-green-50 p-4"
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
(li "Fragment GET infrastructure works (" (code "shared/infrastructure/fragments.py") ")")
(li (code "X-Fragment-Request") " header protocol for internal service calls")
(li "Content type negotiation for text/html and text/sx responses")
(li "Fragment caching and composition in page rendering"))))
(~doc-section :title "What remains" :id "remains"
(div :class "rounded border border-amber-200 bg-amber-50 p-4"
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
(li (strong "POST sexp protocol: ") "Switch from GET to POST with structured sexp body containing " (code ":components") " list of what consumer already has")
(li (strong "Structured response: ") (code "(fragment-response :defs (...) :content (...))") " — provider sends only missing component defs")
(li (strong (code "fragment_response()") " builder: ") "New function in helpers.py that diffs provider's component env against consumer's list")
(li (strong "Register received defs: ") "Consumer parses " (code ":defs") " from response and registers into its " (code "_COMPONENT_ENV"))
(li (strong "Shared blueprint factory: ") (code "create_fragment_blueprint(handlers)") " to deduplicate the identical fragment endpoint pattern across 8 services"))))
(~doc-section :title "Files to modify" :id "files"
(div :class "overflow-x-auto rounded border border-stone-200"
(table :class "w-full text-left text-sm"
(thead (tr :class "border-b border-stone-200 bg-stone-100"
(th :class "px-3 py-2 font-medium text-stone-600" "File")
(th :class "px-3 py-2 font-medium text-stone-600" "Change")))
(tbody
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "shared/infrastructure/fragments.py")
(td :class "px-3 py-2 text-stone-700" "POST sexp body, parse response, register defs"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "shared/sx/helpers.py")
(td :class "px-3 py-2 text-stone-700" "fragment_response() builder"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "shared/infrastructure/fragment_endpoint.py")
(td :class "px-3 py-2 text-stone-700" "NEW — shared blueprint factory"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 font-mono text-sm text-violet-700" "*/bp/fragments/routes.py")
(td :class "px-3 py-2 text-stone-700" "All 8 services: use create_fragment_blueprint"))))))))
;; ---------------------------------------------------------------------------
;; Glue Decoupling
;; ---------------------------------------------------------------------------
(defcomp ~plan-glue-decoupling-content ()
(~doc-page :title "Cross-App Decoupling via Glue Services"
(~doc-section :title "Context" :id "context"
(p "All cross-domain FK constraints have been dropped (with pragmatic exceptions for OrderItem.product_id and CartItem). Cross-domain writes go through internal HTTP and activity bus. However, " (strong "25+ cross-app model imports remain") " — apps still import from each other's models/ directories. This means every app needs every other app's code on disk to start.")
(p "The goal: eliminate all cross-app model imports. Every app only imports from its own models/, from shared/, and from a new glue/ service layer."))
(~doc-section :title "Current state" :id "current"
(p "Apps are partially decoupled via HTTP interfaces (fetch_data, call_action, send_internal_activity) and DTOs. The Cart microservice split (relations, likes, orders) is complete. But direct model imports persist in:")
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
(li (strong "Cart") " — 9 files importing from market, events, blog")
(li (strong "Blog") " — 8 files importing from cart, events, market")
(li (strong "Events") " — 5 files importing from blog, market, cart")
(li (strong "Market") " — 1 file importing from blog")))
(~doc-section :title "What remains" :id "remains"
(div :class "space-y-3"
(div :class "rounded border border-stone-200 p-3"
(h4 :class "font-semibold text-stone-700" "1. glue/services/pages.py")
(p :class "text-sm text-stone-600" "Dict-based Post access for non-blog apps: get_page_by_slug, get_page_by_id, get_pages_by_ids, page_exists, search_posts."))
(div :class "rounded border border-stone-200 p-3"
(h4 :class "font-semibold text-stone-700" "2. glue/services/page_config.py")
(p :class "text-sm text-stone-600" "PageConfig CRUD: get_page_config, get_or_create_page_config, get_page_configs_by_ids."))
(div :class "rounded border border-stone-200 p-3"
(h4 :class "font-semibold text-stone-700" "3. glue/services/calendars.py")
(p :class "text-sm text-stone-600" "Calendar queries + entry associations (from blog): get_calendars_for_page, toggle_entry_association, get_associated_entries."))
(div :class "rounded border border-stone-200 p-3"
(h4 :class "font-semibold text-stone-700" "4. glue/services/marketplaces.py")
(p :class "text-sm text-stone-600" "MarketPlace CRUD (from blog+events): get_marketplaces_for_page, create_marketplace, soft_delete_marketplace."))
(div :class "rounded border border-stone-200 p-3"
(h4 :class "font-semibold text-stone-700" "5. glue/services/cart_items.py")
(p :class "text-sm text-stone-600" "CartItem/CalendarEntry queries for cart: get_cart_items, find_or_create_cart_item, clear_cart_for_order."))
(div :class "rounded border border-stone-200 p-3"
(h4 :class "font-semibold text-stone-700" "6. glue/services/products.py")
(p :class "text-sm text-stone-600" "Minimal Product access for cart orders: get_product."))
(div :class "rounded border border-stone-200 p-3"
(h4 :class "font-semibold text-stone-700" "7. Model registration + cleanup")
(p :class "text-sm text-stone-600" "register_models() in glue/setup.py, update all app.py files, delete moved service files."))))
(~doc-section :title "Docker consideration" :id "docker"
(p :class "text-stone-600" "For glue services to work in Docker (single app per container), model files from other apps must be importable. Recommended: try/except at import time — glue services that can't import a model raise ImportError at call time, which only happens if called from the wrong app."))))
;; ---------------------------------------------------------------------------
;; Social Sharing
;; ---------------------------------------------------------------------------
(defcomp ~plan-social-sharing-content ()
(~doc-page :title "Social Network Sharing"
(~doc-section :title "Context" :id "context"
(p "Rose Ash already has ActivityPub for federated social sharing. This plan adds OAuth-based sharing to mainstream social networks — Facebook, Instagram, Threads, Twitter/X, LinkedIn, and Mastodon.")
(p "All social logic lives in the " (strong "account") " microservice. Content apps get a share button that opens the account share page."))
(~doc-section :title "What remains" :id "remains"
(~doc-note "Nothing has been implemented. This is the full scope of work.")
(div :class "space-y-4"
(div :class "rounded border border-stone-200 p-4"
(h4 :class "font-semibold text-stone-700 mb-2" "Phase 1: Data Model + Encryption")
(ul :class "list-disc pl-5 text-stone-700 space-y-1 text-sm"
(li (code "shared/models/social_connection.py") " — SocialConnection model (user_id, platform, tokens, scopes, extra_data)")
(li (code "shared/infrastructure/social_crypto.py") " — Fernet encrypt/decrypt for tokens")
(li "Alembic migration for social_connections table")
(li "Environment variables for per-platform OAuth credentials")))
(div :class "rounded border border-stone-200 p-4"
(h4 :class "font-semibold text-stone-700 mb-2" "Phase 2: Platform OAuth Clients")
(p :class "text-sm text-stone-600 mb-2" "All in " (code "account/services/social_platforms/") ":")
(ul :class "list-disc pl-5 text-stone-700 space-y-1 text-sm"
(li (code "base.py") " — SocialPlatform ABC, OAuthResult, ShareResult")
(li (code "meta.py") " — Facebook + Instagram + Threads (Graph API)")
(li (code "twitter.py") " — OAuth 2.0 with PKCE")
(li (code "linkedin.py") " — LinkedIn Posts API")
(li (code "mastodon.py") " — Dynamic app registration per instance")))
(div :class "rounded border border-stone-200 p-4"
(h4 :class "font-semibold text-stone-700 mb-2" "Phase 3: Account Blueprint")
(ul :class "list-disc pl-5 text-stone-700 space-y-1 text-sm"
(li (code "account/bp/social/routes.py") " — /social/ list, /social/connect/<platform>/, /social/callback/<platform>/, /social/share/")
(li "Register before account blueprint (account has catch-all /<slug>/ route)")))
(div :class "rounded border border-stone-200 p-4"
(h4 :class "font-semibold text-stone-700 mb-2" "Phase 4: Templates")
(ul :class "list-disc pl-5 text-stone-700 space-y-1 text-sm"
(li "Social panel — platform cards, connect/disconnect")
(li "Share panel — content preview, account checkboxes, share button")
(li "Share result — per-platform success/failure with links")))
(div :class "rounded border border-stone-200 p-4"
(h4 :class "font-semibold text-stone-700 mb-2" "Phase 5: Share Button in Content Apps")
(ul :class "list-disc pl-5 text-stone-700 space-y-1 text-sm"
(li "share-button fragment from account service")
(li "Blog, events, market detail pages fetch and render the fragment")))
(div :class "rounded border border-stone-200 p-4"
(h4 :class "font-semibold text-stone-700 mb-2" "Phase 6: Token Refresh + Share History")
(ul :class "list-disc pl-5 text-stone-700 space-y-1 text-sm"
(li "Automatic token refresh before posting")
(li "Optional social_shares table for history and duplicate prevention")))))))
;; ---------------------------------------------------------------------------
;; Isomorphic Architecture Roadmap

View File

@@ -501,8 +501,12 @@
:current (find-current plans-nav-items slug))
:selected (or (find-current plans-nav-items slug) ""))
:content (case slug
"status" (~plan-status-content)
"reader-macros" (~plan-reader-macros-content)
"sx-activity" (~plan-sx-activity-content)
"predictive-prefetch" (~plan-predictive-prefetch-content)
"content-addressed-components" (~plan-content-addressed-components-content)
"fragment-protocol" (~plan-fragment-protocol-content)
"glue-decoupling" (~plan-glue-decoupling-content)
"social-sharing" (~plan-social-sharing-content)
:else (~plans-index-content)))