diff --git a/brand/build/projects.py b/brand/build/projects.py
index ccce029..3a47edb 100644
--- a/brand/build/projects.py
+++ b/brand/build/projects.py
@@ -21,6 +21,7 @@
"modern-di-fastapi": lambda: sym.bolt_disc(_CX, _CY, R),
"modern-di-litestar": lambda: sym.star_disc(_CX, _CY, R),
"modern-di-starlette": lambda: sym.sparkle_cluster(_CX, _CY, R),
+ "modern-di-aiohttp": lambda: sym.async_loop(_CX, _CY, R),
"modern-di-faststream": lambda: sym.faststream(_CX, _CY, R),
"modern-di-typer": lambda: sym.terminal(_CX, _CY, R),
"modern-di-pytest": lambda: sym.bars(_CX, _CY, R),
diff --git a/brand/build/symbols.py b/brand/build/symbols.py
index e538d8e..b48db14 100644
--- a/brand/build/symbols.py
+++ b/brand/build/symbols.py
@@ -84,7 +84,9 @@ def sparkle_cluster(cx: float, cy: float, r: float) -> str:
return big + small
-def _circ_arc(cx: float, cy: float, rad: float, a0: float, a1: float, w: float) -> str:
+def _circ_arc(
+ cx: float, cy: float, rad: float, a0: float, a1: float, w: float, color: str = GOLD
+) -> str:
"""Clockwise arc a0->a1 (deg, increasing) with a leading arrowhead at a1."""
a1s = a1 - 7 # stop the stroke short so the head caps it cleanly
x0 = cx + rad * math.cos(math.radians(a0))
@@ -94,7 +96,7 @@ def _circ_arc(cx: float, cy: float, rad: float, a0: float, a1: float, w: float)
large = 1 if (a1s - a0) % 360 > 180 else 0
d = (
f''
+ f'fill="none" stroke="{color}" stroke-width="{w}" stroke-linecap="butt"/>'
)
ex = cx + rad * math.cos(math.radians(a1))
ey = cy + rad * math.sin(math.radians(a1))
@@ -108,11 +110,22 @@ def _circ_arc(cx: float, cy: float, rad: float, a0: float, a1: float, w: float)
d += (
f''
+ f'{base[0] - width * px:.1f},{base[1] - width * py:.1f}" fill="{color}"/>'
)
return d
+def async_loop(cx: float, cy: float, r: float) -> str:
+ """aiohttp cue: an async event-loop cycle (two chasing arrows) knocked out
+ of a gold disc."""
+ rad = r * 0.52
+ w = 3.4
+ loop = _circ_arc(cx, cy, rad, 25, 165, w, color=CREAM) + _circ_arc(
+ cx, cy, rad, 205, 345, w, color=CREAM
+ )
+ return f'' + loop
+
+
FASTSTREAM_PATH = (
"m499.61,356.87l-92.61-160.41-36.48-63.19-10.46,251.02c.07,2.86-.78,6.05-2.51,8.6"
"-2.98,4.41-7.42,5.31-9.92,2.02l.02-.03-68.85-90.48-107.13,38.09v.04c-3.89,1.38-7.11"
diff --git a/brand/projects/modern-di-aiohttp/lockup-dark.svg b/brand/projects/modern-di-aiohttp/lockup-dark.svg
new file mode 100644
index 0000000..f3e7c88
--- /dev/null
+++ b/brand/projects/modern-di-aiohttp/lockup-dark.svg
@@ -0,0 +1 @@
+
diff --git a/brand/projects/modern-di-aiohttp/lockup-light.svg b/brand/projects/modern-di-aiohttp/lockup-light.svg
new file mode 100644
index 0000000..d7b06ba
--- /dev/null
+++ b/brand/projects/modern-di-aiohttp/lockup-light.svg
@@ -0,0 +1 @@
+
diff --git a/brand/projects/modern-di-aiohttp/lockup.png b/brand/projects/modern-di-aiohttp/lockup.png
new file mode 100644
index 0000000..495d324
Binary files /dev/null and b/brand/projects/modern-di-aiohttp/lockup.png differ
diff --git a/brand/projects/modern-di-aiohttp/mark-1024.png b/brand/projects/modern-di-aiohttp/mark-1024.png
new file mode 100644
index 0000000..553d645
Binary files /dev/null and b/brand/projects/modern-di-aiohttp/mark-1024.png differ
diff --git a/brand/projects/modern-di-aiohttp/mark-512.png b/brand/projects/modern-di-aiohttp/mark-512.png
new file mode 100644
index 0000000..860ed4b
Binary files /dev/null and b/brand/projects/modern-di-aiohttp/mark-512.png differ
diff --git a/brand/projects/modern-di-aiohttp/mark.svg b/brand/projects/modern-di-aiohttp/mark.svg
new file mode 100644
index 0000000..7dcfdbd
--- /dev/null
+++ b/brand/projects/modern-di-aiohttp/mark.svg
@@ -0,0 +1 @@
+
diff --git a/planning/changes/2026-07-02.02-aiohttp-brand/design.md b/planning/changes/2026-07-02.02-aiohttp-brand/design.md
new file mode 100644
index 0000000..2d1f937
--- /dev/null
+++ b/planning/changes/2026-07-02.02-aiohttp-brand/design.md
@@ -0,0 +1,125 @@
+---
+summary: Mint the modern-di-aiohttp brand mark (async event-loop in a gold disc) and generate its assets; public surfaces deferred until the repo ships.
+---
+
+# Design: modern-di-aiohttp brand mark
+
+## Summary
+
+`modern-di-aiohttp` is a new DI integration, still in development — its GitHub
+repo is not public and the package is not on PyPI yet. This change mints its
+brand mark (an **async event-loop cycle knocked out of a gold disc**) and
+generates the six per-project asset files in this repo (`.github`), so they are
+ready when the repo goes public. The public-facing surfaces (profile README row,
+docs-site bullet, GitHub repo settings) are deliberately **deferred** — they
+show PyPI/Downloads badges and link to a public repo that does not exist yet.
+
+## Motivation
+
+Every mainstream web-framework integration in the org has a brand mark
+(`modern-di-fastapi` bolt-in-disc, `modern-di-litestar` star-in-disc,
+`modern-di-starlette` sparkle). Preparing aiohttp's mark now means the asset
+URLs resolve the moment its repo publishes its README, with no scramble.
+
+We reviewed aiohttp's real logo (a node/network graph inside a ring). We
+deliberately did **not** adapt it: the node-graph motif is already how
+`modern-di` and `that-depends` read in this same lineup, so a faithful copy
+would collide. The chosen mark keeps the async meaning and joins the
+disc-family (FastAPI/Litestar), which is visually distinct from the DI
+graph marks.
+
+## Non-goals
+
+- No changes to the (not-yet-public) `modern-di-aiohttp` repo.
+- **No** profile README row, docs-site bullet, or GitHub repo settings — these
+ wait until the repo is public (tracked under "Deferred follow-ups" below).
+- No social card: integrations are not in `DOCS_REPOS`.
+
+## Design
+
+### 1. Async-loop symbol — `brand/build/symbols.py`
+
+Add `async_loop(cx, cy, r)`: two cream arrows chasing each other in a circular
+loop (the universal "async / event-loop" cycle), knocked out of a gold disc.
+It reuses the existing `_circ_arc` arc-with-arrowhead helper.
+
+`_circ_arc` currently hardcodes `GOLD` for both its stroke and its arrowhead
+fill. Extend its signature with a trailing optional `color: str = GOLD` and
+substitute `{color}` for the two `{GOLD}` occurrences. This is backward
+compatible — the only existing caller (`db_retry`) omits the argument and keeps
+`GOLD`.
+
+```python
+def _circ_arc(cx: float, cy: float, rad: float, a0: float, a1: float, w: float,
+ color: str = GOLD) -> str:
+ ... # stroke and arrowhead now use {color} instead of {GOLD}
+
+
+def async_loop(cx: float, cy: float, r: float) -> str:
+ """aiohttp cue: an async event-loop cycle (two chasing arrows) knocked out
+ of a gold disc."""
+ rad = r * 0.52
+ w = 3.4
+ loop = (
+ _circ_arc(cx, cy, rad, 25, 165, w, color=CREAM)
+ + _circ_arc(cx, cy, rad, 205, 345, w, color=CREAM)
+ )
+ return f'' + loop
+```
+
+`CREAM` is already imported in `symbols.py`. The mark uses only `GOLD` and
+`CREAM`, both in the allowed palette, so `test_only_allowed_colours` passes.
+
+### 2. Manifest entry — `brand/build/projects.py`
+
+Add `"modern-di-aiohttp": lambda: sym.async_loop(_CX, _CY, R)` to `MANIFEST`,
+in the dependency-injection block, immediately after `modern-di-starlette`
+(keeps the web-framework integrations fastapi -> litestar -> starlette ->
+aiohttp adjacent).
+
+### 3. Test — `tests/test_projects.py`
+
+Add `"modern-di-aiohttp"` to `EXPECTED_REPOS`. That set is asserted equal to
+`MANIFEST`, and the valid-SVG / allowed-colours / render tests are parametrized
+off it, so no other test edits are required. `DOCS_EXPECTED` is unchanged.
+
+### 4. Regenerate assets — `just sync-assets`
+
+Produces `brand/projects/modern-di-aiohttp/{mark.svg, mark-512.png,
+mark-1024.png, lockup-light.svg, lockup-dark.svg, lockup.png}`. Commit the
+generated files.
+
+## Deferred follow-ups (when the repo is public)
+
+Ship these in a later change once `github.com/modern-python/modern-di-aiohttp`
+exists and the package is on PyPI:
+
+- Profile README row in the Dependency injection table.
+- Docs-site bullet in the DI list (+ mention in the stack sentence).
+- GitHub repo settings: description `modern-di integration for aiohttp`,
+ homepage `https://modern-di.modern-python.org`, topics
+ `python, dependency-injection, di, ioc-container, modern-di, aiohttp`.
+
+The canonical one-liner for all of the above will be
+`modern-di integration for aiohttp`.
+
+## Testing
+
+- `just test` green, in particular `tests/test_projects.py`:
+ `test_manifest_covers_every_repo`, `test_only_allowed_colours`,
+ `test_project_mark_is_valid_svg`, and the render tests.
+- Visually inspect the regenerated `mark.svg` and light/dark lockups for the
+ aiohttp entry.
+- `just sync-assets` re-run is a no-op diff apart from the six new files
+ (deterministic render).
+- `just check-planning` passes before pushing.
+
+## Risk
+
+- **Low: async_loop reads too close to db-retry's retry arcs** (both are
+ arced arrows). Mitigated — db-retry is a single retry arc around a database
+ cylinder; async_loop is two symmetric arrows forming a closed cycle on a
+ plain disc. Different enough; confirmed against the rendered marks.
+- **Low: extending `_circ_arc` breaks db-retry.** Mitigated — the new param is
+ optional and defaults to `GOLD`; the existing call is unchanged and the
+ palette test covers db-retry.
diff --git a/tests/test_projects.py b/tests/test_projects.py
index 3a9f0d2..c93ee6c 100644
--- a/tests/test_projects.py
+++ b/tests/test_projects.py
@@ -21,6 +21,7 @@ def test_project_frame_parses_and_uses_tokens() -> None:
"modern-di-fastapi",
"modern-di-litestar",
"modern-di-starlette",
+ "modern-di-aiohttp",
"modern-di-faststream",
"modern-di-typer",
"modern-di-pytest",