Commit Graph

98 Commits

Author SHA1 Message Date
ae134907a4 Move _nav_entries_oob.html to shared templates instead of duplicating
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m16s
Used by both blog and events — belongs in shared/browser/templates
where the ChoiceLoader fallback resolves it for all apps.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 10:05:23 +00:00
db7342c7d2 Fix events: add missing _nav_entries_oob.html template
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 57s
Template exists in blog but was missing from events, causing
TemplateNotFound on calendar creation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 10:03:58 +00:00
94b1fca938 Fix entrypoint.sh permissions for new services
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 55s
Mark executable so bind-mounted dev volumes work correctly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 09:50:23 +00:00
96b02d93df Fix blog: add page_configs migration, fix stale cart reference in ghost_sync
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 43s
- Add 0003_add_page_configs.py migration to create table in db_blog
- Fix ghost_sync.py: fetch_data("cart", "page-config-ensure") → "blog"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 09:25:19 +00:00
fe34ea8e5b Fix market crash: remove stale toggle_product_like import
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 43s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 09:19:49 +00:00
f2d040c323 CI: deploy swarm only on main, dev stack on all branches
- Trigger on all branches (not just main/decoupling)
- Swarm stack deploy gated behind main branch check
- Dev stack (docker compose) always deployed
- Added relations, likes, orders to build loop

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 09:18:33 +00:00
22460db450 Rewrite CLAUDE.md to reflect full monorepo
Replaces the old art-dag-only docs with comprehensive documentation
covering all web platform services, shared library, art DAG subsystem,
architecture patterns, auth, inter-service communication, and dev/deploy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 09:17:30 +00:00
1a74d811f7 Incorporate art-dag-mono repo into artdag/ subfolder
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m33s
Merges full history from art-dag/mono.git into the monorepo
under the artdag/ directory. Contains: core (DAG engine),
l1 (Celery rendering server), l2 (ActivityPub registry),
common (shared templates/middleware), client (CLI), test (e2e).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

git-subtree-dir: artdag
git-subtree-mainline: 1a179de547
git-subtree-split: 4c2e716558
2026-02-27 09:07:23 +00:00
1a179de547 Add s-expression architecture transformation plan
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m57s
Vision document for migrating rose-ash to an s-expression-based
architecture where pages, media renders, and LLM-generated content
share a unified DAG execution model with content-addressed caching
on IPFS/IPNS.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 09:05:02 +00:00
fa431ee13e Split cart into 4 microservices: relations, likes, orders, page-config→blog
Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled
Phase 1 - Relations service (internal): owns ContainerRelation, exposes
get-children data + attach/detach-child actions. Retargeted events, blog,
market callers from cart to relations.

Phase 2 - Likes service (internal): unified Like model replaces ProductLike
and PostLike with generic target_type/target_slug/target_id. Exposes
is-liked, liked-slugs, liked-ids data + toggle action.

Phase 3 - PageConfig → blog: moved ownership to blog with direct DB queries,
removed proxy endpoints from cart.

Phase 4 - Orders service (public): owns Order/OrderItem + SumUp checkout
flow. Cart checkout now delegates to orders via create-order action.
Webhook/return routes and reconciliation moved to orders.

Phase 5 - Infrastructure: docker-compose, deploy.sh, Dockerfiles updated
for all 3 new services. Added orders_url helper and factory model imports.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 09:03:33 +00:00
76a9436ea1 Fetch product data from market service in cart's add_to_cart route
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m54s
The global add_to_cart route was calling find_or_create_cart_item without
denormalized product data, leaving NULL columns. Now fetches product info
via fetch_data("market", "products-by-ids") before creating the cart item.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 22:27:52 +00:00
8f8bc4fad9 Move entry_associations to shared — fix events cross-app import
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m33s
entry_associations only uses HTTP fetch_data/call_action, no direct DB.
Events app imported it via ..post.services which doesn't exist in events.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 18:05:30 +00:00
e45edbf362 Drop container_relations from blog DB — now lives in cart
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m1s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 17:51:53 +00:00
1f3d98ecc1 Move container_relations to cart service for cross-service ownership
container_relations is a generic parent/child graph used by blog
(menu_nodes), market (marketplaces), and events (calendars). Move it
to cart as shared infrastructure. All services now call cart actions
(attach-child/detach-child) instead of querying the table directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 17:49:30 +00:00
dd52417241 Fix cross-DB queries: move page_configs to cart, fix OAuth code_hash lookup
page_configs table lives in db_cart but blog was querying it directly,
causing UndefinedTableError. Move all PageConfig read/write endpoints to
cart service and have blog proxy via fetch_data/call_action.

Also fix OAuth callback to use code_hash lookup (codes are now stored
hashed) and pass grant_token in redirect URL to prevent auth loops.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 17:43:21 +00:00
98aee1f656 Add scrape_to_snapshot.py entry point with OAuth device flow login
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m13s
Restores the missing entry point script for API-mode scraping.
Calls get_access_token() before starting to trigger device flow
login if no token is saved.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:06:28 +00:00
81112c716b Decouple cart/market DBs: denormalize product data, AP internal inbox, OAuth scraper auth
Remove cross-DB relationships (CartItem.product, CartItem.market_place,
OrderItem.product) that break with per-service databases. Denormalize
product and marketplace fields onto cart_items/order_items at write time.

- Add AP internal inbox infrastructure (shared/infrastructure/internal_inbox*)
  for synchronous inter-service writes via HMAC-authenticated POST
- Cart inbox blueprint handles Add/Remove/Update rose:CartItem activities
- Market app sends AP activities to cart inbox instead of writing CartItem directly
- Cart services use denormalized columns instead of cross-DB hydration/joins
- Add marketplaces-by-ids data endpoint to market service
- Alembic migration adds denormalized columns to cart_items and order_items
- Add OAuth device flow auth to market scraper persist_api (artdag client pattern)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 14:49:04 +00:00
cf7fbd8e9b Fix cross-DB query: market app cart_items via data endpoint
market_context() was querying CartItem directly via g.s (db_market),
but cart_items lives in db_cart. Replace with fetch_data("cart",
"cart-items") and add the corresponding data endpoint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 13:52:09 +00:00
00249dd2a9 Fix nh3 panic: use link_rel param instead of rel in attributes
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m43s
nh3 manages the rel attribute internally — setting it in
tag_attributes triggers an assertion. Use link_rel parameter instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 13:35:06 +00:00
c015f3f02f Security audit: fix IDOR, add rate limiting, HMAC auth, token hashing, XSS sanitization
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m22s
Critical: Add ownership checks to all order routes (IDOR fix).
High: Redis rate limiting on auth endpoints, HMAC-signed internal
service calls replacing header-presence-only checks, nh3 HTML
sanitization on ghost_sync and product import, internal auth on
market API endpoints, SHA-256 hashed OAuth grant/code tokens.
Medium: SECRET_KEY production guard, AP signature enforcement,
is_admin param removal, cart_sid validation, SSRF protection on
remote actor fetch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 13:30:27 +00:00
404449fcab Fix auth ordering: validate grant before loading user
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m20s
_load_user ran before _check_auth_state, so g.user was set to the wrong
user before the grant check could clear the stale session. Now grant
verification runs first, ensuring stale sessions are cleared before
the user is loaded.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 12:22:35 +00:00
984ef9c65e Fix session security: clear stale sessions after DB rebuild
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m25s
Two issues fixed:
- Sessions with uid but no grant_token (legacy or corrupt) were not
  validated at all, allowing a user to be logged in as whoever got
  their old numeric user ID after a DB rebuild
- DB errors during grant verification silently kept stale sessions
  alive; now treated as invalid to fail-safe

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 12:17:53 +00:00
6f0965aa9c Fix alembic path in entrypoints for both Docker and dev
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m56s
In Docker images, alembic.ini is at /app/alembic.ini (no service subdir).
In dev, it's at /app/{service}/alembic.ini (bind-mounted). Now tries the
service dir first, falls back to current dir.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 12:13:03 +00:00
e65bd41ebe Decouple per-service Alembic migrations and fix cross-DB queries
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m19s
Each service (blog, market, cart, events, federation, account) now owns
its own database schema with independent Alembic migrations. Removes the
monolithic shared/alembic/ that ran all migrations against a single DB.

- Add per-service alembic.ini, env.py, and 0001_initial.py migrations
- Add shared/db/alembic_env.py helper with table-name filtering
- Fix cross-DB FK in blog/models/snippet.py (users lives in db_account)
- Fix cart_impl.py cross-DB queries: fetch products and market_places
  via internal data endpoints instead of direct SQL joins
- Fix blog ghost_sync to fetch page_configs from cart via data endpoint
- Add products-by-ids and page-config-ensure data endpoints
- Update all entrypoint.sh to create own DB and run own migrations
- Cart now uses db_cart instead of db_market
- Add docker-compose.dev.yml, dev.sh for local development
- CI deploys both rose-ash swarm stack and rose-ash-dev compose stack
- Fix Quart namespace package crash (root_path in factory.py)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 12:07:24 +00:00
bde2fd73b8 Rename stack from coop to rose-ash in CI deployment
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 44s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 09:40:22 +00:00
5cca66574e Trigger deployment
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m27s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 01:02:51 +00:00
giles
e9848653d7 Reduce Hypercorn workers to 1 per app to save ~600MB RAM
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m33s
Each app was running 2 workers (~100MB each). On a 3.8GB system with
6 apps, the 12 workers consumed ~1.2GB and caused swap pressure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:09:14 +00:00
giles
c3ba28ea03 Add device authorization flow (RFC 8628) for CLI login
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m5s
Implements the device code grant flow so artdag CLI can authenticate
via browser approval. Includes device/authorize, device/token endpoints,
user code verification page, and approval confirmation template.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:41:09 +00:00
giles
4c2e716558 Make JAX the primary fused-pipeline path for CPU/GPU parity
JAX via XLA produces identical output on CPU and GPU. Previously
CUDA hand-written kernels were preferred on GPU, causing visual
differences vs the JAX CPU fallback. Now JAX is always used first,
with legacy CuPy/GPUFrame as fallback only when JAX is unavailable.

Also adds comprehensive CLAUDE.md for the monorepo.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:31:53 +00:00
giles
b788f1f778 Fix CPU HLS streaming (yuv420p) and opt-in middleware for fragments
- Add -pix_fmt yuv420p to multi_res_output.py libx264 path so browsers
  can decode CPU-encoded segments (was producing yuv444p / High 4:4:4).
- Switch silent auth check and coop fragment middlewares from opt-out
  blocklists to opt-in: only run for GET requests with Accept: text/html.
  Prevents unnecessary nav-tree/auth-menu HTTP calls on every HLS segment,
  IPFS proxy, and API request.
- Add opaque grant token verification to L1/L2 dependencies.
- Migrate client CLI to device authorization flow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:33:53 +00:00
giles
4f49985cd5 Enable JAX compilation for streaming tasks
The StreamInterpreter was created without use_jax=True, so the JAX
compiler was never activated for production rendering. Desktop testing
had this enabled but the celery task path did not.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 15:39:29 +00:00
giles
07cae101ad Use JAX for fused pipeline fallback on CPU instead of GPUFrame path
When CUDA fused kernels aren't available, the fused-pipeline primitive
now uses JAX ops (jax_rotate, jax_scale, jax_shift_hue, etc.) instead
of falling back to one-by-one CuPy/GPUFrame operations. Legacy GPUFrame
path retained as last resort when JAX is also unavailable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 15:35:13 +00:00
giles
c53227d991 Fix multi-res HLS encoding on CPU: fall back to libx264 when NVENC unavailable
The hardcoded h264_nvenc encoder fails on CPU-only workers. Now uses
check_nvenc_available() to auto-detect and falls back to libx264.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 15:30:35 +00:00
giles
3bffb97ca1 Add JAX CPU to L1 worker image, CUDA JAX to GPU image
CPU workers can now run GPU-queue rendering tasks via JAX on CPU.
GPU image overrides with jax[cuda12] for full CUDA acceleration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 15:24:59 +00:00
giles
84e3ff3a91 Route GPU queue to CPU workers for CPU-based job processing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 15:10:43 +00:00
giles
b9fe884ab9 Fix order.page_config → page_config_id in checkout return
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m9s
The page_config relationship was removed during DB split; use the
page_config_id column and fetch page config via HTTP instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 15:00:04 +00:00
giles
3797a0c7c9 Fix error page loop + account startup timeout
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m11s
- Error handlers for FragmentError and generic Exception now return
  self-contained HTML (no render_template) to avoid the infinite loop
  where context processor → fetch_fragments → error → render_template
  → context processor → fetch_fragments → error ...
- Account Ghost membership sync moved to background task so it doesn't
  block Hypercorn's startup timeout (was causing crash-loop).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 14:45:29 +00:00
giles
1ea9ae4050 Show friendly error page when a service is unavailable
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m21s
FragmentError now renders a 503 page naming which service is down
instead of a generic 500 error. Helps debug during deploys.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 14:23:47 +00:00
giles
507a5a66ff Remove cross-DB menu_nodes query from non-blog apps
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m19s
menu_nodes table lives in db_blog. Non-blog apps (cart, market, events,
account, federation) were querying it on their own DB session, causing
UndefinedTableError. The nav-tree fragment from blog provides the real
navigation; menu_items is now an empty fallback list.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 14:18:02 +00:00
giles
094b6c55cd Fix AP blueprint cross-DB queries + harden Ghost sync init
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m10s
AP blueprints (activitypub.py, ap_social.py) were querying federation
tables (ap_actor_profiles etc.) on g.s which points to the app's own DB
after the per-app split. Now uses g._ap_s backed by get_federation_session()
for non-federation apps.

Also hardens Ghost sync before_app_serving to catch/rollback on failure
instead of crashing the Hypercorn worker.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 14:06:42 +00:00
giles
97d2021a00 Rollback session when advisory lock not acquired
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m13s
Prevents PgBouncer connection pool from inheriting dirty
transaction state when the non-syncing worker returns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 13:40:05 +00:00
giles
9f29073cda Fix Ghost sync race: advisory lock for multi-worker startup
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m0s
Two Hypercorn workers both run sync_all_content_from_ghost on startup,
racing on PostAuthor/PostTag rows. Use pg_try_advisory_lock so only
one worker runs the sync.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 13:32:28 +00:00
giles
c53f3025d9 Fix no_autoflush: use manual toggle for async session
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m9s
AsyncSession.no_autoflush is a sync context manager, can't use
with 'async with'. Toggle autoflush manually instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 12:06:54 +00:00
giles
3053cb321d Decouple PageConfig cross-domain queries + merge cart into db_market
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m11s
PageConfig (db_blog) decoupling:
- Blog: add page-config, page-config-by-id, page-configs-batch data endpoints
- Blog: add update-page-config action endpoint for events payment admin
- Cart: hydrate_page, resolve_page_config, get_cart_grouped_by_page all
  fetch PageConfig from blog via HTTP instead of direct DB query
- Cart: check_sumup_status auto-fetches page_config from blog when needed
- Events: payment routes read/write PageConfig via blog HTTP endpoints
- Order model: remove cross-domain page_config ORM relationship (keep column)

Cart + Market DB merge:
- Cart tables (cart_items, orders, order_items) moved into db_market
- Cart app DATABASE_URL now points to db_market (same bounded context)
- CartItem.product / CartItem.market_place relationships work again
  (same database, no cross-domain join issues)
- Updated split-databases.sh, init-databases.sql, docker-compose.yml

Ghost sync fix:
- Wrap PostAuthor/PostTag delete+re-add in no_autoflush block
- Use synchronize_session="fetch" to keep identity map consistent
- Prevents query-invoked autoflush IntegrityError on composite PK

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 11:59:35 +00:00
giles
3be287532d Fix post_authors duplicate key during Ghost sync
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 8m14s
Add explicit flush after DELETE and dedup authors/tags to prevent
autoflush-triggered IntegrityError on composite PK.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 11:47:50 +00:00
giles
95bd32bd71 Decouple cross-domain DB queries for per-app database split
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 6m2s
Move Ghost membership sync from blog to account service so blog no
longer queries account tables (users, ghost_labels, etc.). Account
runs membership sync at startup and exposes HTTP action/data endpoints
for webhook-triggered syncs and user lookups.

Key changes:
- account/services/ghost_membership.py: all membership sync functions
- account/bp/actions + data: ghost-sync-member, user-by-email, newsletters
- blog ghost_sync.py: stripped to content-only (posts, authors, tags)
- blog webhook member: delegates to account via call_action()
- try_publish: opens federation session when DBs differ
- oauth.py callback: uses get_account_session() for OAuthCode
- page_configs moved from db_events to db_blog in split script

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 11:32:14 +00:00
giles
50a9e5d952 Fix pgbouncer port and activate DB split
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 7m29s
edoburu/pgbouncer listens on 5432, not 6432. Update all DATABASE_URL
entries to use pgbouncer:5432. Activate per-app DATABASE_URL split
(no longer commented out).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 10:59:51 +00:00
giles
961067841e Tier 0 scalability: PgBouncer, Redis split, DB split, workers
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m14s
T0.1: Separate redis-auth service (64mb, noeviction) for auth state
T0.2: Bump data Redis from 256mb to 1gb
T0.3: Per-app DATABASE_URL via PgBouncer to per-domain databases
T0.4: PgBouncer service (transaction mode, pool=20, max_conn=300);
      session.py pools reduced to 3+5 with timeout and recycle
T0.5: Hypercorn --workers 2 --keep-alive 75 on all 6 entrypoints

Deploy requires running split-databases.sh first to create per-domain
databases from the existing appdb.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 10:10:18 +00:00
giles
0ccf897f74 Route outbound Follow through EventProcessor for retry
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m25s
send_follow now emits a Follow activity via emit_activity() instead of
inline HTTP POST. New ap_follow_handler delivers to the remote inbox;
EventProcessor retries on failure. Wildcard delivery handler skips
Follow type to avoid duplicate broadcast.

Also add /social/ index page to per-app social blueprint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 09:25:08 +00:00
giles
c6271931a6 Show per-app actor in follow notifications on Hub
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m34s
Add app_domain to APNotification model and NotificationDTO so follow
notifications display "followed you on blog" instead of just "followed
you" when the follow targets a per-app actor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 09:17:34 +00:00