refactor: conform standards docs to doc-forge wiki/lib layout; generic build.py; static kind links
This commit is contained in:
@@ -6,12 +6,12 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
<meta name="description" content="The standardized application blueprint and library registry.">
|
||||
<meta name="description" content="The rules and library registry of the standardized application blueprint.">
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="prev" href="../01_overview/">
|
||||
<link rel="prev" href="..">
|
||||
|
||||
|
||||
<link rel="next" href="../03_composition_root/">
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
|
||||
|
||||
<title>Layering Rules - Aetos Standards</title>
|
||||
<title>Layering Rules - Aetos Standards — Reference</title>
|
||||
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
|
||||
<header class="md-header md-header--shadow" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href=".." title="Aetos Standards" class="md-header__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href=".." title="Aetos Standards — Reference" class="md-header__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
@@ -104,7 +104,7 @@
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
@@ -207,13 +207,13 @@
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href=".." title="Aetos Standards" class="md-nav__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href=".." title="Aetos Standards — Reference" class="md-nav__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
|
||||
</a>
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</label>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
@@ -244,28 +244,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../01_overview/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Overview
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -540,50 +518,6 @@
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../10_testing/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Testing
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../11_build_recipe/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Build Recipe
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../12_genericity_matrix/" class="md-nav__link">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
<meta name="description" content="The standardized application blueprint and library registry.">
|
||||
<meta name="description" content="The rules and library registry of the standardized application blueprint.">
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
|
||||
|
||||
<title>Composition Root - Aetos Standards</title>
|
||||
<title>Composition Root - Aetos Standards — Reference</title>
|
||||
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
|
||||
<header class="md-header md-header--shadow" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href=".." title="Aetos Standards" class="md-header__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href=".." title="Aetos Standards — Reference" class="md-header__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
@@ -104,7 +104,7 @@
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
@@ -207,13 +207,13 @@
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href=".." title="Aetos Standards" class="md-nav__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href=".." title="Aetos Standards — Reference" class="md-nav__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
|
||||
</a>
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</label>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
@@ -246,28 +246,6 @@
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../01_overview/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Overview
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../02_layering_rules/" class="md-nav__link">
|
||||
|
||||
@@ -531,50 +509,6 @@
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../10_testing/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Testing
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../11_build_recipe/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Build Recipe
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../12_genericity_matrix/" class="md-nav__link">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
<meta name="description" content="The standardized application blueprint and library registry.">
|
||||
<meta name="description" content="The rules and library registry of the standardized application blueprint.">
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
|
||||
|
||||
<title>Spec-First Routing - Aetos Standards</title>
|
||||
<title>Spec-First Routing - Aetos Standards — Reference</title>
|
||||
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
|
||||
<header class="md-header md-header--shadow" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href=".." title="Aetos Standards" class="md-header__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href=".." title="Aetos Standards — Reference" class="md-header__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
@@ -104,7 +104,7 @@
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
@@ -207,13 +207,13 @@
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href=".." title="Aetos Standards" class="md-nav__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href=".." title="Aetos Standards — Reference" class="md-nav__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
|
||||
</a>
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</label>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
@@ -246,28 +246,6 @@
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../01_overview/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Overview
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../02_layering_rules/" class="md-nav__link">
|
||||
|
||||
@@ -549,50 +527,6 @@
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../10_testing/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Testing
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../11_build_recipe/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Build Recipe
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../12_genericity_matrix/" class="md-nav__link">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
<meta name="description" content="The standardized application blueprint and library registry.">
|
||||
<meta name="description" content="The rules and library registry of the standardized application blueprint.">
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
|
||||
|
||||
<title>CRUD Template - Aetos Standards</title>
|
||||
<title>CRUD Template - Aetos Standards — Reference</title>
|
||||
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
|
||||
<header class="md-header md-header--shadow" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href=".." title="Aetos Standards" class="md-header__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href=".." title="Aetos Standards — Reference" class="md-header__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
@@ -104,7 +104,7 @@
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
@@ -207,13 +207,13 @@
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href=".." title="Aetos Standards" class="md-nav__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href=".." title="Aetos Standards — Reference" class="md-nav__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
|
||||
</a>
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</label>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
@@ -246,28 +246,6 @@
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../01_overview/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Overview
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../02_layering_rules/" class="md-nav__link">
|
||||
|
||||
@@ -549,50 +527,6 @@
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../10_testing/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Testing
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../11_build_recipe/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Build Recipe
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../12_genericity_matrix/" class="md-nav__link">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
<meta name="description" content="The standardized application blueprint and library registry.">
|
||||
<meta name="description" content="The rules and library registry of the standardized application blueprint.">
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
|
||||
|
||||
<title>Pipeline Scaffold - Aetos Standards</title>
|
||||
<title>Pipeline Scaffold - Aetos Standards — Reference</title>
|
||||
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
|
||||
<header class="md-header md-header--shadow" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href=".." title="Aetos Standards" class="md-header__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href=".." title="Aetos Standards — Reference" class="md-header__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
@@ -104,7 +104,7 @@
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
@@ -207,13 +207,13 @@
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href=".." title="Aetos Standards" class="md-nav__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href=".." title="Aetos Standards — Reference" class="md-nav__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
|
||||
</a>
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</label>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
@@ -246,28 +246,6 @@
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../01_overview/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Overview
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../02_layering_rules/" class="md-nav__link">
|
||||
|
||||
@@ -558,50 +536,6 @@
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../10_testing/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Testing
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../11_build_recipe/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Build Recipe
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../12_genericity_matrix/" class="md-nav__link">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
<meta name="description" content="The standardized application blueprint and library registry.">
|
||||
<meta name="description" content="The rules and library registry of the standardized application blueprint.">
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
|
||||
|
||||
<title>Worker & Progress - Aetos Standards</title>
|
||||
<title>Worker & Progress - Aetos Standards — Reference</title>
|
||||
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
|
||||
<header class="md-header md-header--shadow" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href=".." title="Aetos Standards" class="md-header__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href=".." title="Aetos Standards — Reference" class="md-header__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
@@ -104,7 +104,7 @@
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
@@ -207,13 +207,13 @@
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href=".." title="Aetos Standards" class="md-nav__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href=".." title="Aetos Standards — Reference" class="md-nav__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
|
||||
</a>
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</label>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
@@ -246,28 +246,6 @@
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../01_overview/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Overview
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../02_layering_rules/" class="md-nav__link">
|
||||
|
||||
@@ -531,50 +509,6 @@
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../10_testing/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Testing
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../11_build_recipe/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Build Recipe
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../12_genericity_matrix/" class="md-nav__link">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
<meta name="description" content="The standardized application blueprint and library registry.">
|
||||
<meta name="description" content="The rules and library registry of the standardized application blueprint.">
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
|
||||
|
||||
<title>Human-in-the-Loop - Aetos Standards</title>
|
||||
<title>Human-in-the-Loop - Aetos Standards — Reference</title>
|
||||
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
|
||||
<header class="md-header md-header--shadow" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href=".." title="Aetos Standards" class="md-header__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href=".." title="Aetos Standards — Reference" class="md-header__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
@@ -104,7 +104,7 @@
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
@@ -207,13 +207,13 @@
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href=".." title="Aetos Standards" class="md-nav__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href=".." title="Aetos Standards — Reference" class="md-nav__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
|
||||
</a>
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</label>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
@@ -246,28 +246,6 @@
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../01_overview/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Overview
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../02_layering_rules/" class="md-nav__link">
|
||||
|
||||
@@ -540,50 +518,6 @@
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../10_testing/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Testing
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../11_build_recipe/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Build Recipe
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../12_genericity_matrix/" class="md-nav__link">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
<meta name="description" content="The standardized application blueprint and library registry.">
|
||||
<meta name="description" content="The rules and library registry of the standardized application blueprint.">
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<link rel="prev" href="../08_human_in_loop/">
|
||||
|
||||
|
||||
<link rel="next" href="../10_testing/">
|
||||
<link rel="next" href="../12_genericity_matrix/">
|
||||
|
||||
|
||||
<link rel="icon" href="../assets/images/favicon.png">
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
|
||||
|
||||
<title>Infra Adapters - Aetos Standards</title>
|
||||
<title>Infra Adapters - Aetos Standards — Reference</title>
|
||||
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
|
||||
<header class="md-header md-header--shadow" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href=".." title="Aetos Standards" class="md-header__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href=".." title="Aetos Standards — Reference" class="md-header__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
@@ -104,7 +104,7 @@
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
@@ -207,13 +207,13 @@
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href=".." title="Aetos Standards" class="md-nav__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href=".." title="Aetos Standards — Reference" class="md-nav__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
|
||||
</a>
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</label>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
@@ -246,28 +246,6 @@
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../01_overview/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Overview
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../02_layering_rules/" class="md-nav__link">
|
||||
|
||||
@@ -558,50 +536,6 @@
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../10_testing/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Testing
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../11_build_recipe/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Build Recipe
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../12_genericity_matrix/" class="md-nav__link">
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
<meta name="description" content="The standardized application blueprint and library registry.">
|
||||
<meta name="description" content="The rules and library registry of the standardized application blueprint.">
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="prev" href="../11_build_recipe/">
|
||||
<link rel="prev" href="../09_infra_adaptors/">
|
||||
|
||||
|
||||
<link rel="next" href="../13_library_registry/">
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
|
||||
|
||||
<title>Genericity Matrix - Aetos Standards</title>
|
||||
<title>Genericity Matrix - Aetos Standards — Reference</title>
|
||||
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
|
||||
<header class="md-header md-header--shadow" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href=".." title="Aetos Standards" class="md-header__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href=".." title="Aetos Standards — Reference" class="md-header__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
@@ -104,7 +104,7 @@
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
@@ -207,13 +207,13 @@
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href=".." title="Aetos Standards" class="md-nav__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href=".." title="Aetos Standards — Reference" class="md-nav__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
|
||||
</a>
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</label>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
@@ -246,28 +246,6 @@
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../01_overview/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Overview
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../02_layering_rules/" class="md-nav__link">
|
||||
|
||||
@@ -442,50 +420,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../10_testing/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Testing
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../11_build_recipe/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Build Recipe
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
<meta name="description" content="The standardized application blueprint and library registry.">
|
||||
<meta name="description" content="The rules and library registry of the standardized application blueprint.">
|
||||
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
|
||||
|
||||
<title>Library Registry - Aetos Standards</title>
|
||||
<title>Library Registry - Aetos Standards — Reference</title>
|
||||
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
|
||||
<header class="md-header md-header--shadow" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href=".." title="Aetos Standards" class="md-header__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href=".." title="Aetos Standards — Reference" class="md-header__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
@@ -102,7 +102,7 @@
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
@@ -205,13 +205,13 @@
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href=".." title="Aetos Standards" class="md-nav__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href=".." title="Aetos Standards — Reference" class="md-nav__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
|
||||
</a>
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</label>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
@@ -244,28 +244,6 @@
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../01_overview/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Overview
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../02_layering_rules/" class="md-nav__link">
|
||||
|
||||
@@ -442,50 +420,6 @@
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../10_testing/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Testing
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../11_build_recipe/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Build Recipe
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="../12_genericity_matrix/" class="md-nav__link">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
<meta name="description" content="The standardized application blueprint and library registry.">
|
||||
<meta name="description" content="The rules and library registry of the standardized application blueprint.">
|
||||
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
|
||||
|
||||
<title>Aetos Standards</title>
|
||||
<title>Aetos Standards — Reference</title>
|
||||
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
|
||||
<header class="md-header md-header--shadow" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="/." title="Aetos Standards" class="md-header__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href="/." title="Aetos Standards — Reference" class="md-header__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
@@ -95,7 +95,7 @@
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
@@ -198,13 +198,13 @@
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href="/." title="Aetos Standards" class="md-nav__button md-logo" aria-label="Aetos Standards" data-md-component="logo">
|
||||
<a href="/." title="Aetos Standards — Reference" class="md-nav__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
|
||||
</a>
|
||||
Aetos Standards
|
||||
Aetos Standards — Reference
|
||||
</label>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
@@ -237,28 +237,6 @@
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="/01_overview/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Overview
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="/02_layering_rules/" class="md-nav__link">
|
||||
|
||||
@@ -435,50 +413,6 @@
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="/10_testing/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Testing
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="/11_build_recipe/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Build Recipe
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="/12_genericity_matrix/" class="md-nav__link">
|
||||
|
||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
587
standards/lib/index.html
Normal file
587
standards/lib/index.html
Normal file
@@ -0,0 +1,587 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en" class="no-js">
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
<meta name="description" content="The rules and library registry of the standardized application blueprint.">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="next" href="02_layering_rules/">
|
||||
|
||||
|
||||
<link rel="icon" href="assets/images/favicon.png">
|
||||
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.6.23">
|
||||
|
||||
|
||||
|
||||
<title>Aetos Standards — Reference</title>
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="assets/stylesheets/main.84d31ad4.min.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="assets/stylesheets/palette.06af60db.min.css">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300,300i,400,400i,700,700i%7CJetBrains+Mono:400,400i,700,700i&display=fallback">
|
||||
<style>:root{--md-text-font:"Inter";--md-code-font:"JetBrains Mono"}</style>
|
||||
|
||||
|
||||
|
||||
<script>__md_scope=new URL(".",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<body dir="ltr" data-md-color-scheme="slate" data-md-color-primary="deep-purple" data-md-color-accent="cyan">
|
||||
|
||||
|
||||
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
|
||||
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
|
||||
<label class="md-overlay" for="__drawer"></label>
|
||||
<div data-md-component="skip">
|
||||
|
||||
|
||||
<a href="#standards-reference" class="md-skip">
|
||||
Skip to content
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<div data-md-component="announce">
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<header class="md-header md-header--shadow" data-md-component="header">
|
||||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||||
<a href="." title="Aetos Standards — Reference" class="md-header__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
|
||||
</a>
|
||||
<label class="md-header__button md-icon" for="__drawer">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||||
</label>
|
||||
<div class="md-header__title" data-md-component="header-title">
|
||||
<div class="md-header__ellipsis">
|
||||
<div class="md-header__topic">
|
||||
<span class="md-ellipsis">
|
||||
Aetos Standards — Reference
|
||||
</span>
|
||||
</div>
|
||||
<div class="md-header__topic" data-md-component="header-topic">
|
||||
<span class="md-ellipsis">
|
||||
|
||||
Home
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<form class="md-header__option" data-md-component="palette">
|
||||
|
||||
|
||||
|
||||
|
||||
<input class="md-option" data-md-color-media="" data-md-color-scheme="slate" data-md-color-primary="deep-purple" data-md-color-accent="cyan" aria-hidden="true" type="radio" name="__palette" id="__palette_0">
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<label class="md-header__button md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
</label>
|
||||
<div class="md-search" data-md-component="search" role="dialog">
|
||||
<label class="md-search__overlay" for="__search"></label>
|
||||
<div class="md-search__inner" role="search">
|
||||
<form class="md-search__form" name="search">
|
||||
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
|
||||
<label class="md-search__icon md-icon" for="__search">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
|
||||
</label>
|
||||
<nav class="md-search__options" aria-label="Search">
|
||||
|
||||
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91s2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08"/></svg>
|
||||
</a>
|
||||
|
||||
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div class="md-search__suggest" data-md-component="search-suggest"></div>
|
||||
|
||||
</form>
|
||||
<div class="md-search__output">
|
||||
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
|
||||
<div class="md-search-result" data-md-component="search-result">
|
||||
<div class="md-search-result__meta">
|
||||
Initializing search
|
||||
</div>
|
||||
<ol class="md-search-result__list" role="presentation"></ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</nav>
|
||||
|
||||
</header>
|
||||
|
||||
<div class="md-container" data-md-component="container">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<main class="md-main" data-md-component="main">
|
||||
<div class="md-main__inner md-grid">
|
||||
|
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
|
||||
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
|
||||
<label class="md-nav__title" for="__drawer">
|
||||
<a href="." title="Aetos Standards — Reference" class="md-nav__button md-logo" aria-label="Aetos Standards — Reference" data-md-component="logo">
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||||
|
||||
</a>
|
||||
Aetos Standards — Reference
|
||||
</label>
|
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item md-nav__item--active">
|
||||
|
||||
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="." class="md-nav__link md-nav__link--active">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Home
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="02_layering_rules/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Layering Rules
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="03_composition_root/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Composition Root
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="04_spec_first_routing/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Spec-First Routing
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="05_crud_template/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
CRUD Template
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="06_pipeline_scaffold/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Pipeline Scaffold
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="07_worker_and_progress/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Worker & Progress
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="08_human_in_loop/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Human-in-the-Loop
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="09_infra_adaptors/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Infra Adapters
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="12_genericity_matrix/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Genericity Matrix
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="13_library_registry/" class="md-nav__link">
|
||||
|
||||
|
||||
|
||||
<span class="md-ellipsis">
|
||||
Library Registry
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
|
||||
<div class="md-sidebar__scrollwrap">
|
||||
<div class="md-sidebar__inner">
|
||||
|
||||
|
||||
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="md-content" data-md-component="content">
|
||||
<article class="md-content__inner md-typeset">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h1 id="standards-reference">Standards Reference</h1>
|
||||
<p>The reference half of the Standards description: the enforceable rules,
|
||||
patterns, and the internal library catalog that agents consult while coding.</p>
|
||||
<ul>
|
||||
<li><strong>Layering Rules</strong> — the strict request → domain → repository layering and
|
||||
the rules that enforce it.</li>
|
||||
<li><strong>Composition Root</strong> — a single composition point wiring the whole app.</li>
|
||||
<li><strong>Spec-First Routing</strong> — the OpenAPI contract as the routing source of truth.</li>
|
||||
<li><strong>CRUD Template</strong> — the standardized generic resource endpoint scaffold.</li>
|
||||
<li><strong>Pipeline Scaffold</strong> — the deterministic pipeline skeleton for multi-stage
|
||||
jobs.</li>
|
||||
<li><strong>Worker & Progress</strong> — queued work, progress reporting, and visibility.</li>
|
||||
<li><strong>Human-in-the-Loop</strong> — guardrails and approvals inside automations.</li>
|
||||
<li><strong>Infra Adapters</strong> — how infrastructure connects without leaking into domain.</li>
|
||||
<li><strong>Genericity Matrix</strong> — what is generic vs app-specific.</li>
|
||||
<li><strong>Library Registry</strong> — the catalog of internal libraries behind all of it.</li>
|
||||
</ul>
|
||||
<p>Read the <strong>Wiki</strong> for the philosophy and rationale; these pages are the
|
||||
reference that mirrors the <code>standards</code> MCP bundle.</p>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</article>
|
||||
</div>
|
||||
|
||||
|
||||
<script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script>
|
||||
|
||||
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
|
||||
</div>
|
||||
|
||||
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg>
|
||||
Back to top
|
||||
</button>
|
||||
|
||||
</main>
|
||||
|
||||
<footer class="md-footer">
|
||||
|
||||
<div class="md-footer-meta md-typeset">
|
||||
<div class="md-footer-meta__inner md-grid">
|
||||
<div class="md-copyright">
|
||||
|
||||
|
||||
Made with
|
||||
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
|
||||
Material for MkDocs
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
<div class="md-dialog" data-md-component="dialog">
|
||||
<div class="md-dialog__inner md-typeset"></div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script id="__config" type="application/json">{"base": ".", "features": ["navigation.sections", "navigation.expand", "navigation.top", "navigation.instant", "navigation.tracking", "navigation.indexes", "content.code.copy", "content.code.annotate", "content.tabs.link", "content.action.edit", "search.highlight", "search.share", "search.suggest"], "search": "assets/javascripts/workers/search.973d3a69.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
|
||||
|
||||
|
||||
<script src="assets/javascripts/bundle.f55a23d4.min.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
1
standards/lib/search/search_index.json
Normal file
1
standards/lib/search/search_index.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
53
standards/src/lib/02_layering_rules.md
Normal file
53
standards/src/lib/02_layering_rules.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Layering Rules
|
||||
|
||||
## Summary
|
||||
|
||||
The dependency direction is fixed. Violating it is a design defect.
|
||||
|
||||
---
|
||||
|
||||
## Allowed imports
|
||||
|
||||
```
|
||||
interfaces/* -> domain/* (services, ports, models)
|
||||
interfaces/* -> infra/adapters/* (only read-only value objects)
|
||||
domain/* -> domain/* (same layer)
|
||||
domain/* -> pipelines/* (call a pipeline via a port)
|
||||
infra/adapters/* -> domain/ports/* (implement the interface)
|
||||
main.py/init.py -> everything (composition root only)
|
||||
```
|
||||
|
||||
## Forbidden imports
|
||||
|
||||
```
|
||||
domain/* -> interfaces/* (never)
|
||||
domain/* -> fastapi/starlette (never)
|
||||
infra/adapters/* -> interfaces/* (never)
|
||||
interfaces/* -> infra/db/* (state lives in resources, not imports)
|
||||
```
|
||||
|
||||
## Why
|
||||
|
||||
- A pure domain is testable without a server, a database, or an event loop.
|
||||
- Adapters can be swapped (in-memory fake, real database) without touching
|
||||
business logic.
|
||||
- The HTTP edge stays thin and replaceable.
|
||||
|
||||
## The port/adapter contract
|
||||
|
||||
A **port** is an abstract class or protocol declared in `domain/<x>/ports/`.
|
||||
An **adapter** implements it in `infra/adapters/`. The composition root binds
|
||||
the two.
|
||||
|
||||
```
|
||||
domain/<resource>/ports/db.py -> repository port + document model
|
||||
domain/<resource>/ports/service.py -> service + factory
|
||||
infra/db/repos/<vendor>.py -> repository adapter factory
|
||||
infra/adapters/service.py -> generic CRUD service base
|
||||
```
|
||||
|
||||
## Enforcement
|
||||
|
||||
- Keep every domain module importable with only the standard library and
|
||||
Pydantic. If a domain import pulls in `fastapi`, the layering is broken.
|
||||
- Reviewers (and CI) reject `import fastapi` inside `domain/` or `infra/`.
|
||||
89
standards/src/lib/03_composition_root.md
Normal file
89
standards/src/lib/03_composition_root.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Composition Root
|
||||
|
||||
## Summary
|
||||
|
||||
All wiring happens in exactly one module. It builds a **flat resource
|
||||
registry** and exposes it to the HTTP layer and to scripts.
|
||||
|
||||
---
|
||||
|
||||
## The resource registry
|
||||
|
||||
`init.py` returns a plain `dict[str, Any]`. Keys follow two conventions:
|
||||
|
||||
- `<resource>_repo` — a repository adapter for a resource.
|
||||
- `<resource>_svc` — a service for a resource.
|
||||
|
||||
Plus named infrastructure adaptors:
|
||||
|
||||
- `http_cache_store` — response cache backend.
|
||||
- `progress_bus` — progress/event bus.
|
||||
- `<vendor>_<kind>` — fetchers, clients, stores.
|
||||
|
||||
The API stores the dict on `app.state.resources` during lifespan startup.
|
||||
Handlers read from it; nothing imports concrete adapters.
|
||||
|
||||
## Shape
|
||||
|
||||
```python
|
||||
_resources: dict = {}
|
||||
_initialized = False
|
||||
|
||||
async def init(settings=None) -> dict:
|
||||
global _initialized
|
||||
if _initialized:
|
||||
return _resources
|
||||
|
||||
await setup_database(uri=settings.mongo_uri, db_name=settings.mongo_db)
|
||||
|
||||
# 1. leaf repositories
|
||||
user_repo = create_user_repo()
|
||||
|
||||
# 2. dependent repositories (dependencies passed by keyword)
|
||||
wallet_repo = create_wallet_repo(user_repo=user_repo)
|
||||
|
||||
# 3. services (build via the DI library, injecting the repo)
|
||||
_resources["user_repo"] = user_repo
|
||||
_resources["wallet_repo"] = wallet_repo
|
||||
_resources["user_svc"] = get_user_service(user_repo)
|
||||
_resources["wallet_svc"] = get_wallet_service(wallet_repo)
|
||||
|
||||
# 4. infrastructure adaptors (optional)
|
||||
_resources["http_cache_store"] = HttpCacheStore(...)
|
||||
_resources["progress_bus"] = ProgressEventBus()
|
||||
|
||||
_initialized = True
|
||||
return _resources
|
||||
|
||||
async def shutdown() -> None:
|
||||
global _initialized
|
||||
await close_stores(_resources)
|
||||
await shutdown_database()
|
||||
_resources.clear()
|
||||
_initialized = False
|
||||
```
|
||||
|
||||
## Typed accessors
|
||||
|
||||
Expose a `get_svc(name)` helper with one `@overload` per key so scripts get
|
||||
typing without importing concrete classes:
|
||||
|
||||
```python
|
||||
@overload
|
||||
def get_svc(name: Literal["user_svc"]) -> UserService: ...
|
||||
@overload
|
||||
def get_svc(name: Literal["wallet_svc"]) -> WalletService: ...
|
||||
|
||||
def get_svc(name: str):
|
||||
return _resources[name]
|
||||
```
|
||||
|
||||
Also expose `get_db()` (raw database handle) and pure helpers such as
|
||||
`compute_fingerprint(payload)` (canonical hash used for deduplication).
|
||||
|
||||
## Rules
|
||||
|
||||
- `init()` is **idempotent**.
|
||||
- The composition root is the only place that imports concrete adapters.
|
||||
- Handlers never call `init()`; they read `request.app.state.resources`.
|
||||
- Scripts call `init()` / `shutdown()` around their work.
|
||||
77
standards/src/lib/04_spec_first_routing.md
Normal file
77
standards/src/lib/04_spec_first_routing.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Spec-First Routing
|
||||
|
||||
## Summary
|
||||
|
||||
The OpenAPI document is the single source of truth for the HTTP contract.
|
||||
Handlers are bound to it by name; there is no decorator router.
|
||||
|
||||
---
|
||||
|
||||
## The contract
|
||||
|
||||
- `openapi.yaml` (or `.json`) declares paths, schemas, parameters, responses,
|
||||
and security.
|
||||
- A custom `operationId` names the handler function.
|
||||
- The framework (`openapi_first`) validates requests/responses against the
|
||||
spec and dispatches to the handler whose name equals `operationId`.
|
||||
|
||||
## App bootstrap
|
||||
|
||||
```python
|
||||
# main.py
|
||||
from openapi_first.app import OpenAPIFirstApp
|
||||
|
||||
app = OpenAPIFirstApp(
|
||||
openapi_path="openapi.yaml",
|
||||
routes_module=interfaces.openapi, # module whose namespace holds handlers
|
||||
title="<App> API",
|
||||
version="0.1.0",
|
||||
lifespan=lifespan,
|
||||
)
|
||||
```
|
||||
|
||||
`lifespan` calls the composition root:
|
||||
|
||||
```python
|
||||
@asynccontextmanager
|
||||
async def lifespan(app):
|
||||
app.state.resources = await init()
|
||||
yield
|
||||
await shutdown()
|
||||
```
|
||||
|
||||
## Handler registry
|
||||
|
||||
`interfaces/openapi.py` is a flat re-export of every handler, so the framework
|
||||
can resolve `operationId` across the whole app:
|
||||
|
||||
```python
|
||||
from interfaces.item.routes import *
|
||||
from interfaces.wallet.routes import *
|
||||
from interfaces.progress.sse import *
|
||||
```
|
||||
|
||||
## Handler shape
|
||||
|
||||
Handlers are thin and synchronous with respect to business logic:
|
||||
|
||||
```python
|
||||
async def create_item(request: Request, body: dict):
|
||||
svc = request.app.state.resources["item_svc"]
|
||||
return await svc.create(Item.model_validate(body))
|
||||
```
|
||||
|
||||
## Conventions
|
||||
|
||||
- `operationId` naming: `list_<resource>`, `create_<resource>`,
|
||||
`get_<resource>`, `update_<resource>`, `delete_<resource>`.
|
||||
- Read-only endpoints may use a cache decorator keyed on path + query + user.
|
||||
- Validation errors -> `400`; not found -> `404`; delete -> `204`.
|
||||
- Security is declared once in the spec and enforced by the framework; handlers
|
||||
contain no auth code.
|
||||
|
||||
## Codegen
|
||||
|
||||
The `openapi_first` library can scaffold models and route stubs from the spec,
|
||||
and ships app templates (CRUD app, model app, service). Prefer codegen over
|
||||
hand-writing boilerplate.
|
||||
79
standards/src/lib/05_crud_template.md
Normal file
79
standards/src/lib/05_crud_template.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# CRUD Template
|
||||
|
||||
## Summary
|
||||
|
||||
Every resource follows the same four-file slice. Repetition is intentional: it
|
||||
makes the codebase predictable and generatable.
|
||||
|
||||
---
|
||||
|
||||
## The slice
|
||||
|
||||
```
|
||||
domain/<resource>/
|
||||
models.py # pure Pydantic model (no DB fields)
|
||||
ports/
|
||||
db.py # document model + repository factory
|
||||
service.py # service class + factory
|
||||
converters.py # (optional) transforms
|
||||
```
|
||||
|
||||
## models.py
|
||||
|
||||
A framework-free Pydantic model describing the business shape.
|
||||
|
||||
```python
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
quantity: int = 0
|
||||
```
|
||||
|
||||
## ports/db.py
|
||||
|
||||
The persistence model extends the business model and adds database concerns.
|
||||
The repository factory selects the right repository flavour.
|
||||
|
||||
```python
|
||||
class ItemDocument(Item, BaseDocument): # BaseDocument adds id/created_at/...
|
||||
pass
|
||||
|
||||
def create_item_repo(*, owner_repo=None) -> ItemRepository:
|
||||
return repo_factory(
|
||||
collection="items",
|
||||
document=ItemDocument,
|
||||
populate_rules=[PopulateRule(field="owner", collection="users")],
|
||||
cache=None, # or a CachedBaseRepository config
|
||||
)
|
||||
```
|
||||
|
||||
## ports/service.py
|
||||
|
||||
The service is built through the DI library, injecting the repository. The
|
||||
generic CRUD base provides `create/read/update/patch/delete/search/...`.
|
||||
|
||||
```python
|
||||
class ItemService(CrudsServiceAdaptor[Item, ItemDocument]):
|
||||
model = Item
|
||||
schema = ItemDocument
|
||||
|
||||
def get_item_service(repo) -> ItemService:
|
||||
return build(ItemService, instances={"repo": repo})
|
||||
```
|
||||
|
||||
## Naming conventions
|
||||
|
||||
| Concept | Convention |
|
||||
|---|---|
|
||||
| collection | plural snake case (`items`) |
|
||||
| document | `<Resource>Document` |
|
||||
| repository factory | `create_<resource>_repo` |
|
||||
| service | `<Resource>Service` |
|
||||
| service factory | `get_<resource>_service` |
|
||||
| resource key | `<resource>_svc` |
|
||||
| operationId | `list_items`, `create_item`, ... |
|
||||
|
||||
## Optional files
|
||||
|
||||
- `converters.py` — pure functions mapping between domain types.
|
||||
- `ports/enrichment.py` — adapts a pipeline into an async callable the service
|
||||
can invoke (used when a resource is populated by a pipeline).
|
||||
84
standards/src/lib/06_pipeline_scaffold.md
Normal file
84
standards/src/lib/06_pipeline_scaffold.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# Pipeline Scaffold
|
||||
|
||||
## Summary
|
||||
|
||||
Data-driven processing is expressed as a **composition graph**. The graph is
|
||||
built by a DI library; execution is driven by a DAG engine. Stages are pure and
|
||||
individually testable.
|
||||
|
||||
---
|
||||
|
||||
## When to use a pipeline
|
||||
|
||||
Use a pipeline when a resource is produced by a **multi-step transformation**
|
||||
of an input (a document, a file, a message) rather than by a simple CRUD write.
|
||||
|
||||
## Structure of a family
|
||||
|
||||
A **family** is one input shape (e.g. a file format or a message kind). Each
|
||||
family has the same skeleton:
|
||||
|
||||
```
|
||||
pipelines/<family>/
|
||||
abc.py # port contracts (stage interfaces + root interface)
|
||||
impl.py # concrete stages + root implementation
|
||||
pipeline.yaml # graph spec: nodes, edges, roots, schema
|
||||
resources/
|
||||
pipeline.yaml # runtime spec consumed by the loader
|
||||
patterns/*.yaml # rule fragments (pattern registry)
|
||||
banks/<variant>.py # optional variants that re-pin a few slots
|
||||
```
|
||||
|
||||
## Shared scaffolding
|
||||
|
||||
```
|
||||
pipelines/shared/
|
||||
ports.py # RegistryPort, FinalizePort, etc.
|
||||
loader.py # build(root_cls) -> wires the graph + engine
|
||||
registry.py # resolve_root(...) -> root class for a (kind, variant)
|
||||
```
|
||||
|
||||
## The loader
|
||||
|
||||
```python
|
||||
def build(root_cls, *, instances=None, config=None):
|
||||
# merge resources/patterns/*.yaml into a pattern registry
|
||||
# bind registry/config into each graph node by id
|
||||
# build the schema (state) class and the engine
|
||||
root = root_cls()
|
||||
root._engine = Engine(graph, state_cls, payload_cls)
|
||||
return root
|
||||
```
|
||||
|
||||
## The registry
|
||||
|
||||
A single lookup replaces hand-rolled `if/else` selection:
|
||||
|
||||
```python
|
||||
_FAMILIES = {
|
||||
"csv": {"acme": {"heuristic": AcmeCsvPipeline}},
|
||||
"json": {"globex": {"heuristic": GlobexJsonPipeline}},
|
||||
}
|
||||
|
||||
def resolve_root(kind, variant, mode="heuristic"):
|
||||
return _FAMILIES[kind][variant][mode]
|
||||
```
|
||||
|
||||
Adding a new variant means adding one class and one registry entry.
|
||||
|
||||
## Driving a pipeline
|
||||
|
||||
Stages are driven explicitly so intermediate state can be persisted:
|
||||
|
||||
```python
|
||||
pipeline = build(resolve_root(kind, variant))
|
||||
content = pipeline.ingest.materialize(raw_bytes)
|
||||
records = pipeline.reader.read(content)
|
||||
items = pipeline.normalize.build(records)
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
Because stages are pure, test each stage in isolation and test the assembled
|
||||
root against **fixture inputs** whose expected outputs are stored as golden
|
||||
files. A parity test replays fixtures headlessly.
|
||||
95
standards/src/lib/07_worker_and_progress.md
Normal file
95
standards/src/lib/07_worker_and_progress.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# Worker & Progress
|
||||
|
||||
## Summary
|
||||
|
||||
Long-running work runs in a background worker. Jobs are durable documents that
|
||||
move through an explicit status machine. Progress is streamed to clients.
|
||||
|
||||
---
|
||||
|
||||
## The status machine
|
||||
|
||||
```
|
||||
pending -> processing -> completed
|
||||
\-> failed (retried up to N times)
|
||||
\-> paused (waiting for human input)
|
||||
```
|
||||
|
||||
- `pending` — queued, eligible for pickup.
|
||||
- `processing` — claimed by a worker.
|
||||
- `paused` — blocked on a human-in-the-loop question.
|
||||
- `failed` — error; `retry_count` increments until a max, then skipped.
|
||||
- `completed` — terminal success.
|
||||
|
||||
## The poll loop
|
||||
|
||||
```python
|
||||
async def poll(resources):
|
||||
backoff = MIN_POLL
|
||||
while True:
|
||||
jobs = await job_svc.search(filter={"status": "pending"}, limit=100)
|
||||
backoff = MIN_POLL if jobs else min(backoff * 2, MAX_POLL)
|
||||
for job in jobs:
|
||||
if (job.retry_count or 0) >= MAX_RETRIES:
|
||||
continue
|
||||
await job_svc.update_status(job.id, "processing")
|
||||
try:
|
||||
await process(job, resources)
|
||||
except PauseForResolution:
|
||||
await job_svc.update_status(job.id, "paused")
|
||||
except Exception as e:
|
||||
await job_svc.update_status(
|
||||
job.id, "failed", error=str(e),
|
||||
retry_count=(job.retry_count or 0) + 1,
|
||||
)
|
||||
await asyncio.sleep(backoff)
|
||||
```
|
||||
|
||||
Tunables live in environment variables: `WORKER_POLL_MIN`, `WORKER_POLL_MAX`,
|
||||
`WORKER_MAX_RETRIES`.
|
||||
|
||||
## Progress events
|
||||
|
||||
A worker publishes structured progress events to a **bus** (a port). The HTTP
|
||||
edge exposes a streaming endpoint that replays events for a given job id.
|
||||
|
||||
```python
|
||||
class ProgressEvent(BaseModel):
|
||||
job_id: str
|
||||
step: str
|
||||
status: str # started | progress | completed | skipped | paused
|
||||
message: dict | None = None
|
||||
```
|
||||
|
||||
The bus is an in-memory adapter by default; because it is a port, it can be
|
||||
swapped for a durable broker. Emit from the orchestrator at each stage:
|
||||
|
||||
```python
|
||||
await self.bus.publish(ProgressEvent(job_id=jid, step="normalize",
|
||||
status="completed", message={"count": n}))
|
||||
```
|
||||
|
||||
## Streaming endpoint
|
||||
|
||||
```python
|
||||
async def stream_job_events(request, id):
|
||||
bus = request.app.state.resources.get("progress_bus")
|
||||
if bus is None:
|
||||
return {"events": []}
|
||||
|
||||
async def gen():
|
||||
sent = 0
|
||||
while True:
|
||||
events = bus.get_events(id)
|
||||
for e in events[sent:]:
|
||||
yield f"data: {e.model_dump_json(exclude_none=True)}\n\n"
|
||||
sent += 1
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
return StreamingResponse(gen(), media_type="text/event-stream")
|
||||
```
|
||||
|
||||
> **Known limitation.** The default in-memory bus is per-process. If the worker
|
||||
> and the API run as separate processes, events published by the worker are not
|
||||
> visible to the API. For production, back the bus with a shared broker. The
|
||||
> port boundary makes this a one-adapter change.
|
||||
87
standards/src/lib/08_human_in_loop.md
Normal file
87
standards/src/lib/08_human_in_loop.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# Human-in-the-Loop
|
||||
|
||||
## Summary
|
||||
|
||||
Some inputs are ambiguous and must be resolved by a human before processing can
|
||||
continue. The blueprint models this as a **durable pause**: an exception is
|
||||
raised, a question is recorded, the job pauses, and later resumes.
|
||||
|
||||
---
|
||||
|
||||
## The mechanism
|
||||
|
||||
1. During processing, a stage raises a domain exception carrying the context
|
||||
needed to ask the question:
|
||||
|
||||
```python
|
||||
raise PauseForResolution(
|
||||
job_id=jid,
|
||||
reference="<stable key for this decision>",
|
||||
options=[...],
|
||||
context={...},
|
||||
)
|
||||
```
|
||||
|
||||
2. The orchestrator catches it, records a **question document**, and re-raises
|
||||
so the worker marks the job `paused`.
|
||||
3. A client lists pending questions and answers one:
|
||||
|
||||
```
|
||||
GET /jobs/{id}/questions -> list pending questions
|
||||
POST /questions/{qid}/resolve {choice} -> record the choice
|
||||
```
|
||||
|
||||
4. When the **last** pending question is resolved, the resolve endpoint flips
|
||||
the parent job back to `pending`.
|
||||
5. The worker picks it up again, rebuilds a **resolution map** from previously
|
||||
answered questions, and seeds it into the pipeline so the same decision is
|
||||
not asked twice.
|
||||
|
||||
## The question document
|
||||
|
||||
```python
|
||||
class Question(BaseDocument):
|
||||
job_id: str
|
||||
reference: str # stable key: stage|field|value
|
||||
status: str # pending | resolved
|
||||
options: list[dict]
|
||||
context: dict
|
||||
chosen: dict | None = None
|
||||
resolved_at: str | None = None
|
||||
```
|
||||
|
||||
## Resume map
|
||||
|
||||
The worker turns answered questions into a lookup keyed by `reference`, then
|
||||
injects it into the pipeline so that stage can skip re-raising:
|
||||
|
||||
```python
|
||||
resolved = {q.reference: q.chosen for q in await svc.search(
|
||||
filter={"job_id": jid, "status": "resolved"})}
|
||||
|
||||
pipeline = build(root, instances={"pause": handler}, config={"resolved": resolved})
|
||||
```
|
||||
|
||||
## Resolve endpoint
|
||||
|
||||
```python
|
||||
async def resolve_question(request, id, body):
|
||||
svc = request.app.state.resources["question_svc"]
|
||||
q = await svc.read(id)
|
||||
await svc.update(id, {"chosen": body["chosen"], "status": "resolved",
|
||||
"resolved_at": now()})
|
||||
|
||||
pending = await svc.search(filter={"job_id": q.job_id, "status": "pending"},
|
||||
limit=1)
|
||||
if not pending:
|
||||
jobs = request.app.state.resources["job_svc"]
|
||||
job = await jobs.read(q.job_id)
|
||||
if job and job.status == "paused":
|
||||
await jobs.update_status(q.job_id, "pending")
|
||||
return q
|
||||
```
|
||||
|
||||
## CLI parity
|
||||
|
||||
The same flow is available through scripts/CLI: `list`, `get`, `create`,
|
||||
`questions`, `resolve`, `retry`.
|
||||
84
standards/src/lib/09_infra_adaptors.md
Normal file
84
standards/src/lib/09_infra_adaptors.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# Infra Adapters
|
||||
|
||||
## Summary
|
||||
|
||||
All external systems are reached through adapters in `infra/`. Prefer an
|
||||
existing internal library over writing a new adapter.
|
||||
|
||||
---
|
||||
|
||||
## Database (MongoDB)
|
||||
|
||||
- A single connection manager owns the client lifecycle.
|
||||
`setup_database(uri, db_name)` / `shutdown_database()`.
|
||||
- A repository **factory** selects the repository flavour:
|
||||
|
||||
```python
|
||||
repo_factory(collection, document, engine=None,
|
||||
populate_rules=None, cache=None)
|
||||
```
|
||||
|
||||
- base repository — plain CRUD;
|
||||
- populating repository — resolves referenced documents (foreign keys);
|
||||
- cached repository — an LRU/TTL cache in front of reads.
|
||||
|
||||
- Referenced documents are described declaratively:
|
||||
|
||||
```python
|
||||
PopulateRule(field="owner", collection="users",
|
||||
nested_rules=[], max_depth=2, projection=None)
|
||||
```
|
||||
|
||||
- Transactions use a session context manager provided by the library.
|
||||
|
||||
**Library:** the Mongo operations library (see registry). Do not write raw
|
||||
Motor calls in the domain.
|
||||
|
||||
## HTTP response cache
|
||||
|
||||
A generic GET cache sits behind a decorator.
|
||||
|
||||
```python
|
||||
@cache_response(ttl=60)
|
||||
async def list_items(request): ...
|
||||
```
|
||||
|
||||
- Keyed on path + query + user.
|
||||
- No-op when no store is wired.
|
||||
- `invalidate(*resources)` bumps a generation counter for write-triggered
|
||||
invalidation.
|
||||
|
||||
## Event / progress bus
|
||||
|
||||
A port with an in-memory default adapter (`publish`, `get_events`). Swap for a
|
||||
durable broker in production (see Worker & Progress).
|
||||
|
||||
## External clients (ingestion)
|
||||
|
||||
External fetchers (files, mail, HTTP) are adapters that wrap ingestion
|
||||
libraries. Construct them in the composition root and inject into
|
||||
orchestrators.
|
||||
|
||||
**Libraries:** the ingestion library (formats) and the mail library (email).
|
||||
|
||||
## LLM clients
|
||||
|
||||
When a pipeline calls a model provider, route through a pool adaptor that
|
||||
handles provider selection, retries, and client lifecycle:
|
||||
|
||||
```python
|
||||
pool = setup_async_pool(provider=settings.llm_provider)
|
||||
result = await pool.complete(model="...", messages=[...])
|
||||
```
|
||||
|
||||
Providers are selected by `LLM_PROVIDER` and configured by environment.
|
||||
|
||||
## Key-value store
|
||||
|
||||
For report/snapshot storage, provide a pluggable store port with a Redis
|
||||
adapter and an in-memory fallback.
|
||||
|
||||
## Rule
|
||||
|
||||
If a capability already exists as a library (DB, DI, ingestion, mail, auth,
|
||||
docs), **use it**. Writing a private copy of a library is a defect.
|
||||
57
standards/src/lib/12_genericity_matrix.md
Normal file
57
standards/src/lib/12_genericity_matrix.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# Genericity Matrix
|
||||
|
||||
## Summary
|
||||
|
||||
This page states precisely what is **reusable as-is**, what is a **template to
|
||||
adapt**, and what is **app-specific and must be authored**. Use it to scope a
|
||||
new build.
|
||||
|
||||
---
|
||||
|
||||
## Reusable as-is (do not rewrite)
|
||||
|
||||
| Area | Artifact |
|
||||
|---|---|
|
||||
| Layering & dependency rule | the package layout and import rules |
|
||||
| Composition root | the `init.py` shape, idempotency, `get_svc` overloading |
|
||||
| Web edge | `OpenAPIFirstApp` bootstrap, handler-name = `operationId`, thin handlers |
|
||||
| CRUD slice | the four-file pattern (model / ports-db / ports-service / converters) |
|
||||
| Generic CRUD service | the base service adaptor |
|
||||
| Repository factory | base / populating / cached selection |
|
||||
| Worker | the poll loop, backoff, retry, status machine |
|
||||
| Progress bus | the bus port + in-memory adapter + streaming endpoint |
|
||||
| Human-in-the-loop | the durable pause + question document + resolve endpoint + resume map |
|
||||
| Infra adaptors | HTTP cache decorator, KV store port, LLM pool scaffold |
|
||||
| Testing | integration harness with fakes, fixture-parity pattern |
|
||||
| Docs | GSDFC + generated stubs + MCP bundle pipeline |
|
||||
|
||||
## Template to adapt (copy, then change names)
|
||||
|
||||
| Artifact | Change |
|
||||
|---|---|
|
||||
| `pyproject.toml` | project name, dependencies |
|
||||
| `Dockerfile` / `docker-compose.yaml` | service name, ports, services |
|
||||
| `.env.example` | connection strings, provider keys |
|
||||
| `openapi.yaml` | every path/schema/operationId |
|
||||
| `scripts/` | seed and operator tasks |
|
||||
|
||||
## App-specific (must be authored)
|
||||
|
||||
| Artifact | Why |
|
||||
|---|---|
|
||||
| Resource models | the business shape |
|
||||
| Repository populate/cache rules | the relationships |
|
||||
| Service business logic | domain behaviour |
|
||||
| Pipeline families (if any) | the transformation graph |
|
||||
| Pattern/rule resources | data-driven policy |
|
||||
| Fixtures & golden outputs | test data |
|
||||
|
||||
## The two axes
|
||||
|
||||
Everything app-specific reduces to **two axes**:
|
||||
|
||||
1. **Resource model** — the set of resources, their fields, relationships, and
|
||||
the OpenAPI contract.
|
||||
2. **Pipeline family** — only if inputs require multi-step transformation.
|
||||
|
||||
If you can specify those two axes, the rest of the app follows mechanically.
|
||||
54
standards/src/lib/13_library_registry.md
Normal file
54
standards/src/lib/13_library_registry.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Library Registry
|
||||
|
||||
## Summary
|
||||
|
||||
The Aetos ecosystem provides internal libraries for the shared concerns of
|
||||
every application. **Use them.** Writing a private copy of any capability below
|
||||
is a defect.
|
||||
|
||||
Each library also exposes its own documentation as an MCP server. An AI agent
|
||||
can read a library's full API by connecting to its MCP and reading
|
||||
`docs://index`, `docs://nav`, and `docs://modules/{module}`.
|
||||
|
||||
---
|
||||
|
||||
## Catalog
|
||||
|
||||
| Library | Import | Purpose | Use instead of | MCP |
|
||||
|---|---|---|---|---|
|
||||
| DAG Pipe | `dagpipe` | Deterministic state-transformation DAG engine | hand-rolled orchestration/topological ordering | `:8006` |
|
||||
| Hexa | `hexa` | Declarative DI / composition-root tooling | manual wiring, hand-written factories | *(see note)* |
|
||||
| Mongo Ops | `mongo_ops` | Async MongoDB repositories, cache, populate, transactions | raw Motor CRUD and connection lifecycle | `:8007` |
|
||||
| Omniread | `omniread` | Format-agnostic fetch + parse (HTML, PDF, CSV, XLSX) | custom scrapers/parsers | `:8003` |
|
||||
| OpenAPI First | `openapi_first` | Spec-first FastAPI bootstrap, client, codegen | decorator routers and hand-written clients | `:8004` |
|
||||
| Mail Intake | `mail_intake` | Provider-agnostic read-only email ingestion | Gmail OAuth + MIME parsing | `:8002` |
|
||||
| Py JWT | `jwtlib` | JWT auth primitives (register/login/introspect) | password hashing + token code | `:8005` |
|
||||
| Doc Forge | `docforge` | Docstring compiler → MkDocs + MCP docs | custom doc generators | `:8001` |
|
||||
| Auth Server | *(service)* | Central identity service (register/login/me/introspect) | building auth endpoints yourself | — |
|
||||
|
||||
## How to choose
|
||||
|
||||
| You need to… | Use |
|
||||
|---|---|
|
||||
| persist and query documents | Mongo Ops |
|
||||
| compose dependencies | Hexa |
|
||||
| run a multi-step transformation | DAG Pipe |
|
||||
| read/parse an arbitrary input format | Omniread |
|
||||
| expose a contract-first HTTP API | OpenAPI First |
|
||||
| ingest email | Mail Intake |
|
||||
| authenticate users / verify tokens | Py JWT (+ Auth Server) |
|
||||
| generate documentation | Doc Forge |
|
||||
|
||||
## Discovery
|
||||
|
||||
A single **standards MCP** exposes `find_library(capability)`, which returns the
|
||||
matching library, its import name, its "use instead of" guidance, and its MCP
|
||||
endpoint. Query it before writing any infrastructure.
|
||||
|
||||
## Notes
|
||||
|
||||
- **Hexa** ships an MCP bundle but is not yet assigned a dedicated port in the
|
||||
hub config; it is reachable via the standards registry entry and will be
|
||||
wired to a port.
|
||||
- Library MCP servers expose **documentation resources** (not executable tools):
|
||||
they tell an agent the available API surface so it does not invent code.
|
||||
20
standards/src/lib/index.md
Normal file
20
standards/src/lib/index.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Standards Reference
|
||||
|
||||
The reference half of the Standards description: the enforceable rules,
|
||||
patterns, and the internal library catalog that agents consult while coding.
|
||||
|
||||
- **Layering Rules** — the strict request → domain → repository layering and
|
||||
the rules that enforce it.
|
||||
- **Composition Root** — a single composition point wiring the whole app.
|
||||
- **Spec-First Routing** — the OpenAPI contract as the routing source of truth.
|
||||
- **CRUD Template** — the standardized generic resource endpoint scaffold.
|
||||
- **Pipeline Scaffold** — the deterministic pipeline skeleton for multi-stage
|
||||
jobs.
|
||||
- **Worker & Progress** — queued work, progress reporting, and visibility.
|
||||
- **Human-in-the-Loop** — guardrails and approvals inside automations.
|
||||
- **Infra Adapters** — how infrastructure connects without leaking into domain.
|
||||
- **Genericity Matrix** — what is generic vs app-specific.
|
||||
- **Library Registry** — the catalog of internal libraries behind all of it.
|
||||
|
||||
Read the **Wiki** for the philosophy and rationale; these pages are the
|
||||
reference that mirrors the `standards` MCP bundle.
|
||||
66
standards/src/mkdocs.lib.yml
Normal file
66
standards/src/mkdocs.lib.yml
Normal file
@@ -0,0 +1,66 @@
|
||||
site_name: Aetos Standards — Reference
|
||||
site_description: The rules and library registry of the standardized application blueprint.
|
||||
docs_dir: lib
|
||||
site_dir: ../lib
|
||||
use_directory_urls: true
|
||||
|
||||
theme:
|
||||
name: material
|
||||
palette:
|
||||
- scheme: slate
|
||||
primary: deep purple
|
||||
accent: cyan
|
||||
font:
|
||||
text: Inter
|
||||
code: JetBrains Mono
|
||||
features:
|
||||
- navigation.sections
|
||||
- navigation.expand
|
||||
- navigation.top
|
||||
- navigation.instant
|
||||
- navigation.tracking
|
||||
- navigation.indexes
|
||||
- content.code.copy
|
||||
- content.code.annotate
|
||||
- content.tabs.link
|
||||
- content.action.edit
|
||||
- search.highlight
|
||||
- search.share
|
||||
- search.suggest
|
||||
|
||||
markdown_extensions:
|
||||
- pymdownx.superfences
|
||||
- pymdownx.inlinehilite
|
||||
- pymdownx.snippets
|
||||
- admonition
|
||||
- pymdownx.details
|
||||
- pymdownx.highlight:
|
||||
linenums: true
|
||||
anchor_linenums: true
|
||||
line_spans: __span
|
||||
pygments_lang_class: true
|
||||
- pymdownx.tabbed:
|
||||
alternate_style: true
|
||||
- pymdownx.tasklist:
|
||||
custom_checkbox: true
|
||||
- tables
|
||||
- footnotes
|
||||
- pymdownx.caret
|
||||
- pymdownx.tilde
|
||||
- pymdownx.mark
|
||||
|
||||
plugins:
|
||||
- search
|
||||
|
||||
nav:
|
||||
- Home: index.md
|
||||
- Layering Rules: 02_layering_rules.md
|
||||
- Composition Root: 03_composition_root.md
|
||||
- Spec-First Routing: 04_spec_first_routing.md
|
||||
- CRUD Template: 05_crud_template.md
|
||||
- Pipeline Scaffold: 06_pipeline_scaffold.md
|
||||
- Worker & Progress: 07_worker_and_progress.md
|
||||
- Human-in-the-Loop: 08_human_in_loop.md
|
||||
- Infra Adapters: 09_infra_adaptors.md
|
||||
- Genericity Matrix: 12_genericity_matrix.md
|
||||
- Library Registry: 13_library_registry.md
|
||||
59
standards/src/mkdocs.wiki.yml
Normal file
59
standards/src/mkdocs.wiki.yml
Normal file
@@ -0,0 +1,59 @@
|
||||
site_name: Aetos Standards — Wiki
|
||||
site_description: The philosophy and prompts behind the standardized application blueprint.
|
||||
docs_dir: wiki
|
||||
site_dir: ../wiki
|
||||
use_directory_urls: true
|
||||
|
||||
theme:
|
||||
name: material
|
||||
palette:
|
||||
- scheme: slate
|
||||
primary: deep purple
|
||||
accent: cyan
|
||||
font:
|
||||
text: Inter
|
||||
code: JetBrains Mono
|
||||
features:
|
||||
- navigation.sections
|
||||
- navigation.expand
|
||||
- navigation.top
|
||||
- navigation.instant
|
||||
- navigation.tracking
|
||||
- navigation.indexes
|
||||
- content.code.copy
|
||||
- content.code.annotate
|
||||
- content.tabs.link
|
||||
- content.action.edit
|
||||
- search.highlight
|
||||
- search.share
|
||||
- search.suggest
|
||||
|
||||
markdown_extensions:
|
||||
- pymdownx.superfences
|
||||
- pymdownx.inlinehilite
|
||||
- pymdownx.snippets
|
||||
- admonition
|
||||
- pymdownx.details
|
||||
- pymdownx.highlight:
|
||||
linenums: true
|
||||
anchor_linenums: true
|
||||
line_spans: __span
|
||||
pygments_lang_class: true
|
||||
- pymdownx.tabbed:
|
||||
alternate_style: true
|
||||
- pymdownx.tasklist:
|
||||
custom_checkbox: true
|
||||
- tables
|
||||
- footnotes
|
||||
- pymdownx.caret
|
||||
- pymdownx.tilde
|
||||
- pymdownx.mark
|
||||
|
||||
plugins:
|
||||
- search
|
||||
|
||||
nav:
|
||||
- Home: index.md
|
||||
- Overview: 01_overview.md
|
||||
- Testing: 10_testing.md
|
||||
- Build Recipe: 11_build_recipe.md
|
||||
54
standards/src/wiki/01_overview.md
Normal file
54
standards/src/wiki/01_overview.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Overview
|
||||
|
||||
## Summary
|
||||
|
||||
The blueprint is a **hexagonal (ports and adapters) application** with a
|
||||
**spec-first** web edge and a **single composition root**. It favors explicit
|
||||
wiring over implicit magic, and it prefers existing internal libraries over
|
||||
hand-written infrastructure.
|
||||
|
||||
---
|
||||
|
||||
## The three layers
|
||||
|
||||
```
|
||||
interfaces/ ← HTTP edge: thin handlers bound to an OpenAPI operationId
|
||||
domain/ ← business core: models, ports (interfaces), services, orchestrators
|
||||
infra/ ← adapters: databases, caches, external clients, hardware
|
||||
```
|
||||
|
||||
Plus two optional layers:
|
||||
|
||||
```
|
||||
pipelines/ ← data-driven transformation graphs (extraction, enrichment)
|
||||
scripts/ ← operator scripts that reuse the composition root
|
||||
```
|
||||
|
||||
## Core principles
|
||||
|
||||
1. **One-way dependencies.** `interfaces -> domain -> infra` is allowed;
|
||||
the reverse is forbidden. The domain never imports a framework it does not
|
||||
own.
|
||||
2. **Ports in the domain, adapters in infra.** A service depends on an
|
||||
abstract port; the composition root injects a concrete adapter.
|
||||
3. **One composition root.** Nothing constructs its own dependencies. All
|
||||
wiring lives in one module (`init.py`) and is exposed as a flat resource
|
||||
registry.
|
||||
4. **Spec-first.** The HTTP contract is declared in an OpenAPI document. Route
|
||||
handlers are looked up by `operationId`. There are no decorator routers.
|
||||
5. **Reuse the libraries.** Pipeline orchestration, DI, Mongo repositories,
|
||||
ingestion, auth, and docs are provided by internal libraries. Do not
|
||||
reimplement them — see the library registry.
|
||||
6. **Explicit state machines.** Long-running work moves through explicit,
|
||||
durable statuses and can pause for human input.
|
||||
|
||||
## Runtime processes
|
||||
|
||||
A standard app may run as up to three processes that share the same
|
||||
composition root:
|
||||
|
||||
| Process | Entry point | Responsibility |
|
||||
|---|---|---|
|
||||
| API | `main.py` | serve HTTP; start/stop resources via lifespan |
|
||||
| Worker | `worker.py` | poll durable jobs, run pipelines, publish progress |
|
||||
| Scripts | `python -m scripts.<name>` | one-shot operator tasks |
|
||||
56
standards/src/wiki/10_testing.md
Normal file
56
standards/src/wiki/10_testing.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Testing
|
||||
|
||||
## Summary
|
||||
|
||||
Two test suites: **integration** (the HTTP edge with fakes) and **unit** (pure
|
||||
domain and pipeline logic). Both run under pytest with asyncio auto mode.
|
||||
|
||||
---
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
tests/
|
||||
integration/ # TestClient(app) + mocked resources
|
||||
unit/ # models, converters, pipeline stages/roots
|
||||
conftest.py # fixtures
|
||||
```
|
||||
|
||||
## Integration tests
|
||||
|
||||
- Build the app with `TestClient`.
|
||||
- Replace `app.state.resources` with fake repositories/services so no database
|
||||
is required.
|
||||
- Patch the auth introspection call (`httpx.AsyncClient.post`) to return a
|
||||
canned identity.
|
||||
- Assert status codes and bodies for list/get/create/update/delete, plus
|
||||
`400`, `404`, `204`, and the streaming endpoint's fallback.
|
||||
|
||||
```python
|
||||
client = TestClient(app)
|
||||
app.state.resources = {"item_svc": FakeItemService(), ...}
|
||||
|
||||
def test_create_item():
|
||||
r = client.post("/items", json={"name": "x"})
|
||||
assert r.status_code == 200
|
||||
```
|
||||
|
||||
## Unit tests
|
||||
|
||||
- Domain models and converters: pure, no I/O.
|
||||
- Pipeline stages: feed a fixture state, assert the transformed state.
|
||||
- Assembled pipeline roots: **fixture parity** — replay a stored fixture input
|
||||
and compare against a golden output file.
|
||||
|
||||
## Config-consistency tests
|
||||
|
||||
For rule-driven pipelines, add a test that cross-references rules against the
|
||||
declared pattern registry: every flag emitted must be defined; every defined
|
||||
flag must be reachable. This catches drift without running the pipeline.
|
||||
|
||||
## Conventions
|
||||
|
||||
- `asyncio_mode = "auto"`; no manual event loops.
|
||||
- Fixtures are checked into the repo (inputs + golden outputs).
|
||||
- Tests skip (not fail) when a required fixture is absent.
|
||||
- Do not mock the domain; mock only the infrastructure edge.
|
||||
82
standards/src/wiki/11_build_recipe.md
Normal file
82
standards/src/wiki/11_build_recipe.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Build Recipe
|
||||
|
||||
## Summary
|
||||
|
||||
Follow this ordered checklist to generate a new application of the standard
|
||||
shape. Every step is deterministic; only steps 2 and 7 require app-specific
|
||||
authoring.
|
||||
|
||||
---
|
||||
|
||||
## 0. Query the registry
|
||||
|
||||
Before writing anything, read the **library registry** and note which internal
|
||||
libraries provide DB, DI, routing, ingestion, auth, and docs. Do not
|
||||
reimplement them.
|
||||
|
||||
## 1. Scaffold the repository
|
||||
|
||||
Copy the `templates/` skeleton. It provides: `pyproject.toml`, `openapi.yaml`,
|
||||
`main.py`, `init.py`, `worker.py`, `config.py`, `Dockerfile`,
|
||||
`docker-compose.yaml`, `.env.example`, and the package tree
|
||||
(`domain/ infra/ interfaces/ pipelines/ tests/ scripts/`).
|
||||
|
||||
## 2. Author the spec *(app-specific)*
|
||||
|
||||
Write `openapi.yaml`: resources, schemas, `operationId`s, security, and any
|
||||
extension metadata. This is the contract.
|
||||
|
||||
## 3. Write each CRUD slice *(app-specific)*
|
||||
|
||||
For each resource, create the four-file slice (see **CRUD Template**):
|
||||
`models.py`, `ports/db.py`, `ports/service.py`, optional `converters.py`.
|
||||
|
||||
## 4. Wire the composition root
|
||||
|
||||
In `init.py`: build leaf repositories, then dependent repositories, then
|
||||
services; add infrastructure adaptors (cache store, progress bus, stores,
|
||||
fetchers). Add `get_svc` overloads.
|
||||
|
||||
## 5. Add routes
|
||||
|
||||
Create `interfaces/<resource>/routes.py` (thin handlers) and re-export them in
|
||||
`interfaces/openapi.py`. Add a cache decorator to read endpoints.
|
||||
|
||||
## 6. Bootstrap the app and worker
|
||||
|
||||
Confirm `main.py` wires the OpenAPI-first app with the lifespan. If the app has
|
||||
long-running jobs, adapt `worker.py` to poll the relevant job collection.
|
||||
|
||||
## 7. Add a pipeline family *(only if data-driven)*
|
||||
|
||||
Create `pipelines/<family>/` with `abc.py`, `impl.py`, `pipeline.yaml`,
|
||||
`resources/`, and register the root in `registry.py`. Add fixtures and a parity
|
||||
test.
|
||||
|
||||
## 8. Add infrastructure as needed
|
||||
|
||||
Only where the standard adaptors do not cover the need. Prefer library-backed
|
||||
adapters.
|
||||
|
||||
## 9. Tests
|
||||
|
||||
Add integration tests with fakes and unit tests for domain/pipeline logic; add
|
||||
a config-consistency test if using rule-driven pipelines.
|
||||
|
||||
## 10. Scripts and seeds
|
||||
|
||||
Add operator scripts under `scripts/` that use `init()` / `get_svc()` /
|
||||
`shutdown()`.
|
||||
|
||||
## 11. Docs
|
||||
|
||||
Author GSDFC docstrings, generate the type stubs (`.pyi`), mark the package
|
||||
`py.typed`, and build docs.
|
||||
|
||||
## Definition of done
|
||||
|
||||
- `python -c "import main"` succeeds.
|
||||
- `pytest` passes.
|
||||
- The app serves the spec's operations; the worker drains its queue.
|
||||
- No domain module imports a web framework.
|
||||
- No infrastructure was reimplemented that a library already provides.
|
||||
45
standards/src/wiki/index.md
Normal file
45
standards/src/wiki/index.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# The Aetos Standard App Blueprint
|
||||
|
||||
A single, reusable architecture for backend applications in the Aetos ecosystem.
|
||||
|
||||
Every application built with this blueprint has the same shape:
|
||||
|
||||
- a **spec-first HTTP layer** driven by an OpenAPI document;
|
||||
- a **domain layer** of pure models, ports, and services;
|
||||
- an **infrastructure layer** of adapters (MongoDB, cache, workers, LLM, mail);
|
||||
- a **composition root** that wires everything in exactly one place;
|
||||
- an optional **pipeline layer** for data-driven extraction/enrichment;
|
||||
- an optional **background worker** that runs long-running jobs and reports progress.
|
||||
|
||||
> This blueprints is deliberately **app-agnostic**. It never names a concrete
|
||||
> business domain. Any application — expenses, health metrics, inventory,
|
||||
> analytics — is produced by filling in **two axes only**: the resource model
|
||||
> and (if needed) the pipeline family.
|
||||
|
||||
## How to read this
|
||||
|
||||
| Page | What it covers |
|
||||
|---|---|
|
||||
| Overview | The philosophy and the one-way dependency rule |
|
||||
| Layering Rules | Which package may import which |
|
||||
| Composition Root | The single wiring point |
|
||||
| Spec-First Routing | OpenAPI as the source of truth |
|
||||
| CRUD Template | The canonical resource slice |
|
||||
| Pipeline Scaffold | Data-driven processing families |
|
||||
| Worker & Progress | Async jobs, status machine, streaming progress |
|
||||
| Human-in-the-Loop | Pausing a job to ask a question |
|
||||
| Infra Adapters | MongoDB, cache, stores, LLM, mail |
|
||||
| Testing | Integration + unit conventions |
|
||||
| Build Recipe | The ordered checklist to generate a new app |
|
||||
| Genericity Matrix | What is reusable vs what you must write |
|
||||
|
||||
## For AI agents
|
||||
|
||||
This content is served as MCP resources under `docs://index`, `docs://nav`,
|
||||
and `docs://modules/{module}`. To build a new app:
|
||||
|
||||
1. Read `build_recipe` first.
|
||||
2. Read `genericity_matrix` to know what you must author.
|
||||
3. Query the **library registry** (`find_library`) before writing any
|
||||
infrastructure yourself.
|
||||
4. Copy the `templates/` skeleton and fill the two app-specific axes.
|
||||
54
standards/templates/README.md
Normal file
54
standards/templates/README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Application Template
|
||||
|
||||
A minimal, runnable skeleton of the standardized architecture. It demonstrates
|
||||
both axes of the blueprint (see `standards`):
|
||||
|
||||
1. **Resource axis** — items and jobs, each a Claude-style generic CRUD slice
|
||||
(model → document → repo → service → route), all driven from `openapi.yaml`.
|
||||
2. **Pipeline axis** — a background `worker.py` that polls pending jobs and runs
|
||||
`pipelines/item_sync.py`, a deterministic async step chain.
|
||||
|
||||
Everything here is deliberately small so an agent can rewrite each file rather
|
||||
than delete unused code. The domain is a neutral "item", not an app noun.
|
||||
|
||||
## Instantiate a new app
|
||||
|
||||
cp -r templates/app apps/<your-app>
|
||||
cd apps/<your-app>
|
||||
|
||||
1. `pyproject.toml` — rename the package; keep the internal library pins.
|
||||
2. `config.py` — replace/add settings consumed by your domain.
|
||||
3. `openapi.yaml` — replace the `Item`/`Job` resources with your resources.
|
||||
4. `domain/` — rename `item` and `job` slices to your aggregates.
|
||||
5. `pipelines/item_sync.py` — replace the demo steps with your pipeline.
|
||||
6. `tests/` — keep the two test files as the pattern for your own.
|
||||
|
||||
Run the API:
|
||||
|
||||
pip install -e ".[dev]"
|
||||
uvicorn main:app --reload
|
||||
|
||||
Run the worker:
|
||||
|
||||
python worker.py
|
||||
|
||||
## What to keep, what to replace
|
||||
|
||||
| Keep | Replace / rename |
|
||||
|------------------------------|---------------------------------------|
|
||||
| `config.py` shape | Settings fields |
|
||||
| `init.py` wiring pattern | Resource names |
|
||||
| openapi-first routing | Paths, schemas, operationIds |
|
||||
| mongo-ops repo factory | Collections, doc models |
|
||||
| generic CRUD adaptor | Nothing (reuse as-is) |
|
||||
| worker poll + status loop | Job payloads, pipeline steps |
|
||||
|
||||
## Where the internal libraries slot in
|
||||
|
||||
- `openapi_first.app.OpenAPIFirstApp` — main.py, interfaces/openapi.py
|
||||
- `mongo_ops` — infra/db/setup.py, infra/db/repos/mongo.py, domain/*/ports/db.py
|
||||
- `hexa.build` — domain/*/ports/service.py
|
||||
- `dagpipe` — pipelines/ (wrap step chains in a DAG once they branch)
|
||||
|
||||
Confirm current endpoints and ownership with the `standards` registry MCP tool:
|
||||
`find_library("pipeline")`, `find_library("auth")`, etc.
|
||||
8
standards/templates/app/.dockerignore
Normal file
8
standards/templates/app/.dockerignore
Normal file
@@ -0,0 +1,8 @@
|
||||
.venv/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.env
|
||||
.git/
|
||||
docs/
|
||||
dist/
|
||||
build/
|
||||
5
standards/templates/app/.env.example
Normal file
5
standards/templates/app/.env.example
Normal file
@@ -0,0 +1,5 @@
|
||||
DEBUG=false
|
||||
MONGO_URI=mongodb://localhost:27017
|
||||
MONGO_DB=app_template
|
||||
WORKER_POLL_SECONDS=30
|
||||
WORKER_MAX_RETRIES=3
|
||||
15
standards/templates/app/Dockerfile
Normal file
15
standards/templates/app/Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY pyproject.toml ./
|
||||
COPY . .
|
||||
RUN pip install --no-cache-dir --upgrade pip \
|
||||
&& pip install --no-cache-dir .
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
# Two processes, per the two axes:
|
||||
# - main.py : the spec-first FastAPI application
|
||||
# - worker.py : the pipeline consumer polling pending jobs
|
||||
CMD ["sh", "-c", "uvicorn main:app --host 0.0.0.0 --port 8000 & exec python worker.py"]
|
||||
36
standards/templates/app/config.py
Normal file
36
standards/templates/app/config.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""# Summary
|
||||
|
||||
App configuration loaded from environment — a Settings dataclass with defaults
|
||||
plus a singleton settings instance populated from OS environment variables at
|
||||
import time.
|
||||
"""
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Settings:
|
||||
"""Runtime configuration settings for the application.
|
||||
|
||||
Attributes:
|
||||
debug (bool): Enable debug behavior. Defaults to False.
|
||||
mongo_uri (str): MongoDB connection URI. Defaults to mongodb://localhost:27017.
|
||||
mongo_db (str): MongoDB database name. Defaults to app_template.
|
||||
worker_poll_seconds (int): Worker poll interval in seconds. Defaults to 30.
|
||||
worker_max_retries (int): Maximum job retries before skipping. Defaults to 3.
|
||||
"""
|
||||
|
||||
debug: bool = False
|
||||
mongo_uri: str = "mongodb://localhost:27017"
|
||||
mongo_db: str = "app_template"
|
||||
worker_poll_seconds: int = 30
|
||||
worker_max_retries: int = 3
|
||||
|
||||
|
||||
settings = Settings(
|
||||
debug=os.environ.get("DEBUG", "").lower() in ("1", "true", "yes"),
|
||||
mongo_uri=os.environ.get("MONGO_URI", "mongodb://localhost:27017"),
|
||||
mongo_db=os.environ.get("MONGO_DB", "app_template"),
|
||||
worker_poll_seconds=int(os.environ.get("WORKER_POLL_SECONDS", "30")),
|
||||
worker_max_retries=int(os.environ.get("WORKER_MAX_RETRIES", "3")),
|
||||
)
|
||||
21
standards/templates/app/docker-compose.yaml
Normal file
21
standards/templates/app/docker-compose.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
services:
|
||||
api:
|
||||
build: .
|
||||
command: uvicorn main:app --host 0.0.0.0 --port 8000
|
||||
env_file: .env
|
||||
ports:
|
||||
- "8000:8000"
|
||||
depends_on:
|
||||
- mongo
|
||||
|
||||
worker:
|
||||
build: .
|
||||
command: python worker.py
|
||||
env_file: .env
|
||||
depends_on:
|
||||
- mongo
|
||||
|
||||
mongo:
|
||||
image: mongo:7
|
||||
ports:
|
||||
- "27017:27017"
|
||||
0
standards/templates/app/domain/__init__.py
Normal file
0
standards/templates/app/domain/__init__.py
Normal file
0
standards/templates/app/domain/item/__init__.py
Normal file
0
standards/templates/app/domain/item/__init__.py
Normal file
19
standards/templates/app/domain/item/models.py
Normal file
19
standards/templates/app/domain/item/models.py
Normal file
@@ -0,0 +1,19 @@
|
||||
"""# Summary
|
||||
|
||||
Item domain model — the neutral resource used throughout the template.
|
||||
"""
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
"""A single countable resource.
|
||||
|
||||
Attributes:
|
||||
name (str): Item name.
|
||||
quantity (int): Item quantity. Defaults to 1.
|
||||
"""
|
||||
|
||||
name: str
|
||||
quantity: int = Field(default=1)
|
||||
|
||||
model_config = {"extra": "ignore"}
|
||||
20
standards/templates/app/domain/item/ports/db.py
Normal file
20
standards/templates/app/domain/item/ports/db.py
Normal file
@@ -0,0 +1,20 @@
|
||||
"""# Summary
|
||||
|
||||
Mongo persistence for items: document schema and repository factory.
|
||||
"""
|
||||
from domain.item.models import Item
|
||||
from infra.db.repos.mongo import _repo_factory as mongo_repo_factory
|
||||
from mongo_ops import BaseDocument, BaseRepository
|
||||
|
||||
|
||||
class ItemDocument(Item, BaseDocument):
|
||||
"""Mongo document form of :class:`Item`."""
|
||||
|
||||
|
||||
def create_item_repo() -> BaseRepository:
|
||||
"""Create the Mongo-backed item repository.
|
||||
|
||||
Returns:
|
||||
BaseRepository: Repository wired to the ``items`` collection.
|
||||
"""
|
||||
return mongo_repo_factory("items", ItemDocument)
|
||||
33
standards/templates/app/domain/item/ports/service.py
Normal file
33
standards/templates/app/domain/item/ports/service.py
Normal file
@@ -0,0 +1,33 @@
|
||||
"""# Summary
|
||||
|
||||
CRUD service wiring for the item aggregate via the hexa container.
|
||||
"""
|
||||
from hexa import build
|
||||
|
||||
from domain.item.models import Item
|
||||
from domain.item.ports.db import ItemDocument
|
||||
from infra.adapters.service import CrudsServiceAdaptor
|
||||
|
||||
|
||||
class ItemCrudsService(CrudsServiceAdaptor[Item, ItemDocument]):
|
||||
"""Item CRUD service resolving :class:`ItemDocument` from a repo.
|
||||
|
||||
Notes:
|
||||
Instances are created by :func:`get_item_service`. The ``model`` and
|
||||
``schema`` class attributes bind the domain model and its document type.
|
||||
"""
|
||||
|
||||
model = Item
|
||||
schema = ItemDocument
|
||||
|
||||
|
||||
def get_item_service(repo) -> ItemCrudsService:
|
||||
"""Build an item CRUD service wired to ``repo``.
|
||||
|
||||
Args:
|
||||
repo (Any): Mongo-backed item repository from :func:`create_item_repo`.
|
||||
|
||||
Returns:
|
||||
ItemCrudsService: Ready-to-use item CRUD service.
|
||||
"""
|
||||
return build(ItemCrudsService, instances={"repo": repo})
|
||||
0
standards/templates/app/domain/job/__init__.py
Normal file
0
standards/templates/app/domain/job/__init__.py
Normal file
26
standards/templates/app/domain/job/models.py
Normal file
26
standards/templates/app/domain/job/models.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""# Summary
|
||||
|
||||
Job domain model — a pipeline work item consumed by the background worker.
|
||||
"""
|
||||
from typing import Literal
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class Job(BaseModel):
|
||||
"""A unit of background work.
|
||||
|
||||
Attributes:
|
||||
name (str): Job name.
|
||||
status (str): Lifecycle status. Defaults to "pending".
|
||||
retry_count (int): Times the job failed. Defaults to 0.
|
||||
error_message (str | None): Last failure message. Defaults to None.
|
||||
payload (dict | None): Arbitrary job payload. Defaults to None.
|
||||
"""
|
||||
|
||||
name: str
|
||||
status: Literal["pending", "processing", "done", "failed"] = "pending"
|
||||
retry_count: int = Field(default=0)
|
||||
error_message: str | None = None
|
||||
payload: dict | None = None
|
||||
|
||||
model_config = {"extra": "ignore"}
|
||||
20
standards/templates/app/domain/job/ports/db.py
Normal file
20
standards/templates/app/domain/job/ports/db.py
Normal file
@@ -0,0 +1,20 @@
|
||||
"""# Summary
|
||||
|
||||
Mongo persistence for jobs: document schema and repository factory.
|
||||
"""
|
||||
from domain.job.models import Job
|
||||
from infra.db.repos.mongo import _repo_factory as mongo_repo_factory
|
||||
from mongo_ops import BaseDocument, BaseRepository
|
||||
|
||||
|
||||
class JobDocument(Job, BaseDocument):
|
||||
"""Mongo document form of :class:`Job`."""
|
||||
|
||||
|
||||
def create_job_repo() -> BaseRepository:
|
||||
"""Create the Mongo-backed job repository.
|
||||
|
||||
Returns:
|
||||
BaseRepository: Repository wired to the ``jobs`` collection.
|
||||
"""
|
||||
return mongo_repo_factory("jobs", JobDocument)
|
||||
33
standards/templates/app/domain/job/ports/service.py
Normal file
33
standards/templates/app/domain/job/ports/service.py
Normal file
@@ -0,0 +1,33 @@
|
||||
"""# Summary
|
||||
|
||||
CRUD service wiring for the job aggregate via the hexa container.
|
||||
"""
|
||||
from hexa import build
|
||||
|
||||
from domain.job.models import Job
|
||||
from domain.job.ports.db import JobDocument
|
||||
from infra.adapters.service import CrudsServiceAdaptor
|
||||
|
||||
|
||||
class JobCrudsService(CrudsServiceAdaptor[Job, JobDocument]):
|
||||
"""Job CRUD service resolving :class:`JobDocument` from a repo.
|
||||
|
||||
Notes:
|
||||
Instances are created by :func:`get_job_service`. The ``model`` and
|
||||
``schema`` class attributes bind the domain model and its document type.
|
||||
"""
|
||||
|
||||
model = Job
|
||||
schema = JobDocument
|
||||
|
||||
|
||||
def get_job_service(repo) -> JobCrudsService:
|
||||
"""Build a job CRUD service wired to ``repo``.
|
||||
|
||||
Args:
|
||||
repo (Any): Mongo-backed job repository from :func:`create_job_repo`.
|
||||
|
||||
Returns:
|
||||
JobCrudsService: Ready-to-use job CRUD service.
|
||||
"""
|
||||
return build(JobCrudsService, instances={"repo": repo})
|
||||
0
standards/templates/app/infra/__init__.py
Normal file
0
standards/templates/app/infra/__init__.py
Normal file
0
standards/templates/app/infra/adapters/__init__.py
Normal file
0
standards/templates/app/infra/adapters/__init__.py
Normal file
128
standards/templates/app/infra/adapters/service.py
Normal file
128
standards/templates/app/infra/adapters/service.py
Normal file
@@ -0,0 +1,128 @@
|
||||
"""# Summary
|
||||
|
||||
Generic CRUD service adaptor over a Mongo repository.
|
||||
|
||||
Bridge pydantic models and mongo-ops documents, exposing create, read, update,
|
||||
patch, delete, and search operations for every domain that needs them.
|
||||
"""
|
||||
from typing import Generic, Type, TypeVar
|
||||
|
||||
from infra.db.repos import mongo
|
||||
|
||||
from pydantic import BaseModel
|
||||
from mongo_ops import BaseDocument
|
||||
from mongo_ops.models import PyObjectId
|
||||
|
||||
Model = TypeVar("Model", bound=BaseModel)
|
||||
Schema = TypeVar("Schema", bound=BaseDocument)
|
||||
|
||||
|
||||
class CrudsServiceAdaptor(Generic[Model, Schema]):
|
||||
"""Adapt a generic Mongo repository to a model/schema CRUD service.
|
||||
|
||||
Attributes:
|
||||
repo (mongo.BaseRepository): Repository backing all CRUD operations.
|
||||
model (Type[Model]): Pydantic model type handled by this service.
|
||||
schema (Type[Schema]): Document schema type persisted by the repository.
|
||||
"""
|
||||
|
||||
repo: mongo.BaseRepository
|
||||
model: Type[Model]
|
||||
schema: Type[Schema]
|
||||
|
||||
def __init__(self, *, repo=None):
|
||||
"""Initialize the adaptor with a backing repository.
|
||||
|
||||
Args:
|
||||
repo (mongo.BaseRepository): Repository used for all operations.
|
||||
"""
|
||||
self.repo = repo
|
||||
|
||||
@staticmethod
|
||||
def model_to_dict(model_obj: Model) -> dict:
|
||||
"""Serialize a model into a sparse dictionary.
|
||||
|
||||
Args:
|
||||
model_obj (Model): Model instance to serialize.
|
||||
|
||||
Returns:
|
||||
dict: Fields that are set and not ``None``.
|
||||
"""
|
||||
return model_obj.model_dump(exclude_unset=True, exclude_none=True)
|
||||
|
||||
async def create(self, data: Model | dict) -> Schema:
|
||||
"""Create a document from a model or raw mapping.
|
||||
|
||||
Args:
|
||||
data (Model | dict): Model instance or field mapping to persist.
|
||||
|
||||
Returns:
|
||||
Schema: Persisted document.
|
||||
"""
|
||||
if isinstance(data, dict):
|
||||
data = self.schema(**data)
|
||||
return await self.repo.create(data)
|
||||
|
||||
async def read(self, id: PyObjectId) -> Schema | None:
|
||||
"""Read a document by primary key.
|
||||
|
||||
Args:
|
||||
id (PyObjectId): Document identifier.
|
||||
|
||||
Returns:
|
||||
Schema | None: Matching document, or ``None`` when absent.
|
||||
"""
|
||||
return await self.repo.get_by_id(id)
|
||||
|
||||
async def update(self, id: PyObjectId, data: Model | dict) -> Schema | None:
|
||||
"""Replace a document by primary key.
|
||||
|
||||
Args:
|
||||
id (PyObjectId): Document identifier.
|
||||
data (Model | dict): Replacement model or field mapping.
|
||||
|
||||
Returns:
|
||||
Schema | None: Updated document, or ``None`` when absent.
|
||||
"""
|
||||
return await self.repo.update(
|
||||
id,
|
||||
data if isinstance(data, dict) else self.model_to_dict(data),
|
||||
)
|
||||
|
||||
async def patch(self, id: PyObjectId, data: dict) -> Schema | None:
|
||||
"""Partially update a document by primary key.
|
||||
|
||||
Args:
|
||||
id (PyObjectId): Document identifier.
|
||||
data (dict): Fields to patch.
|
||||
|
||||
Returns:
|
||||
Schema | None: Updated document, or ``None`` when absent.
|
||||
"""
|
||||
return await self.repo.patch(id, data)
|
||||
|
||||
async def delete(self, id: PyObjectId) -> bool:
|
||||
"""Delete a document by primary key.
|
||||
|
||||
Args:
|
||||
id (PyObjectId): Document identifier.
|
||||
|
||||
Returns:
|
||||
bool: ``True`` when a document was deleted.
|
||||
"""
|
||||
return await self.repo.delete(id)
|
||||
|
||||
async def search(self, filter: dict | None = None, *args, **kwargs) -> list[Schema]:
|
||||
"""List documents matching an optional filter.
|
||||
|
||||
Args:
|
||||
filter (dict | None): Query filter passed to the repository.
|
||||
|
||||
Returns:
|
||||
list[Schema]: Matching documents.
|
||||
|
||||
Notes:
|
||||
Extra positional and keyword arguments are forwarded to the
|
||||
repository.
|
||||
"""
|
||||
return await self.repo.get_many(filter=filter, *args, **kwargs)
|
||||
0
standards/templates/app/infra/db/__init__.py
Normal file
0
standards/templates/app/infra/db/__init__.py
Normal file
0
standards/templates/app/infra/db/repos/__init__.py
Normal file
0
standards/templates/app/infra/db/repos/__init__.py
Normal file
42
standards/templates/app/infra/db/repos/mongo.py
Normal file
42
standards/templates/app/infra/db/repos/mongo.py
Normal file
@@ -0,0 +1,42 @@
|
||||
"""# Summary
|
||||
|
||||
Generic MongoDB CRUD repo — single implementation for all domains.
|
||||
|
||||
Select a concrete mongo-ops repository (base, cached, or populating) from the
|
||||
options supplied to the factory.
|
||||
"""
|
||||
from typing import List, Optional, Type
|
||||
|
||||
from mongo_ops import BaseRepository, CachedBaseRepository, PopulatingRepository
|
||||
from mongo_ops.cache import CacheBackend, CacheConfig
|
||||
from mongo_ops.models import BaseDocument
|
||||
from mongo_ops.populate import PopulationEngine, PopulateRule
|
||||
|
||||
|
||||
def _repo_factory(
|
||||
collection: str,
|
||||
db_model: Type[BaseDocument],
|
||||
cache_backend: Optional[CacheBackend] = None,
|
||||
cache_config: Optional[CacheConfig] = None,
|
||||
engine: Optional[PopulationEngine] = None,
|
||||
populate_rules: Optional[List[PopulateRule]] = None,
|
||||
) -> BaseRepository:
|
||||
"""Build the appropriate mongo-ops repository.
|
||||
|
||||
Args:
|
||||
collection (str): Collection name to operate on.
|
||||
db_model (Type[BaseDocument]): Document model bound to the collection.
|
||||
cache_backend (Optional[CacheBackend]): Cache backend enabling cached reads.
|
||||
cache_config (Optional[CacheConfig]): Cache configuration for the backend.
|
||||
engine (Optional[PopulationEngine]): Population engine for reference expansion.
|
||||
populate_rules (Optional[List[PopulateRule]]): Rules applied by the engine.
|
||||
|
||||
Returns:
|
||||
BaseRepository: Repository instance matching the supplied options.
|
||||
"""
|
||||
if engine and populate_rules is not None:
|
||||
return PopulatingRepository(collection, db_model, engine, populate_rules)
|
||||
elif cache_backend is not None:
|
||||
return CachedBaseRepository(collection, db_model, cache_backend, cache_config)
|
||||
else:
|
||||
return BaseRepository(collection, db_model)
|
||||
23
standards/templates/app/infra/db/setup.py
Normal file
23
standards/templates/app/infra/db/setup.py
Normal file
@@ -0,0 +1,23 @@
|
||||
"""# Summary
|
||||
|
||||
Database setup — connection lifecycle only. No domain imports.
|
||||
|
||||
Connect and disconnect the shared Mongo connection manager at application
|
||||
startup and shutdown.
|
||||
"""
|
||||
from mongo_ops import MongoConnectionManager
|
||||
|
||||
|
||||
async def setup_database(uri: str, db_name: str):
|
||||
"""Connect the shared Mongo connection manager.
|
||||
|
||||
Args:
|
||||
uri (str): Mongo connection URI.
|
||||
db_name (str): Target database name.
|
||||
"""
|
||||
await MongoConnectionManager.connect(uri=uri, db_name=db_name)
|
||||
|
||||
|
||||
async def shutdown_database():
|
||||
"""Disconnect the shared Mongo connection manager."""
|
||||
await MongoConnectionManager.disconnect()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user