Repository navigation
Description
Problem
The normal local-development path is split across several commands and configuration surfaces:
- start PostgreSQL and optionally Redis with
deploy/docker-compose.dev.yml; - launch
Cluckwork.Apiwith the correct Development environment, user-secrets, and fixed port; - launch the React/Vite SPA separately;
- configure an OTLP receiver separately to inspect the telemetry the API already emits.
This works, but it leaves resource startup, dependency readiness, dynamic endpoints, health, logs, traces, and metrics spread across Compose, VS Code, Vite, and operator knowledge.
Proposal
Add a project-based Aspire AppHost as a local-development entry point. Aspire 13.5 uses the .NET 10 SDK already targeted by Cluckwork and can model the existing .NET API, React/Vite SPA, PostgreSQL, and Redis without changing the domain/application architecture.
The AppHost should:
- model the current PostgreSQL 18.4 database and Redis 7.4 shared-state backend as local container resources;
- preserve the repository's exact digest-pinned container images rather than accepting floating Aspire defaults;
- launch
Cluckwork.Apiexplicitly in Development and continue loading its existing user-secrets; - supply the PostgreSQL resource as
ConnectionStrings:Default; - map the Redis resource connection string to
SharedState:Redis:ConnectionString; - launch
web/through Aspire's Vite integration and setVITE_API_TARGETfrom the API's dynamically allocated endpoint; - express readiness ordering: database and Redis before API, API before Vite;
- use the existing
/health/liveand/health/readycontracts in the Aspire resource model; - feed the API's existing OpenTelemetry traces and metrics into the Aspire dashboard;
- show each resource's status and console logs in the dashboard.
A representative resource graph is:
PostgreSQL ─┐
Redis ──────┼──> Cluckwork.Api ───> React/Vite
└──> Aspire dashboard: resources, logs, traces, metrics, health
Telemetry integration
Do not add the stock Aspire ServiceDefaults implementation unchanged. Cluckwork already has stronger, application-specific behavior in AddCluckworkTelemetry and its health pipeline:
- sensitive-data redaction before sinks;
- fail-closed endpoint/protocol validation;
- special degradation behavior for one-shot commands such as
migrateandrecover-admin; - an always-on server sampler and Cluckwork-specific instrumentation/filtering;
/health/liveand/health/ready, rather than the template/aliveand/healthcontract.
Instead, make the existing telemetry registration honor Aspire's standard OTEL_EXPORTER_OTLP_* environment variables when the corresponding Otlp:* setting is absent. Explicit Otlp:* configuration remains authoritative, and all current production validation, redaction, and one-shot behavior remains intact. Cover precedence, malformed configuration, and one-shot behavior with tests.
Acceptance criteria
- A project-based
Cluckwork.AppHostis part ofCluckwork.slnand uses a pinned stable Aspire release compatible with .NET 10. -
aspire runfrom the repository starts PostgreSQL, Redis, the API, the Vite SPA, and the secured local Aspire dashboard with no separately entered connection strings or fixed API port. - PostgreSQL and Redis use the same versions and immutable image digests as the existing dev/reference/test stacks; their pins cannot silently drift apart.
- PostgreSQL data persists across ordinary AppHost restarts, with PostgreSQL 18's data-directory semantics handled correctly.
- The API starts in Development, loads user-secrets, migrates the fresh local database using the existing Development behavior, and becomes healthy at
/health/ready. - The SPA's same-origin
/apicalls reach the Aspire-started API through its existing Vite proxy and a dynamically suppliedVITE_API_TARGET. - Redis is wired to
SharedState:Redis:ConnectionString, so local Aspire runs exercise the real shared-state path rather than the in-process fallback. - Dashboard resource status and console logs work, and API traces and metrics arrive through OTLP after exercising at least one API request and one EF query.
- Existing telemetry redaction, production TLS checks, sampling/filtering, and one-shot process-role behavior remain unchanged and covered by tests.
- Existing
/health/liveand/health/readybehavior remains unchanged; no duplicate template health endpoints or instrumentation are introduced. - No credentials, JWT keys, connection strings, or generated Aspire parameter values are committed.
- NuGet lock files are regenerated and committed; locked restore remains green.
-
CONTRIBUTING.mdand the relevant development documentation describeaspire run, prerequisites, dashboard access, reset/persistence behavior, and the existing dependency-only Compose fallback. - Verification includes
dotnet build Cluckwork.sln,dotnet test Cluckwork.sln, frontend typecheck/tests, and a documented runtime smoke test through the Aspire-started SPA.
Out of scope
- Replacing the production Dockerfile or
deploy/docker-compose.yml. - Using the AppHost as the production runtime.
- Adopting
aspire publish/aspire deployor changing the digest-based release and attestation pipeline. - Replacing the separate production
migratejob or changing the serving process's no-DDL contract. - Replacing the simulation harness or the existing Testcontainers integration-test infrastructure.
- Changing the DDD layers, endpoint contracts, persistence model, or SPA product behavior.
- Adding pgAdmin/Redis Insight in the initial slice; those can be evaluated separately after the minimal resource graph is stable.
Rollout
Keep deploy/docker-compose.dev.yml as the documented dependency-only fallback during the first slice. Aspire becomes the preferred full-stack local entry point only after the runtime smoke test demonstrates parity. Production and simulation paths remain authoritative for their current purposes.
Activity
docs: add runbook for loading the simulation fixture into a dev datab…
docs: note the Aspire divergence in the k6 drill
docs: note the Aspire divergence in the k6 drill
docs: add k6 preparation steps to the dev-database fixture runbook (#643
test(e2e): run the smoke suite at phone width, not just 1280 (#817)
feat(sim): seed a second farm for the README dashboard capture (#867)
feat(web): serve a per-request CSP nonce so Emotion's styles apply un…