diff --git a/hub/blueprint/bp-ai-assistant-platform.radical b/hub/blueprint/bp-ai-assistant-platform.radical
index 0c4a045..a8ac504 100644
--- a/hub/blueprint/bp-ai-assistant-platform.radical
+++ b/hub/blueprint/bp-ai-assistant-platform.radical
@@ -405,26 +405,92 @@
"consequences": "Knowledge freshness becomes index-refresh latency instead of training-cycle latency, but overall answer quality is now capped by retrieval quality, not just model quality.",
"alternatives": "1. Periodic fine-tuning on new knowledge — keeps knowledge \"in\" the model, but the feedback loop is slow and risks catastrophic forgetting of earlier knowledge. 2. Retrieval only (chosen). 3. Retrieval plus light fine-tuning for tone and format only — a reasonable hybrid, at the cost of a second training pipeline. 4. Stuffing all knowledge into the prompt without retrieval — doesn't scale past a small, static document set."
},
+ {
+ "id": "flow-customer",
+ "type": "group",
+ "label": "Customer journey",
+ "description": "Getting an answer from the assistant, or a person when it cannot help.",
+ "x": 0,
+ "y": 2084,
+ "width": 1240,
+ "height": 330,
+ "collapsed": false
+ },
+ {
+ "id": "mockup-chat-start",
+ "type": "mockup",
+ "label": "Chat — Start",
+ "description": "Entry point with the most common intents.",
+ "parentId": "flow-customer",
+ "x": 30,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "chat widget",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-chat-answer",
+ "type": "mockup",
+ "label": "Chat — Grounded Answer",
+ "description": "Answer grounded in retrieved sources; actions need the customer's confirmation.",
+ "parentId": "flow-customer",
+ "x": 350,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "chat widget",
+ "wireframe": ""
+ },
{
"id": "mockup-support-chat",
"type": "mockup",
- "label": "Support Chat",
+ "label": "Chat — Handoff",
"description": "Customer-facing chat: the assistant answers with cited sources when it can ground the answer and hands off to a person when it cannot.",
- "x": 0,
- "y": 2084,
+ "parentId": "flow-customer",
+ "x": 670,
+ "y": 110,
"width": 220,
"height": 190,
"collapsed": false,
"screen": "chat widget",
"wireframe": ""
},
+ {
+ "id": "mockup-chat-human",
+ "type": "mockup",
+ "label": "Chat — Agent Joined",
+ "description": "A person joins with the full case history, within {{ESCALATION_SLA_MIN}} minutes of the handoff.",
+ "parentId": "flow-customer",
+ "x": 990,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "chat widget",
+ "wireframe": ""
+ },
+ {
+ "id": "flow-human-agent",
+ "type": "group",
+ "label": "Human agent journey",
+ "description": "Picking up escalations and closing them with a reviewed action.",
+ "x": 0,
+ "y": 2494,
+ "width": 1240,
+ "height": 330,
+ "collapsed": false
+ },
{
"id": "mockup-escalation-queue",
"type": "mockup",
"label": "Escalation Queue",
"description": "Human agents pick up escalated cases oldest-first; each case must be picked up within {{ESCALATION_SLA_MIN}} minutes.",
- "x": 320,
- "y": 2084,
+ "parentId": "flow-human-agent",
+ "x": 30,
+ "y": 110,
"width": 220,
"height": 190,
"collapsed": false,
@@ -436,13 +502,81 @@
"type": "mockup",
"label": "Case Review",
"description": "The assistant's proposed action with its confidence, per-field extraction confidence (below {{EXTRACTION_REVIEW_THRESHOLD_PCT}}% needs review) and the sources it used.",
- "x": 640,
- "y": 2084,
+ "parentId": "flow-human-agent",
+ "x": 350,
+ "y": 110,
"width": 220,
"height": 190,
"collapsed": false,
"screen": "/console/cases/:id",
"wireframe": ""
+ },
+ {
+ "id": "mockup-case-resolved",
+ "type": "mockup",
+ "label": "Case Resolved",
+ "description": "Resolution with every autonomous and human decision recorded for {{AUDIT_RETENTION_DAYS}} days.",
+ "parentId": "flow-human-agent",
+ "x": 670,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/console/cases/:id",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-case-audit",
+ "type": "mockup",
+ "label": "Case Audit Trail",
+ "description": "Search decisions, sources and confidence across cases for audits.",
+ "parentId": "flow-human-agent",
+ "x": 990,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/console/audit",
+ "wireframe": ""
+ },
+ {
+ "id": "flow-assistant-ops",
+ "type": "group",
+ "label": "Assistant ops journey",
+ "description": "Keeping knowledge fresh and answer quality from drifting.",
+ "x": 0,
+ "y": 2904,
+ "width": 600,
+ "height": 330,
+ "collapsed": false
+ },
+ {
+ "id": "mockup-kb-sources",
+ "type": "mockup",
+ "label": "Knowledge Sources",
+ "description": "Source sync status against the {{KB_STALENESS_HOURS}}-hour freshness limit.",
+ "parentId": "flow-assistant-ops",
+ "x": 30,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/ops/knowledge",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-quality-drift",
+ "type": "mockup",
+ "label": "Quality & Drift",
+ "description": "Autonomous resolution rate, eval results and cost per case.",
+ "parentId": "flow-assistant-ops",
+ "x": 350,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/ops/quality",
+ "wireframe": ""
}
],
"relations": [
@@ -611,6 +745,38 @@
"targetId": "adr-confidence-gated-autonomy",
"relationType": "traces-to"
},
+ {
+ "id": "mockup-chat-start--source-channel",
+ "sourceId": "mockup-chat-start",
+ "targetId": "source-channel",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-chat-start--mockup-chat-answer",
+ "sourceId": "mockup-chat-start",
+ "targetId": "mockup-chat-answer",
+ "relationType": "navigates-to",
+ "label": "Ask"
+ },
+ {
+ "id": "mockup-chat-answer--req-case-audit-trail",
+ "sourceId": "mockup-chat-answer",
+ "targetId": "req-case-audit-trail",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-chat-answer--source-channel",
+ "sourceId": "mockup-chat-answer",
+ "targetId": "source-channel",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-chat-answer--mockup-support-chat",
+ "sourceId": "mockup-chat-answer",
+ "targetId": "mockup-support-chat",
+ "relationType": "navigates-to",
+ "label": "Not resolved"
+ },
{
"id": "mockup-support-chat--req-escalation-pickup",
"sourceId": "mockup-support-chat",
@@ -623,6 +789,25 @@
"targetId": "source-channel",
"relationType": "presented-by"
},
+ {
+ "id": "mockup-support-chat--mockup-chat-human",
+ "sourceId": "mockup-support-chat",
+ "targetId": "mockup-chat-human",
+ "relationType": "navigates-to",
+ "label": "Agent joins"
+ },
+ {
+ "id": "mockup-chat-human--req-escalation-pickup",
+ "sourceId": "mockup-chat-human",
+ "targetId": "req-escalation-pickup",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-chat-human--source-channel",
+ "sourceId": "mockup-chat-human",
+ "targetId": "source-channel",
+ "relationType": "presented-by"
+ },
{
"id": "mockup-escalation-queue--req-escalation-pickup",
"sourceId": "mockup-escalation-queue",
@@ -665,6 +850,83 @@
"sourceId": "mockup-case-review",
"targetId": "agent-console",
"relationType": "presented-by"
+ },
+ {
+ "id": "mockup-case-review--mockup-case-resolved",
+ "sourceId": "mockup-case-review",
+ "targetId": "mockup-case-resolved",
+ "relationType": "navigates-to",
+ "label": "Approve & send"
+ },
+ {
+ "id": "mockup-case-resolved--req-case-audit-trail",
+ "sourceId": "mockup-case-resolved",
+ "targetId": "req-case-audit-trail",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-case-resolved--agent-console",
+ "sourceId": "mockup-case-resolved",
+ "targetId": "agent-console",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-case-resolved--mockup-case-audit",
+ "sourceId": "mockup-case-resolved",
+ "targetId": "mockup-case-audit",
+ "relationType": "navigates-to",
+ "label": "Audit trail"
+ },
+ {
+ "id": "mockup-case-resolved--mockup-escalation-queue",
+ "sourceId": "mockup-case-resolved",
+ "targetId": "mockup-escalation-queue",
+ "relationType": "navigates-to",
+ "label": "Next case"
+ },
+ {
+ "id": "mockup-case-audit--req-case-audit-trail",
+ "sourceId": "mockup-case-audit",
+ "targetId": "req-case-audit-trail",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-case-audit--agent-console",
+ "sourceId": "mockup-case-audit",
+ "targetId": "agent-console",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-kb-sources--req-kb-freshness",
+ "sourceId": "mockup-kb-sources",
+ "targetId": "req-kb-freshness",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-kb-sources--platform",
+ "sourceId": "mockup-kb-sources",
+ "targetId": "platform",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-kb-sources--mockup-quality-drift",
+ "sourceId": "mockup-kb-sources",
+ "targetId": "mockup-quality-drift",
+ "relationType": "navigates-to",
+ "label": "Quality"
+ },
+ {
+ "id": "mockup-quality-drift--platform",
+ "sourceId": "mockup-quality-drift",
+ "targetId": "platform",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-quality-drift--mockup-kb-sources",
+ "sourceId": "mockup-quality-drift",
+ "targetId": "mockup-kb-sources",
+ "relationType": "navigates-to",
+ "label": "Fix stale source"
}
],
"sequences": [
diff --git a/hub/blueprint/bp-analytical-data-platform.radical b/hub/blueprint/bp-analytical-data-platform.radical
index 3398bba..baed821 100644
--- a/hub/blueprint/bp-analytical-data-platform.radical
+++ b/hub/blueprint/bp-analytical-data-platform.radical
@@ -370,44 +370,153 @@
"consequences": "Version control, testing and documentation by default; CI/CD required for model deployment; learning curve for SQL-only teams.",
"alternatives": "1. Spark jobs only — flexible, no conventions for tests or lineage. 2. Stored procedures — vendor-coupled and hard to review."
},
+ {
+ "id": "flow-analyst",
+ "type": "group",
+ "label": "Analyst journey",
+ "description": "Finding a dashboard, exploring the data behind it and asking for access.",
+ "x": 0,
+ "y": 1534,
+ "width": 1560,
+ "height": 330,
+ "collapsed": false
+ },
+ {
+ "id": "mockup-bi-home",
+ "type": "mockup",
+ "label": "Dashboards",
+ "description": "Governed dashboards by owning team.",
+ "parentId": "flow-analyst",
+ "x": 30,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/dashboards",
+ "wireframe": ""
+ },
{
"id": "mockup-sales-dashboard",
"type": "mockup",
"label": "Sales Dashboard",
"description": "Self-service dashboard showing how fresh the data is; restricted columns are masked for users whose role does not allow them.",
- "x": 0,
- "y": 1534,
+ "parentId": "flow-analyst",
+ "x": 350,
+ "y": 110,
"width": 220,
"height": 190,
"collapsed": false,
"screen": "/dashboards/sales",
"wireframe": ""
},
+ {
+ "id": "mockup-explore",
+ "type": "mockup",
+ "label": "Explore",
+ "description": "Self-service exploration; row and column policies from the catalogue still apply.",
+ "parentId": "flow-analyst",
+ "x": 670,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/explore",
+ "wireframe": ""
+ },
{
"id": "mockup-dataset-lineage",
"type": "mockup",
"label": "Dataset & Lineage",
"description": "Catalogue page for a curated dataset with owner, classification and column-level lineage from source to dashboard.",
- "x": 320,
- "y": 1534,
+ "parentId": "flow-analyst",
+ "x": 990,
+ "y": 110,
"width": 220,
"height": 190,
"collapsed": false,
"screen": "/datasets/:name/lineage",
"wireframe": ""
},
+ {
+ "id": "mockup-access-request",
+ "type": "mockup",
+ "label": "Request Access",
+ "description": "Access to restricted columns is requested from the dataset owner, time-boxed and audited.",
+ "parentId": "flow-analyst",
+ "x": 1310,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/datasets/:name/access",
+ "wireframe": ""
+ },
+ {
+ "id": "flow-data-engineer",
+ "type": "group",
+ "label": "Data engineer journey",
+ "description": "Keeping pipelines fresh and bad data out of curated tables.",
+ "x": 0,
+ "y": 1944,
+ "width": 1240,
+ "height": 330,
+ "collapsed": false
+ },
+ {
+ "id": "mockup-pipelines",
+ "type": "mockup",
+ "label": "Pipelines",
+ "description": "All pipelines with schedule and freshness.",
+ "parentId": "flow-data-engineer",
+ "x": 30,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/pipelines",
+ "wireframe": ""
+ },
{
"id": "mockup-quality-runs",
"type": "mockup",
"label": "Data Quality Runs",
"description": "Run results with the quality gate applied: failing records are quarantined, the affected models fail and the dataset owner is notified before anything reaches consumers.",
- "x": 640,
- "y": 1534,
+ "parentId": "flow-data-engineer",
+ "x": 350,
+ "y": 110,
"width": 220,
"height": 190,
"collapsed": false,
"screen": "/runs",
"wireframe": ""
+ },
+ {
+ "id": "mockup-quarantine",
+ "type": "mockup",
+ "label": "Quarantine",
+ "description": "Rows that failed the quality gate, held back until fixed, discarded or released.",
+ "parentId": "flow-data-engineer",
+ "x": 670,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/quarantine/:run",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-freshness",
+ "type": "mockup",
+ "label": "Freshness Monitor",
+ "description": "Streaming latency against the {{INGESTION_LATENCY_SECONDS}} s target from event to raw zone.",
+ "parentId": "flow-data-engineer",
+ "x": 990,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/freshness",
+ "wireframe": ""
}
],
"relations": [
@@ -546,6 +655,19 @@
"targetId": "adr-dbt",
"relationType": "implements"
},
+ {
+ "id": "mockup-bi-home--bi",
+ "sourceId": "mockup-bi-home",
+ "targetId": "bi",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-bi-home--mockup-sales-dashboard",
+ "sourceId": "mockup-bi-home",
+ "targetId": "mockup-sales-dashboard",
+ "relationType": "navigates-to",
+ "label": "Open"
+ },
{
"id": "mockup-sales-dashboard--req-rls",
"sourceId": "mockup-sales-dashboard",
@@ -564,6 +686,13 @@
"targetId": "bi",
"relationType": "presented-by"
},
+ {
+ "id": "mockup-sales-dashboard--mockup-explore",
+ "sourceId": "mockup-sales-dashboard",
+ "targetId": "mockup-explore",
+ "relationType": "navigates-to",
+ "label": "Explore"
+ },
{
"id": "mockup-sales-dashboard--mockup-dataset-lineage",
"sourceId": "mockup-sales-dashboard",
@@ -571,6 +700,25 @@
"relationType": "navigates-to",
"label": "View dataset"
},
+ {
+ "id": "mockup-explore--req-rls",
+ "sourceId": "mockup-explore",
+ "targetId": "req-rls",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-explore--bi",
+ "sourceId": "mockup-explore",
+ "targetId": "bi",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-explore--mockup-access-request",
+ "sourceId": "mockup-explore",
+ "targetId": "mockup-access-request",
+ "relationType": "navigates-to",
+ "label": "Request access"
+ },
{
"id": "mockup-dataset-lineage--req-lineage",
"sourceId": "mockup-dataset-lineage",
@@ -584,11 +732,56 @@
"relationType": "presented-by"
},
{
- "id": "mockup-dataset-lineage--mockup-quality-runs",
+ "id": "mockup-dataset-lineage--mockup-access-request",
"sourceId": "mockup-dataset-lineage",
+ "targetId": "mockup-access-request",
+ "relationType": "navigates-to",
+ "label": "Request access"
+ },
+ {
+ "id": "mockup-access-request--req-rls",
+ "sourceId": "mockup-access-request",
+ "targetId": "req-rls",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-access-request--catalog",
+ "sourceId": "mockup-access-request",
+ "targetId": "catalog",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-access-request--mockup-dataset-lineage",
+ "sourceId": "mockup-access-request",
+ "targetId": "mockup-dataset-lineage",
+ "relationType": "navigates-to",
+ "label": "Send request"
+ },
+ {
+ "id": "mockup-pipelines--req-freshness",
+ "sourceId": "mockup-pipelines",
+ "targetId": "req-freshness",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-pipelines--orchestrator",
+ "sourceId": "mockup-pipelines",
+ "targetId": "orchestrator",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-pipelines--mockup-quality-runs",
+ "sourceId": "mockup-pipelines",
"targetId": "mockup-quality-runs",
"relationType": "navigates-to",
- "label": "Quality"
+ "label": "Open runs"
+ },
+ {
+ "id": "mockup-pipelines--mockup-freshness",
+ "sourceId": "mockup-pipelines",
+ "targetId": "mockup-freshness",
+ "relationType": "navigates-to",
+ "label": "Freshness"
},
{
"id": "mockup-quality-runs--req-quality-gate",
@@ -601,6 +794,44 @@
"sourceId": "mockup-quality-runs",
"targetId": "orchestrator",
"relationType": "presented-by"
+ },
+ {
+ "id": "mockup-quality-runs--mockup-quarantine",
+ "sourceId": "mockup-quality-runs",
+ "targetId": "mockup-quarantine",
+ "relationType": "navigates-to",
+ "label": "Open quarantine"
+ },
+ {
+ "id": "mockup-quarantine--req-quality-gate",
+ "sourceId": "mockup-quarantine",
+ "targetId": "req-quality-gate",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-quarantine--orchestrator",
+ "sourceId": "mockup-quarantine",
+ "targetId": "orchestrator",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-quarantine--mockup-quality-runs",
+ "sourceId": "mockup-quarantine",
+ "targetId": "mockup-quality-runs",
+ "relationType": "navigates-to",
+ "label": "Release after fix"
+ },
+ {
+ "id": "mockup-freshness--req-freshness",
+ "sourceId": "mockup-freshness",
+ "targetId": "req-freshness",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-freshness--orchestrator",
+ "sourceId": "mockup-freshness",
+ "targetId": "orchestrator",
+ "relationType": "presented-by"
}
]
}
diff --git a/hub/blueprint/bp-ecommerce-platform.radical b/hub/blueprint/bp-ecommerce-platform.radical
index ec639df..272e825 100644
--- a/hub/blueprint/bp-ecommerce-platform.radical
+++ b/hub/blueprint/bp-ecommerce-platform.radical
@@ -493,44 +493,223 @@
"consequences": "Independent deployment and scaling; cross-service reports require projections or a data platform; joins across services are not possible in SQL.",
"alternatives": "1. Shared database — simple joins, tight coupling. 2. Shared schema with per-service tables — coupling through migrations remains."
},
+ {
+ "id": "flow-shopper",
+ "type": "group",
+ "label": "Shopper journey",
+ "description": "From landing on the shop to tracking a delivered order.",
+ "x": 0,
+ "y": 2084,
+ "width": 1560,
+ "height": 630,
+ "collapsed": false
+ },
+ {
+ "id": "mockup-home",
+ "type": "mockup",
+ "label": "Home",
+ "description": "Landing page: search, campaigns and categories.",
+ "parentId": "flow-shopper",
+ "x": 30,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-search-results",
+ "type": "mockup",
+ "label": "Search Results",
+ "description": "Filtered results from the search index (at most {{INDEX_LAG_SECONDS}} s behind the catalogue).",
+ "parentId": "flow-shopper",
+ "x": 350,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/search",
+ "wireframe": ""
+ },
{
"id": "mockup-product-page",
"type": "mockup",
"label": "Product Page",
"description": "Product detail with live availability from the catalogue; \"Add to cart\" leads into checkout.",
- "x": 0,
- "y": 2084,
+ "parentId": "flow-shopper",
+ "x": 670,
+ "y": 110,
"width": 220,
"height": 190,
"collapsed": false,
"screen": "/products/:slug",
- "wireframe": ""
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-cart",
+ "type": "mockup",
+ "label": "Cart",
+ "description": "Cart with quantities and promo code; shipping is quoted at the next step.",
+ "parentId": "flow-shopper",
+ "x": 990,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/cart",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-checkout-address",
+ "type": "mockup",
+ "label": "Checkout — Address",
+ "description": "First checkout step; like every step it answers within {{CHECKOUT_P95_MS}} ms at p95.",
+ "parentId": "flow-shopper",
+ "x": 1310,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/checkout/address",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-checkout-shipping",
+ "type": "mockup",
+ "label": "Checkout — Shipping",
+ "description": "Live carrier quotes; still inside the checkout latency budget.",
+ "parentId": "flow-shopper",
+ "x": 30,
+ "y": 410,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/checkout/shipping",
+ "wireframe": ""
},
{
"id": "mockup-checkout",
"type": "mockup",
- "label": "Checkout",
+ "label": "Checkout — Payment",
"description": "Final checkout step. Card fields are the provider's hosted fields, so card data never touches platform services; every step must answer within {{CHECKOUT_P95_MS}} ms at p95.",
- "x": 320,
- "y": 2084,
+ "parentId": "flow-shopper",
+ "x": 350,
+ "y": 410,
"width": 220,
"height": 190,
"collapsed": false,
- "screen": "/checkout",
+ "screen": "/checkout/payment",
"wireframe": ""
},
+ {
+ "id": "mockup-payment-declined",
+ "type": "mockup",
+ "label": "Payment Declined",
+ "description": "Declined payment: nothing was captured, items stay reserved for the rest of the {{RESERVATION_MINUTES}}-minute hold, and retrying reuses the same idempotency key.",
+ "parentId": "flow-shopper",
+ "x": 670,
+ "y": 410,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/checkout/payment?declined",
+ "wireframe": ""
+ },
{
"id": "mockup-order-confirmation",
"type": "mockup",
"label": "Order Confirmation",
"description": "Shown once payment is captured. Refreshing or retrying never charges again — capture is keyed by the order id — and stock stays reserved for the order.",
- "x": 640,
- "y": 2084,
+ "parentId": "flow-shopper",
+ "x": 990,
+ "y": 410,
"width": 220,
"height": 190,
"collapsed": false,
"screen": "/orders/:id",
"wireframe": ""
+ },
+ {
+ "id": "mockup-order-tracking",
+ "type": "mockup",
+ "label": "Order Tracking",
+ "description": "Order status and carrier events after fulfilment books the shipment.",
+ "parentId": "flow-shopper",
+ "x": 1310,
+ "y": 410,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/account/orders/:id",
+ "wireframe": ""
+ },
+ {
+ "id": "flow-operations",
+ "type": "group",
+ "label": "Operations journey",
+ "description": "Back-office work: catalogue upkeep and order exceptions.",
+ "x": 0,
+ "y": 2794,
+ "width": 1240,
+ "height": 330,
+ "collapsed": false
+ },
+ {
+ "id": "mockup-bo-catalogue",
+ "type": "mockup",
+ "label": "Catalogue",
+ "description": "Products with price, stock and publication state.",
+ "parentId": "flow-operations",
+ "x": 30,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/admin/catalogue",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-bo-product-editor",
+ "type": "mockup",
+ "label": "Product Editor",
+ "description": "Edit price, stock and copy; changes reach the search index within {{INDEX_LAG_SECONDS}} s.",
+ "parentId": "flow-operations",
+ "x": 350,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/admin/catalogue/:sku",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-bo-orders",
+ "type": "mockup",
+ "label": "Orders",
+ "description": "Order list filtered to the exceptions staff act on.",
+ "parentId": "flow-operations",
+ "x": 670,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/admin/orders",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-bo-order-detail",
+ "type": "mockup",
+ "label": "Order Detail",
+ "description": "Payment attempts share one idempotency key, so staff can see nothing was captured twice; stock is released automatically when the hold expires.",
+ "parentId": "flow-operations",
+ "x": 990,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/admin/orders/:id",
+ "wireframe": ""
}
],
"relations": [
@@ -755,6 +934,32 @@
"targetId": "adr-hosted-payments",
"relationType": "traces-to"
},
+ {
+ "id": "mockup-home--storefront",
+ "sourceId": "mockup-home",
+ "targetId": "storefront",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-home--mockup-search-results",
+ "sourceId": "mockup-home",
+ "targetId": "mockup-search-results",
+ "relationType": "navigates-to",
+ "label": "Search"
+ },
+ {
+ "id": "mockup-search-results--storefront",
+ "sourceId": "mockup-search-results",
+ "targetId": "storefront",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-search-results--mockup-product-page",
+ "sourceId": "mockup-search-results",
+ "targetId": "mockup-product-page",
+ "relationType": "navigates-to",
+ "label": "Open product"
+ },
{
"id": "mockup-product-page--storefront",
"sourceId": "mockup-product-page",
@@ -762,12 +967,63 @@
"relationType": "presented-by"
},
{
- "id": "mockup-product-page--mockup-checkout",
+ "id": "mockup-product-page--mockup-cart",
"sourceId": "mockup-product-page",
- "targetId": "mockup-checkout",
+ "targetId": "mockup-cart",
+ "relationType": "navigates-to",
+ "label": "Add to cart"
+ },
+ {
+ "id": "mockup-cart--storefront",
+ "sourceId": "mockup-cart",
+ "targetId": "storefront",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-cart--mockup-checkout-address",
+ "sourceId": "mockup-cart",
+ "targetId": "mockup-checkout-address",
"relationType": "navigates-to",
"label": "Checkout"
},
+ {
+ "id": "mockup-checkout-address--req-checkout-latency",
+ "sourceId": "mockup-checkout-address",
+ "targetId": "req-checkout-latency",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-checkout-address--storefront",
+ "sourceId": "mockup-checkout-address",
+ "targetId": "storefront",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-checkout-address--mockup-checkout-shipping",
+ "sourceId": "mockup-checkout-address",
+ "targetId": "mockup-checkout-shipping",
+ "relationType": "navigates-to",
+ "label": "Continue"
+ },
+ {
+ "id": "mockup-checkout-shipping--req-checkout-latency",
+ "sourceId": "mockup-checkout-shipping",
+ "targetId": "req-checkout-latency",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-checkout-shipping--storefront",
+ "sourceId": "mockup-checkout-shipping",
+ "targetId": "storefront",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-checkout-shipping--mockup-checkout",
+ "sourceId": "mockup-checkout-shipping",
+ "targetId": "mockup-checkout",
+ "relationType": "navigates-to",
+ "label": "Continue"
+ },
{
"id": "mockup-checkout--req-checkout-latency",
"sourceId": "mockup-checkout",
@@ -780,6 +1036,12 @@
"targetId": "req-pci-scope",
"relationType": "illustrates"
},
+ {
+ "id": "mockup-checkout--req-stock-reservation",
+ "sourceId": "mockup-checkout",
+ "targetId": "req-stock-reservation",
+ "relationType": "illustrates"
+ },
{
"id": "mockup-checkout--storefront",
"sourceId": "mockup-checkout",
@@ -793,6 +1055,38 @@
"relationType": "navigates-to",
"label": "Pay"
},
+ {
+ "id": "mockup-checkout--mockup-payment-declined",
+ "sourceId": "mockup-checkout",
+ "targetId": "mockup-payment-declined",
+ "relationType": "navigates-to",
+ "label": "Card declined"
+ },
+ {
+ "id": "mockup-payment-declined--req-payment-once",
+ "sourceId": "mockup-payment-declined",
+ "targetId": "req-payment-once",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-payment-declined--req-stock-reservation",
+ "sourceId": "mockup-payment-declined",
+ "targetId": "req-stock-reservation",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-payment-declined--storefront",
+ "sourceId": "mockup-payment-declined",
+ "targetId": "storefront",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-payment-declined--mockup-checkout",
+ "sourceId": "mockup-payment-declined",
+ "targetId": "mockup-checkout",
+ "relationType": "navigates-to",
+ "label": "Try another card"
+ },
{
"id": "mockup-order-confirmation--req-payment-once",
"sourceId": "mockup-order-confirmation",
@@ -810,6 +1104,97 @@
"sourceId": "mockup-order-confirmation",
"targetId": "storefront",
"relationType": "presented-by"
+ },
+ {
+ "id": "mockup-order-confirmation--mockup-order-tracking",
+ "sourceId": "mockup-order-confirmation",
+ "targetId": "mockup-order-tracking",
+ "relationType": "navigates-to",
+ "label": "Track order"
+ },
+ {
+ "id": "mockup-order-confirmation--mockup-home",
+ "sourceId": "mockup-order-confirmation",
+ "targetId": "mockup-home",
+ "relationType": "navigates-to",
+ "label": "Continue shopping"
+ },
+ {
+ "id": "mockup-order-tracking--storefront",
+ "sourceId": "mockup-order-tracking",
+ "targetId": "storefront",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-bo-catalogue--backoffice",
+ "sourceId": "mockup-bo-catalogue",
+ "targetId": "backoffice",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-bo-catalogue--mockup-bo-product-editor",
+ "sourceId": "mockup-bo-catalogue",
+ "targetId": "mockup-bo-product-editor",
+ "relationType": "navigates-to",
+ "label": "Edit product"
+ },
+ {
+ "id": "mockup-bo-catalogue--mockup-bo-orders",
+ "sourceId": "mockup-bo-catalogue",
+ "targetId": "mockup-bo-orders",
+ "relationType": "navigates-to",
+ "label": "Orders"
+ },
+ {
+ "id": "mockup-bo-product-editor--backoffice",
+ "sourceId": "mockup-bo-product-editor",
+ "targetId": "backoffice",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-bo-product-editor--mockup-bo-catalogue",
+ "sourceId": "mockup-bo-product-editor",
+ "targetId": "mockup-bo-catalogue",
+ "relationType": "navigates-to",
+ "label": "Save changes"
+ },
+ {
+ "id": "mockup-bo-orders--backoffice",
+ "sourceId": "mockup-bo-orders",
+ "targetId": "backoffice",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-bo-orders--mockup-bo-order-detail",
+ "sourceId": "mockup-bo-orders",
+ "targetId": "mockup-bo-order-detail",
+ "relationType": "navigates-to",
+ "label": "Open order"
+ },
+ {
+ "id": "mockup-bo-order-detail--req-payment-once",
+ "sourceId": "mockup-bo-order-detail",
+ "targetId": "req-payment-once",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-bo-order-detail--req-stock-reservation",
+ "sourceId": "mockup-bo-order-detail",
+ "targetId": "req-stock-reservation",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-bo-order-detail--backoffice",
+ "sourceId": "mockup-bo-order-detail",
+ "targetId": "backoffice",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-bo-order-detail--mockup-bo-orders",
+ "sourceId": "mockup-bo-order-detail",
+ "targetId": "mockup-bo-orders",
+ "relationType": "navigates-to",
+ "label": "Back to orders"
}
]
}
diff --git a/hub/blueprint/bp-fintech-ledger-platform.radical b/hub/blueprint/bp-fintech-ledger-platform.radical
index a63b174..ea55cc0 100644
--- a/hub/blueprint/bp-fintech-ledger-platform.radical
+++ b/hub/blueprint/bp-fintech-ledger-platform.radical
@@ -393,44 +393,206 @@
"consequences": "A balance can update the instant a posting is accepted, before the bank settles — which means \"posted\" and \"reconciled\" must be visibly different states, or a customer or support agent will confuse a provisional balance with a settled one.",
"alternatives": "1. Bank/processor as source of truth, product waits for their confirmation — correct by construction, but the customer's balance now lags the actual action by the settlement delay. 2. Internal ledger as source of truth (chosen). 3. Dual-write both, resolve conflicts on read — no clear authority, hardest of the three to reason about when they disagree."
},
+ {
+ "id": "flow-customer",
+ "type": "group",
+ "label": "Customer journey",
+ "description": "Checking the account, reading the statement and disputing a transaction.",
+ "x": 0,
+ "y": 2084,
+ "width": 1560,
+ "height": 330,
+ "collapsed": false
+ },
+ {
+ "id": "mockup-accounts-overview",
+ "type": "mockup",
+ "label": "Accounts Overview",
+ "description": "Balances (projected from the journal) and recent activity.",
+ "parentId": "flow-customer",
+ "x": 30,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/accounts",
+ "wireframe": ""
+ },
{
"id": "mockup-statement",
"type": "mockup",
"label": "Account Statement",
"description": "Customer statement generated strictly from balances replayed through a fixed cut-off; each line links to a dispute.",
- "x": 0,
- "y": 2084,
+ "parentId": "flow-customer",
+ "x": 350,
+ "y": 110,
"width": 220,
"height": 190,
"collapsed": false,
"screen": "/accounts/:id/statement",
"wireframe": ""
},
+ {
+ "id": "mockup-transaction-detail",
+ "type": "mockup",
+ "label": "Transaction Detail",
+ "description": "One statement line with its settlement status and the journal entry behind it.",
+ "parentId": "flow-customer",
+ "x": 670,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/transactions/:id",
+ "wireframe": ""
+ },
{
"id": "mockup-dispute",
"type": "mockup",
"label": "Dispute a Transaction",
"description": "Opens an investigation the moment it is submitted; the customer gets a resolution or explanation within {{DISPUTE_INVESTIGATION_DAYS}} days.",
- "x": 320,
- "y": 2084,
+ "parentId": "flow-customer",
+ "x": 990,
+ "y": 110,
"width": 220,
"height": 190,
"collapsed": false,
"screen": "/disputes/new",
"wireframe": ""
},
+ {
+ "id": "mockup-dispute-status",
+ "type": "mockup",
+ "label": "Dispute Status",
+ "description": "Open dispute with the time left until the {{DISPUTE_INVESTIGATION_DAYS}}-day resolution deadline.",
+ "parentId": "flow-customer",
+ "x": 1310,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/disputes/:id",
+ "wireframe": ""
+ },
+ {
+ "id": "flow-finance-ops",
+ "type": "group",
+ "label": "Finance Ops journey",
+ "description": "Clearing reconciliation exceptions without ever editing the journal.",
+ "x": 0,
+ "y": 2494,
+ "width": 1560,
+ "height": 330,
+ "collapsed": false
+ },
{
"id": "mockup-reconciliation-exceptions",
"type": "mockup",
"label": "Reconciliation Exceptions",
"description": "Finance Ops queue of settlement events not matched within {{RECONCILIATION_LAG_MIN}} minutes. Corrections are compensating entries that must balance before they can be submitted — the journal itself is never edited.",
- "x": 640,
- "y": 2084,
+ "parentId": "flow-finance-ops",
+ "x": 30,
+ "y": 110,
"width": 220,
"height": 190,
"collapsed": false,
"screen": "/ops/exceptions",
- "wireframe": ""
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-exception-detail",
+ "type": "mockup",
+ "label": "Exception Detail",
+ "description": "Settlement event next to candidate journal entries; a manual match or a compensating entry clears it.",
+ "parentId": "flow-finance-ops",
+ "x": 350,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/ops/exceptions/:id",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-compensating-entry",
+ "type": "mockup",
+ "label": "Compensating Entry",
+ "description": "Double-entry form that cannot be submitted unless debits equal credits.",
+ "parentId": "flow-finance-ops",
+ "x": 670,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/ops/entries/new",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-entry-approval",
+ "type": "mockup",
+ "label": "Four-Eyes Approval",
+ "description": "A second person approves before the entry is posted; nobody approves their own entries.",
+ "parentId": "flow-finance-ops",
+ "x": 990,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/ops/approvals/:id",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-journal-entry",
+ "type": "mockup",
+ "label": "Journal Entry",
+ "description": "Posted entries are immutable; the only correction is another entry.",
+ "parentId": "flow-finance-ops",
+ "x": 1310,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/ops/journal/:id",
+ "wireframe": ""
+ },
+ {
+ "id": "flow-compliance",
+ "type": "group",
+ "label": "Compliance journey",
+ "description": "Auditing postings and signing off regulatory reporting.",
+ "x": 0,
+ "y": 2904,
+ "width": 600,
+ "height": 330,
+ "collapsed": false
+ },
+ {
+ "id": "mockup-audit-trail",
+ "type": "mockup",
+ "label": "Audit Trail",
+ "description": "Who did what to which entry, searchable and exportable.",
+ "parentId": "flow-compliance",
+ "x": 30,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/audit",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-regulatory-reports",
+ "type": "mockup",
+ "label": "Regulatory Reports",
+ "description": "Scheduled regulatory extracts built from the journal at a fixed cut-off, signed off by Compliance.",
+ "parentId": "flow-compliance",
+ "x": 350,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/reports",
+ "wireframe": ""
}
],
"relations": [
@@ -592,6 +754,19 @@
"targetId": "balance-projector",
"relationType": "constrains"
},
+ {
+ "id": "mockup-accounts-overview--statement-service",
+ "sourceId": "mockup-accounts-overview",
+ "targetId": "statement-service",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-accounts-overview--mockup-statement",
+ "sourceId": "mockup-accounts-overview",
+ "targetId": "mockup-statement",
+ "relationType": "navigates-to",
+ "label": "View statements"
+ },
{
"id": "mockup-statement--req-statement-accuracy-cutoff",
"sourceId": "mockup-statement",
@@ -605,8 +780,21 @@
"relationType": "presented-by"
},
{
- "id": "mockup-statement--mockup-dispute",
+ "id": "mockup-statement--mockup-transaction-detail",
"sourceId": "mockup-statement",
+ "targetId": "mockup-transaction-detail",
+ "relationType": "navigates-to",
+ "label": "Open transaction"
+ },
+ {
+ "id": "mockup-transaction-detail--statement-service",
+ "sourceId": "mockup-transaction-detail",
+ "targetId": "statement-service",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-transaction-detail--mockup-dispute",
+ "sourceId": "mockup-transaction-detail",
"targetId": "mockup-dispute",
"relationType": "navigates-to",
"label": "Dispute"
@@ -623,6 +811,25 @@
"targetId": "statement-service",
"relationType": "presented-by"
},
+ {
+ "id": "mockup-dispute--mockup-dispute-status",
+ "sourceId": "mockup-dispute",
+ "targetId": "mockup-dispute-status",
+ "relationType": "navigates-to",
+ "label": "Submit dispute"
+ },
+ {
+ "id": "mockup-dispute-status--req-dispute-investigation-sla",
+ "sourceId": "mockup-dispute-status",
+ "targetId": "req-dispute-investigation-sla",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-dispute-status--statement-service",
+ "sourceId": "mockup-dispute-status",
+ "targetId": "statement-service",
+ "relationType": "presented-by"
+ },
{
"id": "mockup-reconciliation-exceptions--req-reconciliation-freshness",
"sourceId": "mockup-reconciliation-exceptions",
@@ -640,6 +847,121 @@
"sourceId": "mockup-reconciliation-exceptions",
"targetId": "finance-ops-console",
"relationType": "presented-by"
+ },
+ {
+ "id": "mockup-reconciliation-exceptions--mockup-exception-detail",
+ "sourceId": "mockup-reconciliation-exceptions",
+ "targetId": "mockup-exception-detail",
+ "relationType": "navigates-to",
+ "label": "Open exception"
+ },
+ {
+ "id": "mockup-exception-detail--req-reconciliation-freshness",
+ "sourceId": "mockup-exception-detail",
+ "targetId": "req-reconciliation-freshness",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-exception-detail--finance-ops-console",
+ "sourceId": "mockup-exception-detail",
+ "targetId": "finance-ops-console",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-exception-detail--mockup-compensating-entry",
+ "sourceId": "mockup-exception-detail",
+ "targetId": "mockup-compensating-entry",
+ "relationType": "navigates-to",
+ "label": "Compensating entry"
+ },
+ {
+ "id": "mockup-exception-detail--mockup-reconciliation-exceptions",
+ "sourceId": "mockup-exception-detail",
+ "targetId": "mockup-reconciliation-exceptions",
+ "relationType": "navigates-to",
+ "label": "Confirm match"
+ },
+ {
+ "id": "mockup-compensating-entry--req-balanced-entry-enforcement",
+ "sourceId": "mockup-compensating-entry",
+ "targetId": "req-balanced-entry-enforcement",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-compensating-entry--finance-ops-console",
+ "sourceId": "mockup-compensating-entry",
+ "targetId": "finance-ops-console",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-compensating-entry--mockup-entry-approval",
+ "sourceId": "mockup-compensating-entry",
+ "targetId": "mockup-entry-approval",
+ "relationType": "navigates-to",
+ "label": "Submit for approval"
+ },
+ {
+ "id": "mockup-entry-approval--req-balanced-entry-enforcement",
+ "sourceId": "mockup-entry-approval",
+ "targetId": "req-balanced-entry-enforcement",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-entry-approval--finance-ops-console",
+ "sourceId": "mockup-entry-approval",
+ "targetId": "finance-ops-console",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-entry-approval--mockup-journal-entry",
+ "sourceId": "mockup-entry-approval",
+ "targetId": "mockup-journal-entry",
+ "relationType": "navigates-to",
+ "label": "Approve & post"
+ },
+ {
+ "id": "mockup-journal-entry--req-balanced-entry-enforcement",
+ "sourceId": "mockup-journal-entry",
+ "targetId": "req-balanced-entry-enforcement",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-journal-entry--finance-ops-console",
+ "sourceId": "mockup-journal-entry",
+ "targetId": "finance-ops-console",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-journal-entry--mockup-reconciliation-exceptions",
+ "sourceId": "mockup-journal-entry",
+ "targetId": "mockup-reconciliation-exceptions",
+ "relationType": "navigates-to",
+ "label": "Exception cleared"
+ },
+ {
+ "id": "mockup-audit-trail--finance-ops-console",
+ "sourceId": "mockup-audit-trail",
+ "targetId": "finance-ops-console",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-audit-trail--mockup-regulatory-reports",
+ "sourceId": "mockup-audit-trail",
+ "targetId": "mockup-regulatory-reports",
+ "relationType": "navigates-to",
+ "label": "Reports"
+ },
+ {
+ "id": "mockup-regulatory-reports--req-statement-accuracy-cutoff",
+ "sourceId": "mockup-regulatory-reports",
+ "targetId": "req-statement-accuracy-cutoff",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-regulatory-reports--finance-ops-console",
+ "sourceId": "mockup-regulatory-reports",
+ "targetId": "finance-ops-console",
+ "relationType": "presented-by"
}
],
"sequences": [
diff --git a/hub/blueprint/bp-marketplace-platform.radical b/hub/blueprint/bp-marketplace-platform.radical
index abe62fc..70963be 100644
--- a/hub/blueprint/bp-marketplace-platform.radical
+++ b/hub/blueprint/bp-marketplace-platform.radical
@@ -415,26 +415,187 @@
"consequences": "Buyers are protected as fast as the platform can detect a problem; the investigation SLA becomes a trust-critical metric in its own right, since it's now the only thing standing between a wrongly-flagged seller and lost income.",
"alternatives": "1. Investigate first, act after — protects sellers from false positives, but a genuine bad actor keeps selling and collecting funds during the investigation window. 2. Suspend first, investigate after (chosen). 3. Community reporting only, no proactive detection — cheapest, but slowest and worst for buyer trust. 4. Permanent ban on first signal, no reinstatement path — fastest, but the harshest possible cost of a false positive."
},
+ {
+ "id": "flow-buyer",
+ "type": "group",
+ "label": "Buyer journey",
+ "description": "Finding an item, paying with buyer protection and disputing it.",
+ "x": 0,
+ "y": 2084,
+ "width": 1560,
+ "height": 630,
+ "collapsed": false
+ },
+ {
+ "id": "mockup-mp-search",
+ "type": "mockup",
+ "label": "Search",
+ "description": "Only approved, live listings appear in search.",
+ "parentId": "flow-buyer",
+ "x": 30,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/search",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-mp-listing",
+ "type": "mockup",
+ "label": "Listing",
+ "description": "Listing page with seller reputation and buyer protection terms.",
+ "parentId": "flow-buyer",
+ "x": 350,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/listings/:id",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-mp-checkout",
+ "type": "mockup",
+ "label": "Checkout",
+ "description": "Payment goes to the payments facilitator, which holds the funds; the platform never custodies them.",
+ "parentId": "flow-buyer",
+ "x": 670,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/checkout",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-order-dispute",
+ "type": "mockup",
+ "label": "Order & Buyer Protection",
+ "description": "Buyer view of an order: funds stay held for {{DISPUTE_WINDOW_DAYS}} days after delivery, and opening a dispute freezes the payout.",
+ "parentId": "flow-buyer",
+ "x": 990,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/orders/:id",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-mp-open-dispute",
+ "type": "mockup",
+ "label": "Open a Dispute",
+ "description": "Opening a dispute inside the {{DISPUTE_WINDOW_DAYS}}-day window freezes the seller payout.",
+ "parentId": "flow-buyer",
+ "x": 1310,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/orders/:id/dispute",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-mp-dispute-status",
+ "type": "mockup",
+ "label": "Dispute Status",
+ "description": "Conversation and status while Trust & Safety reviews.",
+ "parentId": "flow-buyer",
+ "x": 30,
+ "y": 410,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/disputes/:id",
+ "wireframe": ""
+ },
+ {
+ "id": "flow-seller",
+ "type": "group",
+ "label": "Seller journey",
+ "description": "Listing items, getting them approved and getting paid.",
+ "x": 0,
+ "y": 2794,
+ "width": 1240,
+ "height": 330,
+ "collapsed": false
+ },
+ {
+ "id": "mockup-seller-dashboard",
+ "type": "mockup",
+ "label": "Seller Dashboard",
+ "description": "What needs the seller's attention today.",
+ "parentId": "flow-seller",
+ "x": 30,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/sell",
+ "wireframe": ""
+ },
{
"id": "mockup-new-listing",
"type": "mockup",
"label": "New Listing",
"description": "Seller creates a listing; it goes into review and only appears in search once approved (within {{LISTING_REVIEW_SLA_HOURS}} h).",
- "x": 0,
- "y": 2084,
+ "parentId": "flow-seller",
+ "x": 350,
+ "y": 110,
"width": 220,
"height": 190,
"collapsed": false,
"screen": "/sell/listings/new",
"wireframe": ""
},
+ {
+ "id": "mockup-my-listings",
+ "type": "mockup",
+ "label": "My Listings",
+ "description": "Listing states from the moderation workflow — in review, live, rejected with a reason.",
+ "parentId": "flow-seller",
+ "x": 670,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/sell/listings",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-seller-payouts",
+ "type": "mockup",
+ "label": "Payouts",
+ "description": "Per-order split: {{COMMISSION_PCT}}% commission deducted, payouts held for the protection window and frozen while a dispute is open.",
+ "parentId": "flow-seller",
+ "x": 990,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/sell/payouts",
+ "wireframe": ""
+ },
+ {
+ "id": "flow-trust-safety",
+ "type": "group",
+ "label": "Trust & Safety journey",
+ "description": "Moderating listings, investigating sellers and resolving disputes.",
+ "x": 0,
+ "y": 3204,
+ "width": 920,
+ "height": 330,
+ "collapsed": false
+ },
{
"id": "mockup-listing-review-queue",
"type": "mockup",
"label": "Listing Review Queue",
"description": "Trust & Safety queue sorted by time left on the review SLA. Rejecting a listing or suspending a seller removes it from search within {{SUPPRESSION_MINUTES}} minutes.",
- "x": 320,
- "y": 2084,
+ "parentId": "flow-trust-safety",
+ "x": 30,
+ "y": 110,
"width": 220,
"height": 190,
"collapsed": false,
@@ -442,17 +603,32 @@
"wireframe": ""
},
{
- "id": "mockup-order-dispute",
+ "id": "mockup-seller-investigation",
"type": "mockup",
- "label": "Order & Buyer Protection",
- "description": "Buyer view of an order: funds stay held for {{DISPUTE_WINDOW_DAYS}} days after delivery, and opening a dispute freezes the payout.",
- "x": 640,
- "y": 2084,
+ "label": "Seller Investigation",
+ "description": "Suspending a seller removes every listing from search within {{SUPPRESSION_MINUTES}} minutes.",
+ "parentId": "flow-trust-safety",
+ "x": 350,
+ "y": 110,
"width": 220,
"height": 190,
"collapsed": false,
- "screen": "/orders/:id",
- "wireframe": ""
+ "screen": "/trust/sellers/:id",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-ts-dispute-case",
+ "type": "mockup",
+ "label": "Dispute Case",
+ "description": "Evidence from both sides; the payout stays frozen until a resolution is chosen.",
+ "parentId": "flow-trust-safety",
+ "x": 670,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/trust/disputes/:id",
+ "wireframe": ""
}
],
"relations": [
@@ -614,6 +790,121 @@
"targetId": "trust-safety-console",
"relationType": "constrains"
},
+ {
+ "id": "mockup-mp-search--order-checkout-service",
+ "sourceId": "mockup-mp-search",
+ "targetId": "order-checkout-service",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-mp-search--mockup-mp-listing",
+ "sourceId": "mockup-mp-search",
+ "targetId": "mockup-mp-listing",
+ "relationType": "navigates-to",
+ "label": "Open listing"
+ },
+ {
+ "id": "mockup-mp-listing--order-checkout-service",
+ "sourceId": "mockup-mp-listing",
+ "targetId": "order-checkout-service",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-mp-listing--mockup-mp-checkout",
+ "sourceId": "mockup-mp-listing",
+ "targetId": "mockup-mp-checkout",
+ "relationType": "navigates-to",
+ "label": "Buy now"
+ },
+ {
+ "id": "mockup-mp-checkout--req-dispute-window-hold",
+ "sourceId": "mockup-mp-checkout",
+ "targetId": "req-dispute-window-hold",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-mp-checkout--order-checkout-service",
+ "sourceId": "mockup-mp-checkout",
+ "targetId": "order-checkout-service",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-mp-checkout--mockup-order-dispute",
+ "sourceId": "mockup-mp-checkout",
+ "targetId": "mockup-order-dispute",
+ "relationType": "navigates-to",
+ "label": "Pay"
+ },
+ {
+ "id": "mockup-order-dispute--req-dispute-window-hold",
+ "sourceId": "mockup-order-dispute",
+ "targetId": "req-dispute-window-hold",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-order-dispute--order-checkout-service",
+ "sourceId": "mockup-order-dispute",
+ "targetId": "order-checkout-service",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-order-dispute--mockup-mp-open-dispute",
+ "sourceId": "mockup-order-dispute",
+ "targetId": "mockup-mp-open-dispute",
+ "relationType": "navigates-to",
+ "label": "Open a dispute"
+ },
+ {
+ "id": "mockup-mp-open-dispute--req-dispute-window-hold",
+ "sourceId": "mockup-mp-open-dispute",
+ "targetId": "req-dispute-window-hold",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-mp-open-dispute--order-checkout-service",
+ "sourceId": "mockup-mp-open-dispute",
+ "targetId": "order-checkout-service",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-mp-open-dispute--mockup-mp-dispute-status",
+ "sourceId": "mockup-mp-open-dispute",
+ "targetId": "mockup-mp-dispute-status",
+ "relationType": "navigates-to",
+ "label": "Open dispute"
+ },
+ {
+ "id": "mockup-mp-dispute-status--req-dispute-window-hold",
+ "sourceId": "mockup-mp-dispute-status",
+ "targetId": "req-dispute-window-hold",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-mp-dispute-status--order-checkout-service",
+ "sourceId": "mockup-mp-dispute-status",
+ "targetId": "order-checkout-service",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-seller-dashboard--listings-service",
+ "sourceId": "mockup-seller-dashboard",
+ "targetId": "listings-service",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-seller-dashboard--mockup-new-listing",
+ "sourceId": "mockup-seller-dashboard",
+ "targetId": "mockup-new-listing",
+ "relationType": "navigates-to",
+ "label": "New listing"
+ },
+ {
+ "id": "mockup-seller-dashboard--mockup-seller-payouts",
+ "sourceId": "mockup-seller-dashboard",
+ "targetId": "mockup-seller-payouts",
+ "relationType": "navigates-to",
+ "label": "Payouts"
+ },
{
"id": "mockup-new-listing--req-listing-moderation",
"sourceId": "mockup-new-listing",
@@ -626,6 +917,50 @@
"targetId": "listings-service",
"relationType": "presented-by"
},
+ {
+ "id": "mockup-new-listing--mockup-my-listings",
+ "sourceId": "mockup-new-listing",
+ "targetId": "mockup-my-listings",
+ "relationType": "navigates-to",
+ "label": "Submit for review"
+ },
+ {
+ "id": "mockup-my-listings--req-listing-moderation",
+ "sourceId": "mockup-my-listings",
+ "targetId": "req-listing-moderation",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-my-listings--listings-service",
+ "sourceId": "mockup-my-listings",
+ "targetId": "listings-service",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-my-listings--mockup-new-listing",
+ "sourceId": "mockup-my-listings",
+ "targetId": "mockup-new-listing",
+ "relationType": "navigates-to",
+ "label": "Edit & resubmit"
+ },
+ {
+ "id": "mockup-seller-payouts--req-commission-accuracy",
+ "sourceId": "mockup-seller-payouts",
+ "targetId": "req-commission-accuracy",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-seller-payouts--req-dispute-window-hold",
+ "sourceId": "mockup-seller-payouts",
+ "targetId": "req-dispute-window-hold",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-seller-payouts--payout-worker",
+ "sourceId": "mockup-seller-payouts",
+ "targetId": "payout-worker",
+ "relationType": "presented-by"
+ },
{
"id": "mockup-listing-review-queue--req-listing-moderation",
"sourceId": "mockup-listing-review-queue",
@@ -645,15 +980,48 @@
"relationType": "presented-by"
},
{
- "id": "mockup-order-dispute--req-dispute-window-hold",
- "sourceId": "mockup-order-dispute",
+ "id": "mockup-listing-review-queue--mockup-seller-investigation",
+ "sourceId": "mockup-listing-review-queue",
+ "targetId": "mockup-seller-investigation",
+ "relationType": "navigates-to",
+ "label": "Suspend"
+ },
+ {
+ "id": "mockup-seller-investigation--req-delisting-suppression",
+ "sourceId": "mockup-seller-investigation",
+ "targetId": "req-delisting-suppression",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-seller-investigation--trust-safety-console",
+ "sourceId": "mockup-seller-investigation",
+ "targetId": "trust-safety-console",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-seller-investigation--mockup-ts-dispute-case",
+ "sourceId": "mockup-seller-investigation",
+ "targetId": "mockup-ts-dispute-case",
+ "relationType": "navigates-to",
+ "label": "Disputes"
+ },
+ {
+ "id": "mockup-seller-investigation--mockup-listing-review-queue",
+ "sourceId": "mockup-seller-investigation",
+ "targetId": "mockup-listing-review-queue",
+ "relationType": "navigates-to",
+ "label": "Suspend seller"
+ },
+ {
+ "id": "mockup-ts-dispute-case--req-dispute-window-hold",
+ "sourceId": "mockup-ts-dispute-case",
"targetId": "req-dispute-window-hold",
"relationType": "illustrates"
},
{
- "id": "mockup-order-dispute--order-checkout-service",
- "sourceId": "mockup-order-dispute",
- "targetId": "order-checkout-service",
+ "id": "mockup-ts-dispute-case--trust-safety-console",
+ "sourceId": "mockup-ts-dispute-case",
+ "targetId": "trust-safety-console",
"relationType": "presented-by"
}
],
diff --git a/hub/blueprint/bp-realtime-collaboration.radical b/hub/blueprint/bp-realtime-collaboration.radical
index 571ab96..8acd379 100644
--- a/hub/blueprint/bp-realtime-collaboration.radical
+++ b/hub/blueprint/bp-realtime-collaboration.radical
@@ -358,13 +358,39 @@
"consequences": "Gateway instances scale and restart freely with no handoff logic, but every operation takes an extra hop through the bus, adding a small amount of latency to the common case.",
"alternatives": "1. Sticky sessions routing all of a document's clients to one gateway instance — lower latency per operation, but that instance becomes a single point of failure and a hot-spot for popular documents. 2. Shared broadcast bus (chosen). 3. Peer-to-peer mesh between clients — no server bottleneck, but NAT traversal and offline persistence become much harder to get right."
},
+ {
+ "id": "flow-collaborator",
+ "type": "group",
+ "label": "Collaborator journey",
+ "description": "Editing together, offline and back, and going back in time.",
+ "x": 0,
+ "y": 2084,
+ "width": 1560,
+ "height": 330,
+ "collapsed": false
+ },
+ {
+ "id": "mockup-doc-list",
+ "type": "mockup",
+ "label": "Documents",
+ "description": "Workspace documents with who is editing them right now.",
+ "parentId": "flow-collaborator",
+ "x": 30,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/docs",
+ "wireframe": ""
+ },
{
"id": "mockup-document-editor",
"type": "mockup",
"label": "Document Editor",
"description": "Everyone edits a local replica: changes apply instantly, keep working offline (up to {{OFFLINE_GRACE_HOURS}} h) and merge on reconnect; presence shows up to {{MAX_DOC_COLLABORATORS}} collaborators.",
- "x": 0,
- "y": 2084,
+ "parentId": "flow-collaborator",
+ "x": 350,
+ "y": 110,
"width": 220,
"height": 190,
"collapsed": false,
@@ -372,30 +398,113 @@
"wireframe": ""
},
{
- "id": "mockup-share-dialog",
+ "id": "mockup-reconnect-merge",
"type": "mockup",
- "label": "Share & Access",
- "description": "Workspace admins manage who can open the document; removing someone disconnects their live session instead of waiting for it to expire.",
- "x": 320,
- "y": 2084,
+ "label": "Back Online — Merged",
+ "description": "On reconnect the offline operation log is replayed and merged automatically — no conflict dialogs, no lost edits.",
+ "parentId": "flow-collaborator",
+ "x": 670,
+ "y": 110,
"width": 220,
"height": 190,
"collapsed": false,
- "screen": "/d/:docId/share",
- "wireframe": ""
+ "screen": "/d/:docId",
+ "wireframe": ""
},
{
"id": "mockup-version-history",
"type": "mockup",
"label": "Version History",
"description": "Browse and restore any earlier state of the document, reconstructed from snapshots plus the operation log.",
- "x": 640,
- "y": 2084,
+ "parentId": "flow-collaborator",
+ "x": 990,
+ "y": 110,
"width": 220,
"height": 190,
"collapsed": false,
"screen": "/d/:docId/history",
"wireframe": ""
+ },
+ {
+ "id": "mockup-restore-version",
+ "type": "mockup",
+ "label": "Restore Version",
+ "description": "Restoring an older state is itself a new version, reconstructed from snapshot plus operation log.",
+ "parentId": "flow-collaborator",
+ "x": 1310,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/d/:docId/history/:version",
+ "wireframe": ""
+ },
+ {
+ "id": "flow-workspace-admin",
+ "type": "group",
+ "label": "Workspace admin journey",
+ "description": "Sharing documents and removing people who leave.",
+ "x": 0,
+ "y": 2494,
+ "width": 1240,
+ "height": 330,
+ "collapsed": false
+ },
+ {
+ "id": "mockup-share-dialog",
+ "type": "mockup",
+ "label": "Share & Access",
+ "description": "Workspace admins manage who can open the document; removing someone disconnects their live session instead of waiting for it to expire.",
+ "parentId": "flow-workspace-admin",
+ "x": 30,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/d/:docId/share",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-workspace-members",
+ "type": "mockup",
+ "label": "Workspace Members",
+ "description": "Everyone with access to the workspace and their role.",
+ "parentId": "flow-workspace-admin",
+ "x": 350,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/settings/members",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-remove-member",
+ "type": "mockup",
+ "label": "Remove Member",
+ "description": "Removal disconnects live sessions immediately instead of waiting for tokens to expire.",
+ "parentId": "flow-workspace-admin",
+ "x": 670,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/settings/members/:id/remove",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-access-revoked",
+ "type": "mockup",
+ "label": "Access Removed",
+ "description": "What the removed collaborator sees: the session is closed and further operations are rejected.",
+ "parentId": "flow-workspace-admin",
+ "x": 990,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/d/:docId",
+ "wireframe": ""
}
],
"relations": [
@@ -536,6 +645,19 @@
"targetId": "adr-stateless-gateway-fleet",
"relationType": "traces-to"
},
+ {
+ "id": "mockup-doc-list--editor-client",
+ "sourceId": "mockup-doc-list",
+ "targetId": "editor-client",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-doc-list--mockup-document-editor",
+ "sourceId": "mockup-doc-list",
+ "targetId": "mockup-document-editor",
+ "relationType": "navigates-to",
+ "label": "Open"
+ },
{
"id": "mockup-document-editor--req-offline-merge",
"sourceId": "mockup-document-editor",
@@ -554,6 +676,13 @@
"targetId": "editor-client",
"relationType": "presented-by"
},
+ {
+ "id": "mockup-document-editor--mockup-reconnect-merge",
+ "sourceId": "mockup-document-editor",
+ "targetId": "mockup-reconnect-merge",
+ "relationType": "navigates-to",
+ "label": "Reconnect"
+ },
{
"id": "mockup-document-editor--mockup-share-dialog",
"sourceId": "mockup-document-editor",
@@ -569,14 +698,14 @@
"label": "History"
},
{
- "id": "mockup-share-dialog--req-access-revocation",
- "sourceId": "mockup-share-dialog",
- "targetId": "req-access-revocation",
+ "id": "mockup-reconnect-merge--req-offline-merge",
+ "sourceId": "mockup-reconnect-merge",
+ "targetId": "req-offline-merge",
"relationType": "illustrates"
},
{
- "id": "mockup-share-dialog--editor-client",
- "sourceId": "mockup-share-dialog",
+ "id": "mockup-reconnect-merge--editor-client",
+ "sourceId": "mockup-reconnect-merge",
"targetId": "editor-client",
"relationType": "presented-by"
},
@@ -591,6 +720,108 @@
"sourceId": "mockup-version-history",
"targetId": "editor-client",
"relationType": "presented-by"
+ },
+ {
+ "id": "mockup-version-history--mockup-restore-version",
+ "sourceId": "mockup-version-history",
+ "targetId": "mockup-restore-version",
+ "relationType": "navigates-to",
+ "label": "Restore this version"
+ },
+ {
+ "id": "mockup-restore-version--req-snapshot-recovery",
+ "sourceId": "mockup-restore-version",
+ "targetId": "req-snapshot-recovery",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-restore-version--editor-client",
+ "sourceId": "mockup-restore-version",
+ "targetId": "editor-client",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-restore-version--mockup-document-editor",
+ "sourceId": "mockup-restore-version",
+ "targetId": "mockup-document-editor",
+ "relationType": "navigates-to",
+ "label": "Restore"
+ },
+ {
+ "id": "mockup-share-dialog--req-access-revocation",
+ "sourceId": "mockup-share-dialog",
+ "targetId": "req-access-revocation",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-share-dialog--editor-client",
+ "sourceId": "mockup-share-dialog",
+ "targetId": "editor-client",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-share-dialog--mockup-workspace-members",
+ "sourceId": "mockup-share-dialog",
+ "targetId": "mockup-workspace-members",
+ "relationType": "navigates-to",
+ "label": "Manage members"
+ },
+ {
+ "id": "mockup-workspace-members--req-access-revocation",
+ "sourceId": "mockup-workspace-members",
+ "targetId": "req-access-revocation",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-workspace-members--editor-client",
+ "sourceId": "mockup-workspace-members",
+ "targetId": "editor-client",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-workspace-members--mockup-remove-member",
+ "sourceId": "mockup-workspace-members",
+ "targetId": "mockup-remove-member",
+ "relationType": "navigates-to",
+ "label": "Remove"
+ },
+ {
+ "id": "mockup-remove-member--req-access-revocation",
+ "sourceId": "mockup-remove-member",
+ "targetId": "req-access-revocation",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-remove-member--editor-client",
+ "sourceId": "mockup-remove-member",
+ "targetId": "editor-client",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-remove-member--mockup-access-revoked",
+ "sourceId": "mockup-remove-member",
+ "targetId": "mockup-access-revoked",
+ "relationType": "navigates-to",
+ "label": "Remove"
+ },
+ {
+ "id": "mockup-access-revoked--req-access-revocation",
+ "sourceId": "mockup-access-revoked",
+ "targetId": "req-access-revocation",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-access-revoked--editor-client",
+ "sourceId": "mockup-access-revoked",
+ "targetId": "editor-client",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-access-revoked--mockup-doc-list",
+ "sourceId": "mockup-access-revoked",
+ "targetId": "mockup-doc-list",
+ "relationType": "navigates-to",
+ "label": "Back to workspace"
}
],
"sequences": [
diff --git a/hub/blueprint/bp-saas-b2b-platform.radical b/hub/blueprint/bp-saas-b2b-platform.radical
index 6f84d19..6396024 100644
--- a/hub/blueprint/bp-saas-b2b-platform.radical
+++ b/hub/blueprint/bp-saas-b2b-platform.radical
@@ -441,44 +441,217 @@
"consequences": "A tenant revoking a user at their IdP takes effect on the token's next short expiry, not instantly, which is an accepted trade-off; token issuance adds one extra hop to first login.",
"alternatives": "Storing IdP session cookies directly was rejected — it would tie the platform's session lifetime to each IdP's own cookie semantics and leak IdP-specific state into the app tier."
},
+ {
+ "id": "flow-onboarding",
+ "type": "group",
+ "label": "Onboarding journey",
+ "description": "From sign-up to a working, provisioned workspace.",
+ "x": 0,
+ "y": 2324,
+ "width": 920,
+ "height": 330,
+ "collapsed": false
+ },
{
"id": "mockup-signup",
"type": "mockup",
"label": "Sign Up",
"description": "Self-serve sign-up; the new tenant gets an isolated, ready-to-use workspace within {{PROVISION_MINUTES}} minutes.",
- "x": 0,
- "y": 2324,
+ "parentId": "flow-onboarding",
+ "x": 30,
+ "y": 110,
"width": 220,
"height": 190,
"collapsed": false,
"screen": "/signup",
"wireframe": ""
},
+ {
+ "id": "mockup-provisioning",
+ "type": "mockup",
+ "label": "Provisioning",
+ "description": "Provisioning progress; an isolated workspace is ready within {{PROVISION_MINUTES}} minutes.",
+ "parentId": "flow-onboarding",
+ "x": 350,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/setup",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-invite-team",
+ "type": "mockup",
+ "label": "Invite Your Team",
+ "description": "Last onboarding step; invites land in the new tenant only.",
+ "parentId": "flow-onboarding",
+ "x": 670,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/setup/invite",
+ "wireframe": ""
+ },
+ {
+ "id": "flow-tenant-user",
+ "type": "group",
+ "label": "Tenant user journey",
+ "description": "Daily work inside the tenant, and what happens when billing lapses.",
+ "x": 0,
+ "y": 2734,
+ "width": 920,
+ "height": 330,
+ "collapsed": false
+ },
{
"id": "mockup-workspace-dashboard",
"type": "mockup",
"label": "Workspace Dashboard",
- "description": "Tenant home. When a payment fails the workspace turns read-only — data stays visible, writes are blocked — until billing is fixed.",
- "x": 320,
- "y": 2324,
+ "description": "Tenant home; every request carries the resolved tenant context.",
+ "parentId": "flow-tenant-user",
+ "x": 30,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-project",
+ "type": "mockup",
+ "label": "Project",
+ "description": "Project board; all data is scoped by tenant id — nothing from other tenants is reachable.",
+ "parentId": "flow-tenant-user",
+ "x": 350,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/projects/:id",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-read-only",
+ "type": "mockup",
+ "label": "Read-Only Workspace",
+ "description": "After a failed payment the workspace stays readable but writes are blocked until billing is fixed.",
+ "parentId": "flow-tenant-user",
+ "x": 670,
+ "y": 110,
"width": 220,
"height": 190,
"collapsed": false,
"screen": "/",
"wireframe": ""
},
+ {
+ "id": "flow-tenant-admin",
+ "type": "group",
+ "label": "Tenant admin journey",
+ "description": "Billing, SSO and user management for one organisation.",
+ "x": 0,
+ "y": 3144,
+ "width": 1240,
+ "height": 330,
+ "collapsed": false
+ },
{
"id": "mockup-billing-usage",
"type": "mockup",
"label": "Billing & Usage",
"description": "Tenant admin view of plan, metered usage and invoices; usage figures are recorded exactly once and reconciled against source logs.",
- "x": 640,
- "y": 2324,
+ "parentId": "flow-tenant-admin",
+ "x": 30,
+ "y": 110,
"width": 220,
"height": 190,
"collapsed": false,
"screen": "/settings/billing",
- "wireframe": ""
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-update-payment",
+ "type": "mockup",
+ "label": "Update Payment Method",
+ "description": "New card; the failed invoice is retried immediately and write access comes back on success.",
+ "parentId": "flow-tenant-admin",
+ "x": 350,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/settings/billing/payment",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-sso-settings",
+ "type": "mockup",
+ "label": "Single Sign-On",
+ "description": "SAML/OIDC federation with the tenant's own identity provider.",
+ "parentId": "flow-tenant-admin",
+ "x": 670,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/settings/sso",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-users-roles",
+ "type": "mockup",
+ "label": "Users & Roles",
+ "description": "Seats, roles and how each user signs in.",
+ "parentId": "flow-tenant-admin",
+ "x": 990,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/settings/users",
+ "wireframe": ""
+ },
+ {
+ "id": "flow-platform-ops",
+ "type": "group",
+ "label": "Platform ops journey",
+ "description": "Watching tenant health across the pooled tier.",
+ "x": 0,
+ "y": 3554,
+ "width": 600,
+ "height": 330,
+ "collapsed": false
+ },
+ {
+ "id": "mockup-tenant-list",
+ "type": "mockup",
+ "label": "Tenants",
+ "description": "All tenants with plan, tier and share of pooled load.",
+ "parentId": "flow-platform-ops",
+ "x": 30,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/admin/tenants",
+ "wireframe": ""
+ },
+ {
+ "id": "mockup-tenant-detail",
+ "type": "mockup",
+ "label": "Tenant Detail",
+ "description": "Per-tenant health against the {{MAX_TENANT_SHARE_PCT}}% noisy-neighbour limit; impersonation is audited.",
+ "parentId": "flow-platform-ops",
+ "x": 350,
+ "y": 110,
+ "width": 220,
+ "height": 190,
+ "collapsed": false,
+ "screen": "/admin/tenants/:id",
+ "wireframe": ""
}
],
"relations": [
@@ -614,18 +787,44 @@
"relationType": "presented-by"
},
{
- "id": "mockup-signup--mockup-workspace-dashboard",
+ "id": "mockup-signup--mockup-provisioning",
"sourceId": "mockup-signup",
- "targetId": "mockup-workspace-dashboard",
+ "targetId": "mockup-provisioning",
"relationType": "navigates-to",
"label": "Create workspace"
},
{
- "id": "mockup-workspace-dashboard--req-subscription-enforcement",
- "sourceId": "mockup-workspace-dashboard",
- "targetId": "req-subscription-enforcement",
+ "id": "mockup-provisioning--req-tenant-provisioning-time",
+ "sourceId": "mockup-provisioning",
+ "targetId": "req-tenant-provisioning-time",
"relationType": "illustrates"
},
+ {
+ "id": "mockup-provisioning--webapp",
+ "sourceId": "mockup-provisioning",
+ "targetId": "webapp",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-provisioning--mockup-invite-team",
+ "sourceId": "mockup-provisioning",
+ "targetId": "mockup-invite-team",
+ "relationType": "navigates-to",
+ "label": "Ready"
+ },
+ {
+ "id": "mockup-invite-team--webapp",
+ "sourceId": "mockup-invite-team",
+ "targetId": "webapp",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-invite-team--mockup-workspace-dashboard",
+ "sourceId": "mockup-invite-team",
+ "targetId": "mockup-workspace-dashboard",
+ "relationType": "navigates-to",
+ "label": "Send invites"
+ },
{
"id": "mockup-workspace-dashboard--webapp",
"sourceId": "mockup-workspace-dashboard",
@@ -633,8 +832,46 @@
"relationType": "presented-by"
},
{
- "id": "mockup-workspace-dashboard--mockup-billing-usage",
+ "id": "mockup-workspace-dashboard--mockup-project",
+ "sourceId": "mockup-workspace-dashboard",
+ "targetId": "mockup-project",
+ "relationType": "navigates-to",
+ "label": "Open project"
+ },
+ {
+ "id": "mockup-workspace-dashboard--mockup-read-only",
"sourceId": "mockup-workspace-dashboard",
+ "targetId": "mockup-read-only",
+ "relationType": "navigates-to",
+ "label": "Payment fails"
+ },
+ {
+ "id": "mockup-project--req-tenant-isolation",
+ "sourceId": "mockup-project",
+ "targetId": "req-tenant-isolation",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-project--webapp",
+ "sourceId": "mockup-project",
+ "targetId": "webapp",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-read-only--req-subscription-enforcement",
+ "sourceId": "mockup-read-only",
+ "targetId": "req-subscription-enforcement",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-read-only--webapp",
+ "sourceId": "mockup-read-only",
+ "targetId": "webapp",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-read-only--mockup-billing-usage",
+ "sourceId": "mockup-read-only",
"targetId": "mockup-billing-usage",
"relationType": "navigates-to",
"label": "Update billing"
@@ -656,6 +893,83 @@
"sourceId": "mockup-billing-usage",
"targetId": "webapp",
"relationType": "presented-by"
+ },
+ {
+ "id": "mockup-billing-usage--mockup-update-payment",
+ "sourceId": "mockup-billing-usage",
+ "targetId": "mockup-update-payment",
+ "relationType": "navigates-to",
+ "label": "Update payment method"
+ },
+ {
+ "id": "mockup-billing-usage--mockup-sso-settings",
+ "sourceId": "mockup-billing-usage",
+ "targetId": "mockup-sso-settings",
+ "relationType": "navigates-to",
+ "label": "Single sign-on"
+ },
+ {
+ "id": "mockup-update-payment--req-subscription-enforcement",
+ "sourceId": "mockup-update-payment",
+ "targetId": "req-subscription-enforcement",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-update-payment--webapp",
+ "sourceId": "mockup-update-payment",
+ "targetId": "webapp",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-update-payment--mockup-workspace-dashboard",
+ "sourceId": "mockup-update-payment",
+ "targetId": "mockup-workspace-dashboard",
+ "relationType": "navigates-to",
+ "label": "Save & pay"
+ },
+ {
+ "id": "mockup-sso-settings--webapp",
+ "sourceId": "mockup-sso-settings",
+ "targetId": "webapp",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-sso-settings--mockup-users-roles",
+ "sourceId": "mockup-sso-settings",
+ "targetId": "mockup-users-roles",
+ "relationType": "navigates-to",
+ "label": "Users & roles"
+ },
+ {
+ "id": "mockup-users-roles--webapp",
+ "sourceId": "mockup-users-roles",
+ "targetId": "webapp",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-tenant-list--admin-console",
+ "sourceId": "mockup-tenant-list",
+ "targetId": "admin-console",
+ "relationType": "presented-by"
+ },
+ {
+ "id": "mockup-tenant-list--mockup-tenant-detail",
+ "sourceId": "mockup-tenant-list",
+ "targetId": "mockup-tenant-detail",
+ "relationType": "navigates-to",
+ "label": "Open tenant"
+ },
+ {
+ "id": "mockup-tenant-detail--req-tenant-isolation",
+ "sourceId": "mockup-tenant-detail",
+ "targetId": "req-tenant-isolation",
+ "relationType": "illustrates"
+ },
+ {
+ "id": "mockup-tenant-detail--admin-console",
+ "sourceId": "mockup-tenant-detail",
+ "targetId": "admin-console",
+ "relationType": "presented-by"
}
]
}
diff --git a/tests/hubCatalogue.test.ts b/tests/hubCatalogue.test.ts
index 9f991f0..994d61b 100644
--- a/tests/hubCatalogue.test.ts
+++ b/tests/hubCatalogue.test.ts
@@ -4,15 +4,16 @@
* - every top-level hub//.radical is valid (hub block, ids, refs)
* - index.json summaries carry what the cards / drop-target check need
* - doc ↔ concept mapping round-trips
- * - blueprint mockups are wired up with metamodel-valid relations and
- * carry wireframes that are already sanitised
+ * - blueprint mockups form per-actor screen flows (flow groups linked by
+ * navigates-to), use metamodel-valid relations and carry wireframes that
+ * are already sanitised
*/
import { describe, it, expect } from 'vitest'
import { resolve } from 'node:path'
import { readCatalogue, validateCatalogue, buildIndex } from '../tools/hubCatalogue'
import { conceptToDoc, docToConcept, summarize, type HubRadicalDoc } from '../src/renderer/src/hub/hubFormat'
import { useDiagramStore } from '../src/renderer/src/store/diagramStore'
-import { builtInGovernanceMetamodel } from '../src/renderer/src/types/metamodel'
+import { builtInGovernanceMetamodel, isParentAllowed } from '../src/renderer/src/types/metamodel'
import { sanitizeWireframeSvg } from '../src/renderer/src/ai/mockupWireframe'
const HUB_DIR = resolve(__dirname, '../hub')
@@ -89,12 +90,17 @@ describe('blueprint mockups', () => {
const blueprints = readCatalogue(HUB_DIR).filter((e) => e.doc.hub.category === 'blueprint')
const mm = builtInGovernanceMetamodel()
- it.each(blueprints.map((e) => [e.file, e.doc] as const))('%s has wired-up mockups with sanitised wireframes', (_file, doc) => {
+ it.each(blueprints.map((e) => [e.file, e.doc] as const))('%s has screen flows with sanitised wireframes', (_file, doc) => {
const byId = new Map(doc.nodes.map((n) => [String(n.id), n]))
const mockups = doc.nodes.filter((n) => n.type === 'mockup')
- expect(mockups.length).toBeGreaterThan(0)
+ expect(mockups.length).toBeGreaterThanOrEqual(8)
+ const inFlow = new Set((doc.relations ?? []).filter((r) => r.relationType === 'navigates-to').flatMap((r) => [r.sourceId, r.targetId]))
for (const m of mockups) {
+ const flow = byId.get(String(m.parentId))
+ expect(flow?.type, `${m.id}: not inside a flow group`).toBe('group')
+ expect(isParentAllowed(mm, 'mockup', 'group')).toBe(true)
+ expect(inFlow.has(String(m.id)), `${m.id}: not linked into its flow`).toBe(true)
const wireframe = m.wireframe as string
expect(typeof m.screen).toBe('string')
expect(sanitizeWireframeSvg(wireframe)).toBe(wireframe)