367 lines
19 KiB
Markdown
367 lines
19 KiB
Markdown
---
|
|
phase: 01-architecture-foundation
|
|
plan: 02
|
|
type: execute
|
|
wave: 2
|
|
depends_on:
|
|
- 01-01
|
|
files_modified:
|
|
- src/ChatAgent.Shared/Models/HealthResponse.cs
|
|
- src/ChatAgent.Api/Program.cs
|
|
- src/ChatAgent.Api/Controllers/HealthController.cs
|
|
- src/ChatAgent.Client/Program.cs
|
|
- src/ChatAgent.Client/Services/ChatApiClient.cs
|
|
- src/ChatAgent.Client/Pages/Home.razor
|
|
- src/ChatAgent.Client/Layout/MainLayout.razor
|
|
- src/ChatAgent.Client/_Imports.razor
|
|
- src/ChatAgent.Client/wwwroot/css/app.css
|
|
autonomous: false
|
|
requirements:
|
|
- CODE-01
|
|
- CODE-02
|
|
|
|
must_haves:
|
|
truths:
|
|
- "A GET request from the WASM client to /api/health on the API server returns a HealthResponse with status 'healthy'"
|
|
- "CORS does not block the cross-origin request from localhost:5200 to localhost:7100"
|
|
- "The Home page displays the health check result (status and timestamp) fetched from the API"
|
|
- "Every .cs and .razor file contains inline comments explaining the Blazor/ASP.NET Core concept it demonstrates"
|
|
- "dotnet publish on the WASM project completes with no IL trimmer warnings"
|
|
artifacts:
|
|
- path: "src/ChatAgent.Shared/Models/HealthResponse.cs"
|
|
provides: "Shared DTO for health check"
|
|
contains: "public class HealthResponse"
|
|
- path: "src/ChatAgent.Api/Controllers/HealthController.cs"
|
|
provides: "Health check API endpoint"
|
|
contains: "[HttpGet]"
|
|
- path: "src/ChatAgent.Api/Program.cs"
|
|
provides: "API entry point with CORS and controller mapping"
|
|
contains: "AllowBlazorClient"
|
|
- path: "src/ChatAgent.Client/Services/ChatApiClient.cs"
|
|
provides: "Typed HttpClient wrapper"
|
|
contains: "GetHealthAsync"
|
|
- path: "src/ChatAgent.Client/Program.cs"
|
|
provides: "WASM entry point with DI registration"
|
|
contains: "AddHttpClient<ChatApiClient>"
|
|
- path: "src/ChatAgent.Client/Pages/Home.razor"
|
|
provides: "Landing page showing health check result"
|
|
contains: "@inject"
|
|
key_links:
|
|
- from: "src/ChatAgent.Client/Pages/Home.razor"
|
|
to: "src/ChatAgent.Client/Services/ChatApiClient.cs"
|
|
via: "@inject ChatApiClient"
|
|
pattern: "@inject.*ChatApiClient"
|
|
- from: "src/ChatAgent.Client/Services/ChatApiClient.cs"
|
|
to: "src/ChatAgent.Api/Controllers/HealthController.cs"
|
|
via: "HTTP GET to api/health"
|
|
pattern: "api/health"
|
|
- from: "src/ChatAgent.Api/Controllers/HealthController.cs"
|
|
to: "src/ChatAgent.Shared/Models/HealthResponse.cs"
|
|
via: "returns HealthResponse object"
|
|
pattern: "HealthResponse"
|
|
- from: "src/ChatAgent.Api/Program.cs"
|
|
to: "CORS middleware"
|
|
via: "UseCors before MapControllers"
|
|
pattern: "UseCors.*AllowBlazorClient"
|
|
---
|
|
|
|
<objective>
|
|
Implement the health check round-trip: shared DTO, API controller with CORS, typed HttpClient in the WASM client, and a home page that displays the result. Every file gets full tutorial-style inline comments per CODE-01.
|
|
|
|
Purpose: Prove the WASM-to-API communication path works end-to-end with CORS, establishing the pattern all future API calls will follow. This is Phase 1's single concept: "solution structure and project boundaries" (CODE-02).
|
|
Output: A running app where the WASM client calls the API health endpoint and displays the response.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@/home/ys/family-repo/AgenticCode/.claude/get-shit-done/workflows/execute-plan.md
|
|
@/home/ys/family-repo/AgenticCode/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/STATE.md
|
|
@.planning/phases/01-architecture-foundation/01-CONTEXT.md
|
|
@.planning/phases/01-architecture-foundation/01-RESEARCH.md
|
|
@.planning/phases/01-architecture-foundation/01-01-SUMMARY.md
|
|
|
|
<interfaces>
|
|
<!-- From Plan 01 outputs -- executor needs these project paths and port config -->
|
|
|
|
Projects created by Plan 01:
|
|
- src/ChatAgent.Client/ (Blazor WASM, port 5200)
|
|
- src/ChatAgent.Api/ (ASP.NET Core Web API, port 7100)
|
|
- src/ChatAgent.Shared/ (class library, referenced by both)
|
|
|
|
Client config (src/ChatAgent.Client/wwwroot/appsettings.json):
|
|
```json
|
|
{
|
|
"ApiBaseUrl": "https://localhost:7100"
|
|
}
|
|
```
|
|
|
|
All projects target net9.0. Solution file at repo root: ChatAgent.sln
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Implement shared DTO, API health endpoint with CORS, and client services</name>
|
|
<files>
|
|
src/ChatAgent.Shared/Models/HealthResponse.cs,
|
|
src/ChatAgent.Api/Program.cs,
|
|
src/ChatAgent.Api/Controllers/HealthController.cs,
|
|
src/ChatAgent.Client/Program.cs,
|
|
src/ChatAgent.Client/Services/ChatApiClient.cs,
|
|
src/ChatAgent.Client/Pages/Home.razor,
|
|
src/ChatAgent.Client/Layout/MainLayout.razor,
|
|
src/ChatAgent.Client/_Imports.razor,
|
|
src/ChatAgent.Client/wwwroot/css/app.css
|
|
</files>
|
|
<read_first>
|
|
src/ChatAgent.Shared/ChatAgent.Shared.csproj,
|
|
src/ChatAgent.Api/Program.cs,
|
|
src/ChatAgent.Api/ChatAgent.Api.csproj,
|
|
src/ChatAgent.Client/Program.cs,
|
|
src/ChatAgent.Client/ChatAgent.Client.csproj,
|
|
src/ChatAgent.Client/_Imports.razor,
|
|
src/ChatAgent.Client/Layout/MainLayout.razor,
|
|
src/ChatAgent.Client/Pages/Home.razor,
|
|
src/ChatAgent.Client/wwwroot/css/app.css,
|
|
.planning/phases/01-architecture-foundation/01-RESEARCH.md,
|
|
.planning/phases/01-architecture-foundation/01-CONTEXT.md
|
|
</read_first>
|
|
<action>
|
|
Create/modify the following files. Per D-07/D-08/D-09 (CODE-01), EVERY file must have tutorial-style inline comments explaining WHAT each Blazor/ASP.NET Core concept is and WHY it is used. Comments go inline as XML doc comments and `//` comments right next to the code.
|
|
|
|
**A. Shared DTO** -- Create `src/ChatAgent.Shared/Models/HealthResponse.cs`:
|
|
|
|
Create the `Models/` directory under `src/ChatAgent.Shared/`. Write the HealthResponse class in namespace `ChatAgent.Shared.Models` with two properties:
|
|
- `public string Status { get; set; } = string.Empty;` -- server health status (e.g., "healthy")
|
|
- `public DateTime Timestamp { get; set; }` -- server timestamp proving live response
|
|
|
|
Include XML doc comments on the class explaining it is a shared DTO (Data Transfer Object) that lives in the Shared project so both Client and Api use the same type without duplication. When the API returns this object, the Client deserializes it into the same class.
|
|
|
|
**B. API Program.cs** -- Rewrite `src/ChatAgent.Api/Program.cs`:
|
|
|
|
The file must contain, in order:
|
|
1. Top comment block explaining this is the ASP.NET Core Web API entry point, that it will eventually proxy OpenAI calls and manage JSON storage, but in Phase 1 only serves a health check
|
|
2. `builder.Services.AddControllers();` with comment: "We use Controllers (not Minimal API) for explicit structure (D-05)"
|
|
3. CORS configuration using `builder.Services.AddCors()` with a named policy `"AllowBlazorClient"` that allows origin `"https://localhost:5200"`, `AllowAnyHeader()`, `AllowAnyMethod()`. Include comments explaining: CORS is required because the WASM client runs on a different origin (different port), browsers block cross-origin requests by default for security
|
|
4. `var app = builder.Build();`
|
|
5. Middleware in correct order with comment "Middleware order matters in ASP.NET Core -- CORS must be applied before routing and authorization":
|
|
- `app.UseCors("AllowBlazorClient");`
|
|
- `app.UseAuthorization();`
|
|
- `app.MapControllers();` with comment explaining it discovers all [ApiController] classes
|
|
6. `app.Run();`
|
|
|
|
Do NOT include Swagger/OpenAPI middleware. Do NOT include `app.UseHttpsRedirection()` (it causes issues with local dev).
|
|
|
|
**C. Health Controller** -- Create `src/ChatAgent.Api/Controllers/HealthController.cs`:
|
|
|
|
Create the `Controllers/` directory if it does not exist (template may have one already). Write:
|
|
- Namespace: `ChatAgent.Api.Controllers`
|
|
- Class: `HealthController : ControllerBase`
|
|
- Attributes: `[ApiController]` and `[Route("api/[controller]")]`
|
|
- Method: `[HttpGet] public IActionResult Get()` returning `Ok(new HealthResponse { Status = "healthy", Timestamp = DateTime.UtcNow })`
|
|
- Using: `ChatAgent.Shared.Models`
|
|
- Comments explaining: [ApiController] enables automatic model validation, [Route] maps the class to a URL path, [HttpGet] maps this method to GET requests. This endpoint proves CORS works between WASM and API.
|
|
|
|
**D. Client Program.cs** -- Rewrite `src/ChatAgent.Client/Program.cs`:
|
|
|
|
The file must contain:
|
|
1. Top comment block explaining this is the Blazor WASM application entry point, that WebAssemblyHostBuilder configures root components, services (DI), and configuration
|
|
2. Using statements: `Microsoft.AspNetCore.Components.Web`, `Microsoft.AspNetCore.Components.WebAssembly.Hosting`, `ChatAgent.Client`, `ChatAgent.Client.Services`
|
|
3. `var builder = WebAssemblyHostBuilder.CreateDefault(args);`
|
|
4. `builder.RootComponents.Add<App>("#app");` with comment explaining it renders the App component inside the `<div id="app">` element in index.html
|
|
5. `builder.RootComponents.Add<HeadOutlet>("head::after");` with comment explaining HeadOutlet manages head elements from Razor components
|
|
6. Read API base URL from config: `var apiBaseUrl = builder.Configuration["ApiBaseUrl"] ?? "https://localhost:7100";` with comment explaining wwwroot/appsettings.json is PUBLIC, never put secrets there
|
|
7. Register typed HttpClient: `builder.Services.AddHttpClient<ChatApiClient>(client => { client.BaseAddress = new Uri(apiBaseUrl); });` with comments explaining AddHttpClient uses IHttpClientFactory internally for proper socket management, per-client configuration, and constructor DI
|
|
8. `await builder.Build().RunAsync();`
|
|
|
|
**E. Typed HttpClient** -- Create `src/ChatAgent.Client/Services/ChatApiClient.cs`:
|
|
|
|
Create the `Services/` directory under `src/ChatAgent.Client/`. Write:
|
|
- Namespace: `ChatAgent.Client.Services`
|
|
- Class: `ChatApiClient` with constructor injection of `HttpClient`
|
|
- Method: `public async Task<HealthResponse?> GetHealthAsync()` calling `_httpClient.GetFromJsonAsync<HealthResponse>("api/health")`
|
|
- Using: `ChatAgent.Shared.Models`, `System.Net.Http.Json`
|
|
- Comments explaining: In Blazor WASM, HttpClient is backed by the browser's Fetch API. We wrap it in a typed client so components don't depend on HttpClient directly (D-04). This makes testing easier and centralizes API URL management.
|
|
|
|
**F. Home Page** -- Rewrite `src/ChatAgent.Client/Pages/Home.razor`:
|
|
|
|
The file must contain:
|
|
1. `@page "/"` directive with comment explaining this maps the component to the root URL
|
|
2. `@using ChatAgent.Client.Services`
|
|
3. `@using ChatAgent.Shared.Models`
|
|
4. `@inject ChatApiClient ApiClient` with comment explaining @inject requests a service from the DI container
|
|
5. `<PageTitle>Chat Agent</PageTitle>`
|
|
6. An `<h1>Chat Agent</h1>` heading
|
|
7. A section that displays health check results:
|
|
- If `_healthResponse` is null and `_error` is null, show "Checking API connection..." text with class="loading"
|
|
- If `_healthResponse` is not null, show "API Status: {Status}" and "Server Time: {Timestamp}" in a div with class="health-status"
|
|
- If `_error` is not null, show the error message in a div with class="error-message"
|
|
8. An `@code` block with:
|
|
- `private HealthResponse? _healthResponse;`
|
|
- `private string? _error;`
|
|
- `protected override async Task OnInitializedAsync()` that calls `ApiClient.GetHealthAsync()` in a try/catch, setting `_error` on failure
|
|
- Comment explaining `OnInitializedAsync` is a Blazor lifecycle method called once when the component is first rendered, and that `StateHasChanged()` is called automatically after it completes
|
|
|
|
Use plain HTML/CSS per D-10 (no MudBlazor). Light theme per D-11.
|
|
|
|
**G. MainLayout** -- Update `src/ChatAgent.Client/Layout/MainLayout.razor`:
|
|
|
|
Simplify to a clean layout with:
|
|
- `@inherits LayoutComponentBase` with comment explaining LayoutComponentBase is the base class for layout components
|
|
- A `<main>` element wrapping `@Body` with comment explaining @Body is where the routed page content renders
|
|
- Remove any template navigation or sidebar (not needed in Phase 1)
|
|
- Basic styling: centered content with padding
|
|
|
|
**H. _Imports.razor** -- Update `src/ChatAgent.Client/_Imports.razor`:
|
|
|
|
Ensure these using directives are present (keep existing ones, add missing):
|
|
- `@using System.Net.Http`
|
|
- `@using System.Net.Http.Json`
|
|
- `@using Microsoft.AspNetCore.Components.Forms`
|
|
- `@using Microsoft.AspNetCore.Components.Routing`
|
|
- `@using Microsoft.AspNetCore.Components.Web`
|
|
- `@using Microsoft.AspNetCore.Components.Web.Virtualization`
|
|
- `@using Microsoft.AspNetCore.Components.WebAssembly.Http`
|
|
- `@using Microsoft.JSInterop`
|
|
- `@using ChatAgent.Client`
|
|
- `@using ChatAgent.Client.Layout`
|
|
- `@using ChatAgent.Client.Services`
|
|
- `@using ChatAgent.Shared.Models`
|
|
|
|
Add a comment at the top explaining _Imports.razor provides global using directives for all .razor files in the project.
|
|
|
|
**I. App CSS** -- Update `src/ChatAgent.Client/wwwroot/css/app.css`:
|
|
|
|
Replace with minimal clean CSS for Phase 1 (D-10 plain HTML/CSS, D-11 light theme):
|
|
```css
|
|
html, body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
margin: 0;
|
|
padding: 0;
|
|
background-color: #ffffff;
|
|
color: #333333;
|
|
}
|
|
|
|
main {
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
padding: 2rem;
|
|
}
|
|
|
|
h1 {
|
|
color: #1a1a1a;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.health-status {
|
|
padding: 1rem;
|
|
border: 1px solid #e0e0e0;
|
|
border-radius: 8px;
|
|
background-color: #f9f9f9;
|
|
}
|
|
|
|
.health-status p {
|
|
margin: 0.5rem 0;
|
|
}
|
|
|
|
.error-message {
|
|
color: #d32f2f;
|
|
padding: 1rem;
|
|
border: 1px solid #d32f2f;
|
|
border-radius: 8px;
|
|
background-color: #fce4ec;
|
|
}
|
|
|
|
.loading {
|
|
color: #666666;
|
|
font-style: italic;
|
|
}
|
|
```
|
|
|
|
After all files are created, run:
|
|
1. `dotnet build ChatAgent.sln` -- must succeed
|
|
2. `dotnet publish src/ChatAgent.Client/ChatAgent.Client.csproj -c Release --nologo 2>&1 | grep -i "warning IL"` -- must produce no output (no IL trimmer warnings)
|
|
</action>
|
|
<verify>
|
|
<automated>cd /home/ys/family-repo/AgenticCode && dotnet build ChatAgent.sln --nologo 2>&1 | tail -5 && echo "---PUBLISH---" && dotnet publish src/ChatAgent.Client/ChatAgent.Client.csproj -c Release --nologo 2>&1 | grep -ci "warning IL" || echo "0 IL warnings"</automated>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- `src/ChatAgent.Shared/Models/HealthResponse.cs` contains `public class HealthResponse`
|
|
- `src/ChatAgent.Shared/Models/HealthResponse.cs` contains `public string Status`
|
|
- `src/ChatAgent.Shared/Models/HealthResponse.cs` contains `public DateTime Timestamp`
|
|
- `src/ChatAgent.Api/Program.cs` contains `AddCors`
|
|
- `src/ChatAgent.Api/Program.cs` contains `AllowBlazorClient`
|
|
- `src/ChatAgent.Api/Program.cs` contains `WithOrigins("https://localhost:5200")`
|
|
- `src/ChatAgent.Api/Program.cs` contains `UseCors` BEFORE `MapControllers` (line number of UseCors < line number of MapControllers)
|
|
- `src/ChatAgent.Api/Program.cs` contains `AddControllers`
|
|
- `src/ChatAgent.Api/Program.cs` does NOT contain `swagger` or `Swagger` or `UseHttpsRedirection`
|
|
- `src/ChatAgent.Api/Controllers/HealthController.cs` contains `[ApiController]`
|
|
- `src/ChatAgent.Api/Controllers/HealthController.cs` contains `[HttpGet]`
|
|
- `src/ChatAgent.Api/Controllers/HealthController.cs` contains `HealthResponse`
|
|
- `src/ChatAgent.Client/Program.cs` contains `AddHttpClient<ChatApiClient>`
|
|
- `src/ChatAgent.Client/Program.cs` contains `ApiBaseUrl`
|
|
- `src/ChatAgent.Client/Services/ChatApiClient.cs` contains `GetHealthAsync`
|
|
- `src/ChatAgent.Client/Services/ChatApiClient.cs` contains `api/health`
|
|
- `src/ChatAgent.Client/Pages/Home.razor` contains `@inject ChatApiClient`
|
|
- `src/ChatAgent.Client/Pages/Home.razor` contains `OnInitializedAsync`
|
|
- `src/ChatAgent.Client/_Imports.razor` contains `ChatAgent.Shared.Models`
|
|
- `dotnet build ChatAgent.sln` exits with code 0
|
|
- `dotnet publish` produces 0 IL trimmer warnings
|
|
- Every `.cs` file created contains at least 3 comment lines (`//` or `///`)
|
|
- `src/ChatAgent.Api/Program.cs` contains at least 5 comment lines
|
|
- `src/ChatAgent.Client/Program.cs` contains at least 5 comment lines
|
|
</acceptance_criteria>
|
|
<done>All source files created with full tutorial comments. Solution builds. WASM publishes with no IL trim warnings. Health check round-trip code is in place.</done>
|
|
</task>
|
|
|
|
<task type="checkpoint:human-verify" gate="blocking">
|
|
<name>Task 2: Verify CORS health check works end-to-end</name>
|
|
<files>
|
|
src/ChatAgent.Client/Pages/Home.razor,
|
|
src/ChatAgent.Api/Controllers/HealthController.cs
|
|
</files>
|
|
<action>
|
|
Start both projects and verify the WASM client successfully calls the API health endpoint across origins.
|
|
|
|
1. In terminal 1: `cd /home/ys/family-repo/AgenticCode && dotnet run --project src/ChatAgent.Api`
|
|
2. In terminal 2: `cd /home/ys/family-repo/AgenticCode && dotnet run --project src/ChatAgent.Client`
|
|
3. Open browser to https://localhost:5200
|
|
4. Verify the page shows "Chat Agent" heading with "API Status: healthy" and a server timestamp
|
|
5. Check browser console (F12) for absence of CORS errors
|
|
6. Optionally test API directly: `curl -k https://localhost:7100/api/health`
|
|
</action>
|
|
<verify>
|
|
<automated>cd /home/ys/family-repo/AgenticCode && dotnet build ChatAgent.sln --nologo 2>&1 | tail -3</automated>
|
|
</verify>
|
|
<done>User confirms health check displays correctly in browser with no CORS errors.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
Phase 1 success criteria from ROADMAP.md:
|
|
1. `dotnet run` on both projects starts without errors -- verified in checkpoint
|
|
2. WASM client reaches API server and gets response (CORS working) -- verified in checkpoint
|
|
3. Shared models library referenced by both projects -- verified by build success + ProjectReference in .csproj
|
|
4. `dotnet publish` on WASM completes with no IL trim warnings -- verified in Task 1 automated check
|
|
5. Every file contains inline comments explaining the Blazor concept -- verified by acceptance criteria comment line counts
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- Health check response visible in browser at https://localhost:5200 showing "healthy" status and timestamp
|
|
- No CORS errors in browser console
|
|
- All .cs and .razor files have tutorial-style inline comments (CODE-01)
|
|
- Phase introduces exactly one concept: solution structure and project boundaries (CODE-02)
|
|
- Build and publish both succeed cleanly
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/01-architecture-foundation/01-02-SUMMARY.md`
|
|
</output>
|