From 3ebf504ed2114c6adba3ab93c18b9b06ad0eda3d Mon Sep 17 00:00:00 2001 From: AleksMorozova Date: Mon, 31 Aug 2026 23:36:48 +0300 Subject: [PATCH] Add AI documentation --- AGENTS.md | 22 +++- README.md | 56 +++++++-- design-qa.md | 4 +- docs/ai-workflow/README.md | 80 ++++++++++++ docs/ai-workflow/ai-rules.md | 103 ++++++++++++++++ docs/ai-workflow/checklists/ai-code-review.md | 36 ++++++ .../checklists/definition-of-done.md | 19 +++ .../example-storage-reconciliation.md | 92 ++++++++++++++ docs/ai-workflow/prompts/generate-tests.md | 43 +++++++ docs/ai-workflow/prompts/implement-feature.md | 43 +++++++ docs/ai-workflow/prompts/refactor-code.md | 32 +++++ .../prompts/review-pull-request.md | 39 ++++++ .../prompts/update-documentation.md | 34 +++++ docs/ai-workflow/use-cases.md | 16 +++ docs/analytics.md | 116 +++++------------- docs/course-project/README.md | 2 + 16 files changed, 640 insertions(+), 97 deletions(-) create mode 100644 docs/ai-workflow/README.md create mode 100644 docs/ai-workflow/ai-rules.md create mode 100644 docs/ai-workflow/checklists/ai-code-review.md create mode 100644 docs/ai-workflow/checklists/definition-of-done.md create mode 100644 docs/ai-workflow/example-storage-reconciliation.md create mode 100644 docs/ai-workflow/prompts/generate-tests.md create mode 100644 docs/ai-workflow/prompts/implement-feature.md create mode 100644 docs/ai-workflow/prompts/refactor-code.md create mode 100644 docs/ai-workflow/prompts/review-pull-request.md create mode 100644 docs/ai-workflow/prompts/update-documentation.md create mode 100644 docs/ai-workflow/use-cases.md diff --git a/AGENTS.md b/AGENTS.md index befe21f..1cff79c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,8 +10,8 @@ Prefer the smallest coherent change that fixes demonstrated behavior. Avoid spec ## Repository map -- `backend/src/MathArchive.Domain`: document entity and domain enum. -- `backend/src/MathArchive.Application`: document use cases, DTOs, validation, and storage/repository abstractions. +- `backend/src/MathArchive.Domain`: document and analytics entities and enums. +- `backend/src/MathArchive.Application`: document, storage audit, and analytics use cases, DTOs, validation, and storage/repository abstractions. - `backend/src/MathArchive.Infrastructure`: EF Core/PostgreSQL repository, migrations, local file storage, authentication implementations, and development seed data. - `backend/src/MathArchive.Api`: ASP.NET Core controllers, DI composition, ProblemDetails handling, health checks, and runtime configuration. - `backend/tests/MathArchive.Application.Tests`: xUnit unit, API, health, storage, and PostgreSQL integration tests. @@ -101,12 +101,22 @@ When changing document behavior, verify both sides agree on: - string `DocumentType` values; - `DocumentDto` fields and date/number shapes; - multipart create/update field names and optional replacement file; -- `search`, `grade`, `generalOnly`, `topic`, `documentType`, `page`, and `pageSize` parameters; +- `search`, `grade`, `generalOnly`, `topic`, `documentType`, `createdFrom`, `createdTo`, `sort`, `page`, and `pageSize` parameters; - `PagedResult` fields and infinite-page progression; - content types, filenames, preview/download semantics, and ProblemDetails status codes. Avoid relying on a new undocumented assumption on only one side of the contract. Update focused backend and frontend tests together when the API changes. +## Analytics invariants + +- Event names are `SiteVisit`, `DocumentPreview`, and `DocumentDownload`; reporting uses `summary.documentDownloads` and `documents[].downloadCount`. +- Track previews only after successful intentional PDF/image preview navigation. Track downloads from the MathArchive card download, details download, and details open-file actions, not effects, raw file endpoints, or browser PDF controls. +- Analytics action counts are separate from the document metadata `DownloadCount`. The open-file link uses `/preview` but records a `DocumentDownload` action. +- Dispatch must not block file access. The public tracking helper intentionally uses credential-free `fetch` with `keepalive: true`, bypassing Axios authentication interceptors. Do not add blind retries that can double-count actions. +- Reporting requires `AdminOnly`. Calendar dates use the browser timezone and become an inclusive UTC start and exclusive UTC end; material-list creation-date filters instead use inclusive UTC calendar dates. +- Event names are persisted as strings. Renaming them requires a data migration for existing rows. Historical events survive document deletion; do not introduce cascading deletion. +- See [analytics documentation](docs/analytics.md) for the exact API, privacy limitations, and verification commands. + ## Deployment and operations - Frontend: Vercel; `vercel.json` provides SPA rewrites. @@ -148,6 +158,8 @@ dotnet test backend/MathArchive.sln --no-build cd frontend/math-archive-web npm ci npm test +npm run test:seo-generator +$env:VITE_API_BASE_URL='http://localhost:5293' npm run build ``` @@ -172,3 +184,7 @@ Always account for one admin and a small audience. Do not inflate theoretical co 6. Report files changed, behavior changed, validation results, environment blockers, and intentionally out-of-scope concerns. Preserve unrelated user changes in a dirty worktree. Do not modify backend and frontend areas outside the requested scope merely because adjacent cleanup is possible. + +## AI-assisted development + +Use the reusable workflow, prompts, and checklists in [`docs/ai-workflow/README.md`](docs/ai-workflow/README.md) for AI-assisted tasks. AI output requires human review, especially for authorization, API contracts, migrations, file deletion, CI, merge, and deployment. Never provide secrets or personal data to AI, and never invent test, PR, CI, or deployment results. diff --git a/README.md b/README.md index 79afa9c..17643be 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ A single administrator can manage the archive through a protected admin panel. * Navigate by school grade and topic * Preview supported files * Download documents -* View download statistics +* Open the actual file in a new tab * Use the application without registration ### Administration @@ -28,6 +28,8 @@ A single administrator can manage the archive through a protected admin panel. * Edit document metadata * Replace uploaded files * Delete documents +* Filter materials by class and upload date, with newest-first sorting and a filtered total +* View site openings, document previews, and file-open/download analytics for a selected period * Audit consistency between database records and stored files * Safely clean up unreferenced files after an explicit confirmation * Manage the archive through a dedicated admin interface @@ -103,7 +105,7 @@ The current implementation stores files locally: storage/documents ``` -This keeps storage concerns outside controllers and application use cases and allows the local implementation to be replaced later with S3-compatible or cloud storage. +This is the local development path. Production uses a Render Persistent Disk mounted at `/app/storage`, with `FileStorage__RootPath=/app/storage/documents`. The backend intentionally runs as a single instance. PostgreSQL stores metadata, not file contents; database and source-file backups are separate concerns. ## Security @@ -118,7 +120,7 @@ This keeps storage concerns outside controllers and application use cases and al ## Prerequisites * .NET 9 SDK -* Node.js and npm +* Node.js 22 and npm (matching CI) * Docker Desktop or another Docker Compose runtime ## Local Development @@ -180,7 +182,7 @@ dotnet run --project backend/src/MathArchive.Api Swagger is available in development at: ```text -https://localhost:7000/swagger +http://localhost:5293/swagger ``` Development seed data is applied when the application starts with an empty database. @@ -189,17 +191,21 @@ Development seed data is applied when the application starts with an empty datab ```powershell cd frontend/math-archive-web -npm install +npm ci npm run dev ``` -Optional frontend environment variable: +Vite normally starts at `http://localhost:5173` and selects another port if occupied. Optional frontend environment variable (in `.env.local` or the shell): ```text -VITE_API_BASE_URL=http://localhost:5000 +VITE_API_BASE_URL=http://localhost:5293 ``` -For a production build, `VITE_API_BASE_URL` must point to the public API. Google Search Console HTML-tag verification is optional and can be enabled in the Vercel project environment variables: +Development defaults to this API URL. Development CORS permits loopback origins on different ports; production uses configured allowed origins. + +For a production build, `VITE_API_BASE_URL` must point to the public API. The SEO generator fetches public material metadata. After retries, network failures or HTTP 502/503/504 responses use the three stable SEO pages and base sitemap as a fallback; invalid configuration, malformed data, and other HTTP failures fail the build. + +Google Search Console HTML-tag verification is optional and can be enabled in the Vercel project environment variables: ```text VITE_GOOGLE_SITE_VERIFICATION=verification-token-from-google @@ -209,6 +215,8 @@ Store only the token value, not the complete `` element. When configured, ## Database Migrations +The backend applies migrations on startup when `Database:ApplyMigrationsOnStartup` is enabled (the default). Analytics includes a data migration that renames persisted event values; see [analytics migration behavior](docs/analytics.md#migration-and-deletion-behavior). + Apply existing migrations: ```powershell @@ -233,6 +241,7 @@ dotnet ef migrations add MigrationName ` / /materials /materials/:id +/about ``` ### Administration @@ -243,8 +252,11 @@ dotnet ef migrations add MigrationName ` /admin/documents/new /admin/documents/:id/edit /admin/storage +/admin/analytics ``` +`/admin` redirects to `/admin/documents`. Admin pages other than login require authentication. + ## API Endpoints ### Public @@ -253,7 +265,9 @@ dotnet ef migrations add MigrationName ` GET /api/documents GET /api/documents/topics GET /api/documents/{id} +GET /api/documents/{id}/preview GET /api/documents/{id}/download +POST /api/analytics/events ``` ### Administration @@ -262,16 +276,32 @@ GET /api/documents/{id}/download POST /api/auth/login POST /api/admin/documents GET /api/admin/storage/audit +GET /api/admin/analytics?from=&to= POST /api/admin/storage/cleanup-orphans PUT /api/admin/documents/{id} DELETE /api/admin/documents/{id} ``` +Login is public; the remaining administrative endpoints require the `Admin` role. The health endpoint is `GET /health`. + +### Material list contract + +`GET /api/documents` accepts `search`, `grade`, `generalOnly`, `topic`, `documentType`, `createdFrom`, `createdTo`, `sort`, `page`, and `pageSize`. Creation-date filters use inclusive UTC calendar dates (`YYYY-MM-DD`). The response contains `items`, `page`, `pageSize`, `totalCount`, and `totalPages`; filtering and sorting occur before pagination, so `totalCount` reflects all matching materials. + +The admin list reuses this endpoint with `sort=CreatedAtDescending` and class/date controls; topic and type controls are hidden there, but the API still supports them. The public default sort groups grades in ascending order, general materials last, then newest materials first within each group. General materials have `grade=null`; the public URL `class=general` maps to the API's `generalOnly=true`. + +### Analytics and file counters + +Analytics event types are `SiteVisit`, `DocumentPreview`, and `DocumentDownload`. The admin report returns `summary.documentDownloads` and per-document `downloadCount`, counting MathArchive file-open/download actions, not browser PDF-viewer activity. These period-filtered analytics counts are separate from the existing document metadata `downloadCount`, which increments only through the backend `/download` operation. Embedded previews use `/preview` and do not increment that counter. + +See the [analytics guide](docs/analytics.md) for tracking, privacy, date boundaries, migrations, and tests, and the [storage audit guide](docs/course-project/README.md) for reconciliation and cleanup. + ## Tests -Run backend tests: +Run backend tests from the repository root. Integration tests create temporary PostgreSQL databases using local port `5433`; `MATHARCHIVE_TEST_CONNECTION_STRING` overrides the connection (CI uses port `5432`). Never point tests at production. ```powershell +docker compose up -d postgres dotnet test backend/MathArchive.sln ``` @@ -280,6 +310,8 @@ Run frontend tests and production build: ```powershell cd frontend/math-archive-web npm test +npm run test:seo-generator +$env:VITE_API_BASE_URL='http://localhost:5293' npm run build ``` @@ -288,7 +320,7 @@ npm run build * Only one administrator is supported. * Files are stored locally instead of in cloud object storage. * PDF and image preview is supported. -* Word and Excel documents are download-only. +* Word and Excel documents have no embedded preview; file-open/download controls remain available, with handling determined by the browser. * There is no public registration or user profile system. * There is no moderation or multi-user administration workflow. @@ -297,3 +329,7 @@ npm run build MathArchive is an actively developed pet project built around a real use case. The current focus is keeping document publishing simple for the administrator while making materials easy to find and download for students and teachers. + +## AI-assisted development + +Reusable AI rules, prompts, checklists, and an evidence-based Storage Reconciliation example are documented in [`docs/ai-workflow/README.md`](docs/ai-workflow/README.md). diff --git a/design-qa.md b/design-qa.md index 67d014e..9a3f17c 100644 --- a/design-qa.md +++ b/design-qa.md @@ -1,5 +1,7 @@ # Homepage design QA +This is a historical QA record of the homepage/public-page redesign, not a current release sign-off. Test counts, local URLs, and observed fixture issues describe that verification session. See the [README](README.md) for current setup and commands. Admin styling was subsequently updated separately. + ## Sources and setup - Reference: `C:\Users\pc\AppData\Local\Temp\codex-clipboard-e24a1450-170f-4fda-b0dc-c4e5be95373d.png` @@ -40,7 +42,7 @@ Passed. - Verified the shared academic palette on `/materials`, a material details route, and `/about` at the desktop browser viewport. - Confirmed five seeded material cards load from the local API and public navigation remains functional. -- Confirmed mathematical illustrations move over time with independent 12–18 second animations; `prefers-reduced-motion` reduces them to a static state. +- Confirmed mathematical illustrations move over time; `prefers-reduced-motion` reduces them to a static state. The current CSS uses independent 8, 10, and 12 second animations after subsequent movement adjustments. - Confirmed the updated pages produce no browser console warnings or errors during the visual checks. - Local CORS preflight from `http://localhost:5174` returned `204`, echoed that origin, and allowed the established API methods. - The details preview reported a missing seeded physical file; this is existing local fixture/storage state rather than a CORS or redesign failure. diff --git a/docs/ai-workflow/README.md b/docs/ai-workflow/README.md new file mode 100644 index 0000000..4712049 --- /dev/null +++ b/docs/ai-workflow/README.md @@ -0,0 +1,80 @@ +# AI-assisted workflow for MathArchive + +## Purpose and scope + +This workflow helps developers use AI consistently for analysis, implementation, testing, review, and documentation in MathArchive. It is intended for new and current contributors and applies to features, fixes, refactoring, tests, documentation, and CI analysis. + +AI is a developer tool: it can accelerate navigation, prepare a diff, or support review, but it does not replace human responsibility for correctness, security, data, merge decisions, or deployment. + +## Overall process + +```text +Task definition +→ repository analysis +→ planning +→ implementation +→ test generation or updates +→ local verification +→ AI code review +→ manual review +→ documentation +→ Pull Request +→ CI +→ merge +→ deployment verification +``` + +Steps after local verification are performed by a developer or under explicit human control. Never mark a PR, CI run, merge, or deployment as complete without factual confirmation. + +## Roles + +### AI + +- reads the task, `AGENTS.md`, related code, tests, and documentation; +- finds analogous implementations and proposes a minimal plan; +- prepares changes within the agreed scope; +- proposes or updates behavior-focused tests; +- runs available checks and reports their exact results; +- reviews the diff and identifies residual risks. + +### Developer + +- confirms requirements, risky decisions, and destructive operations; +- reviews the diff, contracts, authorization, and data scenarios; +- evaluates recommendations against MathArchive's actual scale; +- remains responsible for commits, Pull Requests, CI, merge, and deployment verification. + +## How to use the workflow + +1. Record the task description, acceptance criteria, constraints, and explicit non-goals. +2. Ask AI to read the [rules](ai-rules.md), root [`AGENTS.md`](../../AGENTS.md), and related files. +3. Select the relevant [use case](use-cases.md) and prompt from [`prompts/`](prompts/). +4. Before editing, inspect `git status`, architecture, analogous code, API contracts, and tests. +5. Confirm the plan if it changes data, authorization, an API, the database schema, or deployment. +6. Run the relevant local checks after implementation. +7. Perform AI review with the [checklist](checklists/ai-code-review.md), followed by manual review. +8. Complete the [Definition of Done](checklists/definition-of-done.md) before opening a PR. Leave unverified items unchecked and explain why. + +## Quick start for a new developer + +1. Read the root [`README.md`](../../README.md) and [`AGENTS.md`](../../AGENTS.md). +2. Review the [AI rules](ai-rules.md) and [Definition of Done](checklists/definition-of-done.md). +3. For the first task, copy the [feature implementation prompt](prompts/implement-feature.md), fill in its fields, and add acceptance criteria. +4. Read the [Storage Reconciliation example](example-storage-reconciliation.md) and its source [course-project documentation](../course-project/README.md) to understand the full cycle. +5. Never provide AI with secrets or production data, and never ask it to invent verification results. + +## Documentation structure + +- [AI rules](ai-rules.md) +- [Use cases](use-cases.md) +- [Storage Reconciliation example](example-storage-reconciliation.md) +- Prompts: + - [Implement a feature](prompts/implement-feature.md) + - [Generate tests](prompts/generate-tests.md) + - [Refactor code safely](prompts/refactor-code.md) + - [Update documentation](prompts/update-documentation.md) + - [Review a Pull Request](prompts/review-pull-request.md) +- Checklists: + - [AI code review](checklists/ai-code-review.md) + - [Definition of Done](checklists/definition-of-done.md) + diff --git a/docs/ai-workflow/ai-rules.md b/docs/ai-workflow/ai-rules.md new file mode 100644 index 0000000..fe15434 --- /dev/null +++ b/docs/ai-workflow/ai-rules.md @@ -0,0 +1,103 @@ +# Rules for using AI in MathArchive + +These rules complement the root [`AGENTS.md`](../../AGENTS.md). If a rule conflicts with a specific task, AI must stop, describe the conflict, and request a developer decision. + +## What AI may generate + +- plans, explanations, documentation, prompts, and checklists; +- minimal production-code changes within an explicitly assigned task; +- unit, integration, and frontend tests that verify real behavior; +- review findings with a concrete practical impact; +- verification commands already supported by the repository. + +AI must not invent classes, methods, endpoints, configuration, commands, or execution results. Every technical name must be found in the repository or explicitly labeled as a proposal. + +## What AI may change only after analysis + +Before making changes, AI must read the related code, tests, configuration, documentation, and analogous implementations. This is mandatory for: + +- application services, repository abstractions, and storage abstractions; +- the document lifecycle and physical files; +- API DTOs, query parameters, and ProblemDetails; +- frontend API clients, types, and TanStack Query keys; +- authorization, validation, cancellation, and error handling; +- migrations, health checks, and deployment configuration. + +Do not modify unrelated code or perform opportunistic refactoring. + +## Decisions requiring human confirmation + +- deleting or mass-changing files or records; +- changing a public API contract or introducing an incompatible migration; +- changing authorization, roles, secrets, or production configuration; +- pushing, opening a PR, merging, deploying, or changing access; +- accepting a data-loss risk; +- materially expanding the task scope or architecture. + +## Data that must not be shared with AI + +- secrets, access tokens, JWT signing keys, and passwords; +- production connection strings and provider credentials; +- personal data belonging to students, parents, teachers, or the administrator; +- private educational files without explicit permission; +- production logs or dumps that have not been sanitized. + +Use fictitious values in examples. Never place real secrets in prompts, commands, documentation, or logs. + +## What must not be accepted without manual review + +- production code and migrations; +- authorization changes and deletion operations; +- API/frontend contract changes; +- claims about security, the absence of race conditions, or complete atomicity; +- AI review as a substitute for human review; +- test, CI, PR, merge, or deployment status without factual evidence. + +## Required checks before completing a task + +1. Review `git diff` and `git status`. +2. Confirm that the scope is minimal and unrelated changes are preserved. +3. Build the affected projects. +4. Run relevant tests; for broader changes, use the commands in [`AGENTS.md`](../../AGENTS.md#tests-and-validation). +5. Verify validation, authorization, error handling, and cancellation. +6. Align frontend and backend types, fields, enums, and status codes. +7. Validate documentation and relative links. +8. Report any unexecuted or blocked checks exactly. + +## Safe deletion + +- Never run destructive commands without explicit permission. +- Resolve the exact target and check current references before deletion. +- Never delete a file based only on a stale audit; immediately re-check whether it has become validly referenced. +- Never automatically delete database rows for missing or size-mismatched files. +- Preserve the priority: valid referenced content is more important than perfect orphan cleanup. + +## Authorization + +- Administrative endpoints must remain protected by the `AdminOnly` policy. +- Frontend route protection does not replace backend authorization. +- Tests should cover unauthenticated and insufficient-role scenarios when administrative behavior changes. +- Never weaken authorization to simplify tests or local development. + +## API contracts + +- Inspect the controller, application contract, frontend type, and API client first. +- Preserve established ProblemDetails and string-enum conventions. +- Update both sides of the contract and related tests within the same scope. +- Require human confirmation for incompatible changes and document the migration path. + +## Database and migrations + +- Change the schema only through EF Core migrations. +- Account for existing rows, nullability, defaults, and rollback or recovery risks. +- Do not store physical files in PostgreSQL. +- Never claim that a migration was applied in production without confirmation. + +## Documentation + +- Verify names, routes, endpoints, roles, and commands against the repository. +- Use relative GitHub links for internal files. +- Update an existing document instead of duplicating it. +- Record non-goals, limitations, residual risks, and blocked checks. +- Never invent test, CI, PR, merge, or deployment results. + diff --git a/docs/ai-workflow/checklists/ai-code-review.md b/docs/ai-workflow/checklists/ai-code-review.md new file mode 100644 index 0000000..7596cd0 --- /dev/null +++ b/docs/ai-workflow/checklists/ai-code-review.md @@ -0,0 +1,36 @@ +# AI code review checklist + +## Task and scope + +- [ ] The diff matches the task and acceptance criteria. +- [ ] There are no invented APIs, classes, methods, endpoints, or configuration keys. +- [ ] The change follows existing architecture and patterns. +- [ ] The scope is minimal and unrelated code was not modified. +- [ ] There are no accidental generated, formatting, or user changes. + +## Security and correctness + +- [ ] Backend authorization was verified for administrative operations. +- [ ] Validation is enforced server-side. +- [ ] Error handling uses established ProblemDetails/API patterns. +- [ ] CancellationToken or AbortSignal is propagated where relevant. +- [ ] Realistic concurrency and TOCTOU scenarios were evaluated. +- [ ] Data integrity and file lifecycle invariants are preserved. +- [ ] Deletion verifies the exact target and current references. +- [ ] Valid referenced content is not put at risk for the sake of cleanup. +- [ ] Frontend and backend contracts agree. +- [ ] Logging is structured and contains no secrets or personal data. +- [ ] There are no hardcoded secrets, tokens, or production connection strings. + +## Tests and documentation + +- [ ] Unit tests cover key behavior and boundary/failure cases. +- [ ] Integration/API tests cover contracts and authorization where relevant. +- [ ] Frontend tests cover user behavior and pending/error/empty states. +- [ ] Tests are not unnecessarily coupled to implementation details. +- [ ] Documentation, commands, routes, and limitations were updated. +- [ ] Test/build/CI results were not invented; blockers are reported exactly. +- [ ] A developer manually verified critical scenarios. + +The review result must separate blocking issues, warnings, and suggestions, and list residual risks even when no blocking issues are found. + diff --git a/docs/ai-workflow/checklists/definition-of-done.md b/docs/ai-workflow/checklists/definition-of-done.md new file mode 100644 index 0000000..26d2b22 --- /dev/null +++ b/docs/ai-workflow/checklists/definition-of-done.md @@ -0,0 +1,19 @@ +# Definition of Done for an AI-assisted task + +- [ ] Acceptance criteria are complete and mapped to actual behavior. +- [ ] A developer reviewed the entire diff. +- [ ] Affected projects compile. +- [ ] Relevant unit, integration, and frontend tests passed. +- [ ] Unexecuted checks and environment blockers are stated explicitly. +- [ ] Documentation, contracts, commands, and links were updated. +- [ ] Security, secrets handling, and authorization were verified. +- [ ] Data integrity, safe deletion, and cancellation were checked where relevant. +- [ ] EF Core migrations were created and verified if the schema changed. +- [ ] CI passed for the specific commit; otherwise this item remains unchecked. +- [ ] Blocking review findings were fixed, and other findings were addressed or rejected with rationale. +- [ ] The PR was reviewed and approved by a developer. +- [ ] Merge occurred only after required checks. +- [ ] Deployment was verified when it was in scope; otherwise this is stated explicitly. + +The presence of a CI workflow, deployment configuration, or test commands does not prove successful execution. Do not check an item without an actual result. + diff --git a/docs/ai-workflow/example-storage-reconciliation.md b/docs/ai-workflow/example-storage-reconciliation.md new file mode 100644 index 0000000..d5c0bb3 --- /dev/null +++ b/docs/ai-workflow/example-storage-reconciliation.md @@ -0,0 +1,92 @@ +# Workflow example: Storage Reconciliation + +This example is grounded in the actual code on the `storage-reconciliation` branch, the [course-project description](../course-project/README.md), the [AI interaction log](../course-project/prompts.md), and the existing [review checklist](../course-project/pr-review-checklist.md). It does not claim that external delivery steps were completed unless the repository confirms them. + +## 1. Initial problem + +PostgreSQL stores metadata while `LocalFileStorage` stores files on a separate disk. Partial failures can therefore leave: + +- a database record without a physical file; +- a file without a database reference; +- a referenced file whose actual size differs from its stored metadata. + +The task was to make these states visible to the administrator and allow manual cleanup of orphaned files only. + +## 2. Risk analysis + +The highest practical risk is deleting a file that became referenced after the initial audit. Other risks include path traversal, automatically deleting a recoverable database record, unauthorized cleanup, duplicate mutation requests, and falsely promising atomicity across PostgreSQL and the filesystem. + +The chosen approach fits a single administrator and a single backend instance: manual audit and cleanup without a queue, background worker, or distributed transaction. + +## 3. Implementation plan + +1. Add narrow read contracts for database references and top-level stored files. +2. Implement an application service that classifies inconsistencies. +3. Re-read references immediately before every deletion. +4. Add protected admin endpoints with an explicit confirmation payload. +5. Add a Ukrainian admin page with query and mutation states. +6. Add behavior-focused backend and frontend tests. +7. Document safety decisions, the demo procedure, and limitations. + +## 4. Backend implementation + +- [`StorageAuditService`](../../backend/src/MathArchive.Application/StorageAudit/StorageAuditService.cs) compares `DocumentStorageReference` and `StoredFileInfo`, then produces missing, orphaned, and size-mismatch groups. +- [`StorageAuditReport`](../../backend/src/MathArchive.Application/StorageAudit/StorageAuditReport.cs) contains the categorized results and totals. +- [`DocumentRepository.GetStorageReferencesAsync`](../../backend/src/MathArchive.Infrastructure/Persistence/DocumentRepository.cs) uses an `AsNoTracking` projection containing only the required fields. +- [`LocalFileStorage.ListAsync`](../../backend/src/MathArchive.Infrastructure/Storage/LocalFileStorage.cs) enumerates top-level files and returns names, sizes, and timestamps without exposing physical paths. +- [`AdminStorageController`](../../backend/src/MathArchive.Api/Controllers/AdminStorageController.cs) delegates to the service and requires the exact confirmation phrase `DELETE ORPHANS`. + +## 5. Frontend implementation + +- [`StorageAuditPage`](../../frontend/math-archive-web/src/pages/admin/StorageAuditPage.tsx) displays metrics, healthy/warning/error states, and three issue groups. +- [`storageApi`](../../frontend/math-archive-web/src/api/storageApi.ts) uses the existing Axios client. +- [`storageAudit` types](../../frontend/math-archive-web/src/types/storageAudit.ts) mirror the backend result fields. +- Cleanup opens a confirmation dialog, prevents duplicate submission, and replaces the TanStack Query cache with `currentState` from the cleanup response. + +## 6. Testing + +The repository contains: + +- [`StorageAuditServiceTests`](../../backend/tests/MathArchive.Application.Tests/StorageAuditServiceTests.cs), covering missing/orphaned/mismatch classification and the reference re-check before deletion; +- [`LocalFileStorageTests`](../../backend/tests/MathArchive.Application.Tests/LocalFileStorageTests.cs), including enumeration behavior added by the backend feature commit; +- [`StorageAuditPage.test.tsx`](../../frontend/math-archive-web/src/pages/admin/StorageAuditPage.test.tsx), covering confirmation, post-cleanup state, and duplicate-mutation prevention. + +The course-project documentation lists full verification commands, but the presence of commands does not prove that a particular run succeeded. Results must be recorded separately for each task. + +## 7. Safe orphan deletion + +`DeleteOrphansAsync` first creates an audit and then calls `GetStorageReferencesAsync` again immediately before each `DeleteAsync`. A candidate that became referenced is skipped and logged structurally. Cleanup does not accept a filename or path from the client and does not change database rows, missing records, or size-mismatched referenced files. + +A small check/delete race remains because the filesystem and PostgreSQL do not share a transaction. This is a documented limitation for MathArchive; preserving valid referenced content remains the priority. + +## 8. Administrative authorization + +`AdminStorageController` has `[Authorize(Policy = "AdminOnly")]`, and the policy in [`Program.cs`](../../backend/src/MathArchive.Api/Program.cs) requires the `Admin` role. The admin route is also registered in the frontend. No API integration test specifically covering storage authorization was found in the reviewed tests; this remains a manual check or a candidate for a future focused test. + +## 9. Documentation updates + +The feature is documented in the [course-project README](../course-project/README.md), AI decisions are recorded in the [prompt log](../course-project/prompts.md), and checks are listed in the [PR checklist](../course-project/pr-review-checklist.md). This example links to those sources instead of duplicating their full content. + +## 10. AI review and manual review + +The existing checklist requires review of data-loss risks, TOCTOU behavior, path safety, authorization, cancellation, API contracts, tests, and scope. The second-tool review section in the prompt log remains marked `to be recorded`; therefore, a comparative review and the developer's decisions are not confirmed. + +## 11. Pull Request, CI, merge, and deployment + +Repository history confirms local commits `569a3d8` for the backend, `9a0f850` for the frontend, and combined feature commit `9f6e87b` on the `storage-reconciliation` branch. The [CI workflow](../../.github/workflows/ci.yml) defines backend build/test/migration validation and frontend test/build jobs. + +The repository does not confirm PR #36, a successful CI run, merge of this branch, or deployment of the feature. A link to PR #36 is therefore not included. + +## 12. Limitations and residual risks + +- cleanup does not repair missing or corrupted files; +- nested directories are not scanned; +- there is no automatic startup or background cleanup; +- the small check/delete race remains; +- a focused storage authorization integration test is not confirmed; +- second-tool review, PR, CI, merge, and deployment verification are not recorded. + +## 13. Responsibility split + +According to the [recorded interaction log](../course-project/prompts.md), AI supported repository navigation, contract design, type definitions, and test scaffolding. The developer rejected automatic database deletion and disproportionate distributed architecture, added confirmation, checked the path boundary, required the pre-delete reference check, and remains responsible for final manual review and delivery decisions. + diff --git a/docs/ai-workflow/prompts/generate-tests.md b/docs/ai-workflow/prompts/generate-tests.md new file mode 100644 index 0000000..0a22467 --- /dev/null +++ b/docs/ai-workflow/prompts/generate-tests.md @@ -0,0 +1,43 @@ +# Prompt: generate tests + +## Inputs + +```text +Behavior or diff: +[description/link] + +Acceptance criteria: +[criteria] + +Test level and constraints: +[unit/integration/frontend; available infrastructure] +``` + +## Prompt + +```text +Inspect existing MathArchive tests, fixtures, naming, and assertion style before making changes. Identify the behavior that genuinely needs protection and avoid duplicating existing tests. + +Create a test matrix covering, where relevant: +- happy paths; +- validation and boundary values; +- unauthenticated and forbidden authorization; +- cancellation; +- dependency, database, and filesystem failures; +- repeated or duplicate non-idempotent requests; +- concurrent changes and stale reads; +- protection against loss of valid referenced content; +- frontend/backend contracts and loading/error/empty/pending states. + +Reuse existing xUnit, WebApplicationFactory, PostgreSQL integration, Vitest, and Testing Library patterns. Do not change production code solely to make tests pass or test private implementation details without a behavioral reason. + +Implement the smallest sufficient set. Run focused tests and report exact results. If PostgreSQL or another dependency is unavailable, distinguish a code failure from an environment blocker. + +Final response format: +- test matrix and protected risks; +- created or changed test files; +- commands executed and their results; +- gaps requiring manual or integration verification; +- confirmation that production code was not changed to bypass tests. +``` + diff --git a/docs/ai-workflow/prompts/implement-feature.md b/docs/ai-workflow/prompts/implement-feature.md new file mode 100644 index 0000000..849f46a --- /dev/null +++ b/docs/ai-workflow/prompts/implement-feature.md @@ -0,0 +1,43 @@ +# Prompt: implement a feature + +## Inputs + +```text +Task: +[description] + +Acceptance criteria: +[verifiable criteria] + +Constraints and non-goals: +[scope, compatibility, prohibited changes] +``` + +## Prompt + +```text +Work in the MathArchive repository as an engineering assistant. Final decisions and review belong to the developer. + +1. Read the root AGENTS.md and README.md, check git status, and preserve unrelated changes. +2. Analyze the architecture, related production code, tests, and documentation. Find analogous implementations. Do not invent classes, endpoints, or conventions. +3. Ask clarifying questions only when material uncertainty affects scope, data, contracts, authorization, or risk. +4. Before editing, provide a short plan covering files, contracts, risks, tests, and documentation. +5. Make the smallest coherent change. Do not break backward compatibility without necessity and human confirmation. Do not refactor unrelated code. +6. Follow existing layers and patterns: thin controllers, Application services, IDocumentRepository, IFileStorage, the Axios client, TanStack Query, and Material UI. +7. Verify authorization, server-side validation, ProblemDetails/error handling, CancellationToken propagation, and file/data lifecycle. Backend authorization is mandatory for administrative operations. +8. If the API changes, align backend DTOs/controllers, frontend types/clients, and focused tests. +9. Add or update behavior-focused tests. Never change production code solely to make a test pass artificially. +10. Run the smallest relevant checks, followed by broader affected builds/tests. Never invent results; report exact blockers separately. +11. Update existing documentation when behavior, API, configuration, risks, or commands change. Use relative GitHub links. + +Final response format: +- what changed and why; +- changed files; +- completed acceptance criteria; +- result of every verification command; +- unexecuted checks and blockers; +- risks and required manual checks; +- whether a migration is required; +- what intentionally remained out of scope. +``` + diff --git a/docs/ai-workflow/prompts/refactor-code.md b/docs/ai-workflow/prompts/refactor-code.md new file mode 100644 index 0000000..f39d5dd --- /dev/null +++ b/docs/ai-workflow/prompts/refactor-code.md @@ -0,0 +1,32 @@ +# Prompt: refactor code safely + +## Inputs + +```text +Refactoring goal: +[problem] + +Scope: +[files/modules] + +Public contracts and constraints: +[what must remain unchanged] +``` + +## Prompt + +```text +Before editing, read AGENTS.md, related code, and tests; check git status; and record current observable behavior. Run focused baseline tests when the environment permits. + +Define scope and non-goals precisely. Do not change public APIs, the database schema, routes, DTOs, error shapes, or UI behavior without a separate requirement and human confirmation. Make small logical changes while preserving file lifecycle, authorization, cancellation, and data integrity invariants. Do not introduce an abstraction without concrete value for MathArchive. + +Review the diff after each logical step. Run the same tests after the change and build the affected project. List every intentional behavior change separately; if there are none, say so explicitly. + +Final response format: +- baseline and scope; +- structural changes; +- intentional behavior changes or confirmation that none occurred; +- tests before and after, plus build result; +- risks and manual checks. +``` + diff --git a/docs/ai-workflow/prompts/review-pull-request.md b/docs/ai-workflow/prompts/review-pull-request.md new file mode 100644 index 0000000..203150c --- /dev/null +++ b/docs/ai-workflow/prompts/review-pull-request.md @@ -0,0 +1,39 @@ +# Prompt: review a Pull Request + +## Inputs + +```text +PR or diff: +[link or base/head] + +Task and acceptance criteria: +[description] + +Known constraints: +[scope, infrastructure, non-goals] +``` + +## Prompt + +```text +Review the change as a skeptical senior .NET/React engineer while accounting for MathArchive's actual scale: one administrator, a small audience, PostgreSQL metadata, and single-instance file storage. + +Read the complete diff and related code, tests, contracts, and documentation. Check security, backend authorization, data integrity, file lifecycle, safe deletion, concurrency/TOCTOU behavior, cancellation, validation, frontend/backend API contracts, ProblemDetails/error handling, logging without secrets, tests, and documentation. + +Do not create ceremonial findings without practical impact or propose enterprise architecture without a demonstrated need. + +Separate the result into: +1. Blocking issues. +2. Warnings. +3. Suggestions. + +For every finding, provide: +- file and exact location; +- the problem; +- realistic impact; +- a concrete correction; +- why the severity fits MathArchive's scale. + +If no blocking issues are found, say so explicitly. Finish with residual risks, unverified assumptions, and manual checks. Do not infer successful CI or deployment merely from the presence of a workflow. +``` + diff --git a/docs/ai-workflow/prompts/update-documentation.md b/docs/ai-workflow/prompts/update-documentation.md new file mode 100644 index 0000000..e16e06e --- /dev/null +++ b/docs/ai-workflow/prompts/update-documentation.md @@ -0,0 +1,34 @@ +# Prompt: update documentation + +## Inputs + +```text +Change or topic: +[description/diff] + +Documents in scope: +[paths] + +Confirmed facts: +[tests, CI, and deployment only when supported by evidence] +``` + +## Prompt + +```text +Synchronize MathArchive documentation with the actual repository. First inspect existing documentation and do not create a duplicate when an appropriate document already exists. + +Verify exact endpoints, routes, roles/policies, DTO/type names, configuration keys, environment variables, commands, and supported behavior in code. Do not invent names or results. Repair stale internal links and use relative GitHub links. + +Document current non-goals, security/data risks, operational limitations, and manual steps. Do not include secrets, production credentials, personal data, or real private filenames. Do not claim that tests, a PR, CI, merge, or deployment completed without evidence. + +After editing, validate Markdown formatting, code fences, anchors, and all relative links. + +Final response format: +- updated or created documents; +- sources for confirmed facts; +- repaired links; +- unsupported claims intentionally omitted; +- Markdown and link validation result. +``` + diff --git a/docs/ai-workflow/use-cases.md b/docs/ai-workflow/use-cases.md new file mode 100644 index 0000000..4d89088 --- /dev/null +++ b/docs/ai-workflow/use-cases.md @@ -0,0 +1,16 @@ +# AI use cases in MathArchive + +| Use case | Goal | Inputs | Expected result | Required human control | Tool | +|---|---|---|---|---|---| +| Analyze a new task | Define scope, dependencies, risks, and acceptance criteria | Task description, `AGENTS.md`, related code and tests | Plan covering files, contracts, risks, and checks | Confirm requirements, non-goals, and risky decisions | [Implement feature](prompts/implement-feature.md) | +| Implement backend changes | Add a use case within existing layers | Application/API/Infrastructure analogues, contracts, validation | Minimal diff with cancellation, authorization, and ProblemDetails | Review data integrity, authorization, migrations, and API | [Implement feature](prompts/implement-feature.md), [AI review](checklists/ai-code-review.md) | +| Implement frontend changes | Add UI without creating a parallel architecture | Route, API type/client, MUI and TanStack Query patterns | Ukrainian UI with loading, error, empty, and pending states | Review UX, accessibility, and backend-contract alignment | [Implement feature](prompts/implement-feature.md) | +| Generate tests | Protect behavior and risky scenarios | Acceptance criteria, diff, existing test fixtures | Behavior-focused unit, integration, or UI tests | Confirm tests do not freeze implementation details | [Generate tests](prompts/generate-tests.md) | +| Refactor code | Improve structure without hidden behavior changes | Defined scope, baseline tests, public contracts | Small logical steps and a list of intentional changes | Compare behavior before and after; review the diff | [Refactor code](prompts/refactor-code.md) | +| Update documentation | Synchronize documentation with code | Actual routes, endpoints, roles, configuration, and commands | Accurate documentation with working relative links | Confirm operational facts and absence of secrets | [Update documentation](prompts/update-documentation.md) | +| Review a Pull Request | Find practical defects and residual risks | PR diff, issue/acceptance criteria, related code and tests | Blocking issues, warnings, suggestions, or an explicit statement that no blocking issues were found | Accept or reject findings and perform manual review | [Review PR](prompts/review-pull-request.md), [AI review](checklists/ai-code-review.md) | +| Analyze CI failures | Distinguish code failures from infrastructure blockers | Complete job log, workflow, local result | Root cause, minimal corrective action, and uncertainty | Verify secrets, environment, and rerun results | [AI review](checklists/ai-code-review.md) | +| Verify API contracts | Prevent frontend/backend drift | Controller/DTO, TypeScript types, API client, tests | Mapping of fields, types, nullability, enums, and status codes | Confirm backward compatibility | [Implement feature](prompts/implement-feature.md), [Review PR](prompts/review-pull-request.md) | + +For file operations, also apply the file lifecycle rules in [`AGENTS.md`](../../AGENTS.md#file-lifecycle-invariants). Complete every use case with the [Definition of Done](checklists/definition-of-done.md). + diff --git a/docs/analytics.md b/docs/analytics.md index 5bfed0c..c7274e0 100644 --- a/docs/analytics.md +++ b/docs/analytics.md @@ -1,6 +1,6 @@ # Anonymous usage analytics -The admin statistics page is `/admin/analytics` ("Статистика"). It uses the existing admin authorization and theme. Counts represent openings, not unique students or proof that someone read a document. +The admin statistics page is `/admin/analytics` ("Статистика"). It uses the existing admin authorization and theme. Counts represent site openings and intentional preview/file-open/download actions, not unique students or proof that someone read a document. ## Event meanings and frontend locations @@ -12,9 +12,9 @@ The existing document metadata `DownloadCount` remains unchanged: it counts the `src/api/analyticsApi.ts` creates a random UUID in localStorage under `matharchive_session_id`, reuses it across visits, and falls back to a page-local ID when storage is unavailable. Clearing browser storage resets this ID. Concurrent first-ever tabs can each generate an ID; no cross-tab locking is introduced for this small application. -Public tracking uses a small fire-and-forget fetch with `credentials: 'omit'` and no Authorization header. It deliberately bypasses the shared Axios authentication interceptors so an analytics failure cannot log out the admin. Both synchronous and asynchronous failures are suppressed; events are not retried or queued. Missing browser crypto, offline mode, blockers, or server failures can cause undercounting without breaking the site. +Public tracking uses a small fire-and-forget fetch with `keepalive: true`, `credentials: 'omit'`, and no Authorization header. It deliberately bypasses the shared Axios authentication interceptors so an analytics failure cannot log out the admin. Both synchronous and asynchronous failures are suppressed; events are not retried or queued. Missing browser crypto, offline mode, blockers, or server failures can cause undercounting without breaking the site. Keepalive supports navigation-time dispatch but does not guarantee delivery. -Only the five requested event fields are persisted. No IP address, user agent, name, email, account ID, page URL, or cookies are stored by this feature. Hosting/provider access logs are independent of this application table. The persistent random browser identifier is not an identified student account. Public browsing by the administrator is not excluded, and this is not a bot-proof analytics system. +Only `Id`, `SessionId`, `EventType`, `DocumentId`, and `CreatedAt` are persisted. No IP address, user agent, name, email, account ID, page URL, or cookies are stored by this feature. Hosting/provider access logs are independent of this application table. The persistent random browser identifier is not an identified student account. Public browsing by the administrator is not excluded, and this is not a bot-proof analytics system. ## API contract @@ -23,7 +23,7 @@ Only the five requested event fields are persisted. No IP address, user agent, n ```json { "sessionId": "11111111-1111-4111-8111-111111111111", - "eventType": "DocumentPreview", + "eventType": "DocumentDownload", "documentId": "22222222-2222-4222-8222-222222222222" } ``` @@ -60,102 +60,52 @@ The UI interprets dates in the administrator's browser timezone and shows that t Initial migration: `20260831135409_AddAnalyticsEvents`. -Rename migration: `20260831145701_RenameDocumentViewToDocumentDownload`. Because EF stores event names as strings, this data-only migration changes existing `DocumentView` rows to `DocumentDownload`, preserving their IDs, sessions, documents, and timestamps. Down reverses the string rename. No schema changes or backfill of previously untracked download clicks are needed. Deploy backend and frontend together: old clients sending `DocumentView` are no longer supported. +Rename migration: `20260831145701_RenameDocumentViewToDocumentDownload`. Because EF stores event names as strings, this data-only migration changes existing `DocumentView` rows to `DocumentDownload`, preserving their IDs, sessions, documents, and timestamps. Down reverses the string rename without deleting rows. No schema changes are required for the rename. Previously untracked download clicks cannot be reconstructed and are not backfilled. Deploy backend and frontend together: old clients sending `DocumentView` are no longer supported. -Adds `analytics_events` with UUID ID/session/document fields, a string event type, and a UTC timestamp. Indexes cover `(created_at, event_type)` and `document_id`. There is no session index because this feature does not query sessions. +The initial migration adds `analytics_events` with UUID ID/session/document fields, a string event type, and a UTC timestamp. Indexes cover `(created_at, event_type)` and `document_id`. There is no session index because this feature does not query sessions. -Historical document IDs intentionally have no foreign key: deleting a material must not erase its historical counts or block normal deletion. The report left-joins current document titles, displaying "Видалений матеріал" when the material no longer exists. Titles are not copied into events. No retention job is introduced; events remain until explicitly removed. Rolling the migration back drops analytics data only. +Historical document IDs intentionally have no foreign key: deleting a material must not erase its historical counts or block normal deletion. The report left-joins current document titles, displaying "Видалений матеріал" when the material no longer exists. Titles are not copied into events. No retention job is introduced; events remain until explicitly removed. Rolling back the initial `AddAnalyticsEvents` migration drops the analytics table and its data, but does not delete documents or files. Generate/apply migrations using the existing deployment workflow. The backend's existing migration-on-startup setting applies this migration when enabled. Do not deploy tracking before the corresponding backend migration/API is available. ## Tests and local verification - `AnalyticsServiceTests`: recording all event types, validation, UTC timestamps, and date-range validation/normalization. -- `AnalyticsApiIntegrationTests`: public recording/validation, admin authorization, PostgreSQL date boundaries, summary/table counts, zero counters, sorting, empty results, and preservation after document deletion. Uses the existing temporary PostgreSQL database fixture; never a production database. +- `AnalyticsApiIntegrationTests`: public recording/validation, admin authorization, PostgreSQL date boundaries, download summary/table fields, zero counters, sorting, empty results, preservation after document deletion, and migration of historical event names. Uses the existing temporary PostgreSQL database fixture; never a production database. - `analyticsApi.test.tsx`: UUID reuse, credential-free payloads, StrictMode/remount/reload behavior, storage/network failure isolation. - `analyticsDates.test.ts`: presets, invalid ranges, and independent local-midnight conversion across DST dates. -- `AnalyticsPage.test.tsx`: period requests, summary/table, loading, error/retry, and empty states. -- `DocumentDetailsPage.test.tsx`: preview deduplication and usable file-opening navigation when analytics is offline. +- `AnalyticsPage.test.tsx`: period requests, summary/table, Ukrainian download labels, loading, error/retry, and empty states. +- `DocumentDetailsPage.test.tsx`: preview deduplication, one download event per activation of all three MathArchive file controls (including the card), and usable file actions when analytics is offline. ```powershell +docker compose up -d postgres +dotnet restore backend/MathArchive.sln dotnet build backend/MathArchive.sln --no-restore dotnet test backend/MathArchive.sln --no-build cd frontend/math-archive-web +npm ci npx vitest run --maxWorkers=1 $env:VITE_API_BASE_URL='http://localhost:5293' npm run build ``` -## Download-metric refactor - -The previous metric omitted the card/details download buttons and described explicit file opens as "views". A prior live trace proved the existing keepalive transport returned 204 and persisted the open-link event; no transport failure was reproduced. The refactor expands action coverage rather than changing PDF-viewer behavior or adding retries that could double-count events. - -Changed for this refactor: the analytics domain enum, application contracts/validation, repository aggregation, rename migration and designer, analytics service/integration tests, frontend analytics API/types/tests, `DocumentCard.tsx`, `DocumentDetailsPage.tsx` and tests, admin `AnalyticsPage.tsx` and tests, and this document. API response fields are now `summary.documentDownloads` and `documents[].downloadCount`; endpoints and date handling are unchanged. Migration tests verify conversion of historical string values. Frontend tests verify single events from all three actions, preview separation, nonblocking failures, and Ukrainian download labels. - -## Verification and changed files - -### Download refactor verification and changed files - -Verification: 87 backend tests (including PostgreSQL integration and data-migration tests) and 73 frontend tests passed. Both builds passed and EF reported no pending model changes. The frontend build retained its existing large-chunk warning and used the stable SEO fallback while the local API was restarting. - -A real browser session exercised all three MathArchive file controls. Each sent `DocumentDownload` with the selected document ID to the public endpoint and received 204. PostgreSQL download events for that document increased from 1 to 4 (one per click), while opening the preview increased preview events from 3 to 4 without an additional download. The temporary request-tracing proxy was removed afterward. No PDF-viewer behavior changed. The rename migration was applied to the local development database; production deployment still needs the updated backend/migration and frontend together. - -Files changed specifically for this refactor: - -- `backend/src/MathArchive.Domain/Analytics/AnalyticsEvent.cs` -- `backend/src/MathArchive.Application/Analytics/AnalyticsContracts.cs` -- `backend/src/MathArchive.Application/Analytics/AnalyticsService.cs` -- `backend/src/MathArchive.Infrastructure/Persistence/AnalyticsRepository.cs` -- `backend/src/MathArchive.Infrastructure/Migrations/20260831145701_RenameDocumentViewToDocumentDownload.cs` -- `backend/src/MathArchive.Infrastructure/Migrations/20260831145701_RenameDocumentViewToDocumentDownload.Designer.cs` -- `backend/tests/MathArchive.Application.Tests/AnalyticsServiceTests.cs` -- `backend/tests/MathArchive.Application.Tests/Integration/AnalyticsApiIntegrationTests.cs` -- `frontend/math-archive-web/src/api/analyticsApi.ts` -- `frontend/math-archive-web/src/api/analyticsApi.test.tsx` -- `frontend/math-archive-web/src/components/DocumentCard.tsx` -- `frontend/math-archive-web/src/pages/DocumentDetailsPage.tsx` -- `frontend/math-archive-web/src/pages/DocumentDetailsPage.test.tsx` -- `frontend/math-archive-web/src/pages/admin/AnalyticsPage.tsx` -- `frontend/math-archive-web/src/pages/admin/AnalyticsPage.test.tsx` -- `docs/analytics.md` - -### Initial implementation verification - -Implementation verification: all 70 frontend tests passed with a single worker; all 49 non-database backend tests passed outside the Windows sandbox; backend and frontend builds passed; EF reported no pending model changes. The broad initial runs encountered UI timeouts and Windows Event Log permissions, resolved by those reruns. PostgreSQL integration tests could not execute because localhost:5433 was unavailable and Docker Desktop's Linux engine was not running. The frontend build used the existing three-page SEO fallback because the local API was unavailable; the existing large-chunk warning remains. The migration was generated but not applied to a live database in this session. Authenticated browser visual verification was not performed. - -Paths below are relative to the repository root. - -### Backend - -- `backend/src/MathArchive.Domain/Analytics/AnalyticsEvent.cs` -- `backend/src/MathArchive.Application/Analytics/AnalyticsContracts.cs` -- `backend/src/MathArchive.Application/Analytics/AnalyticsService.cs` -- `backend/src/MathArchive.Application/DependencyInjection.cs` -- `backend/src/MathArchive.Infrastructure/DependencyInjection.cs` -- `backend/src/MathArchive.Infrastructure/Persistence/MathArchiveDbContext.cs` -- `backend/src/MathArchive.Infrastructure/Persistence/AnalyticsEventConfiguration.cs` -- `backend/src/MathArchive.Infrastructure/Persistence/AnalyticsRepository.cs` -- `backend/src/MathArchive.Infrastructure/Migrations/20260831135409_AddAnalyticsEvents.cs` -- `backend/src/MathArchive.Infrastructure/Migrations/20260831135409_AddAnalyticsEvents.Designer.cs` -- `backend/src/MathArchive.Infrastructure/Migrations/MathArchiveDbContextModelSnapshot.cs` -- `backend/src/MathArchive.Api/Controllers/AnalyticsController.cs` -- `backend/src/MathArchive.Api/Controllers/AdminAnalyticsController.cs` -- `backend/tests/MathArchive.Application.Tests/AnalyticsServiceTests.cs` -- `backend/tests/MathArchive.Application.Tests/Integration/AnalyticsApiIntegrationTests.cs` -- `backend/tests/MathArchive.Application.Tests/Integration/ApiIntegrationFixture.cs` - -### Frontend and documentation - -- `frontend/math-archive-web/src/api/analyticsApi.ts` -- `frontend/math-archive-web/src/api/analyticsApi.test.tsx` -- `frontend/math-archive-web/src/api/queryKeys.ts` -- `frontend/math-archive-web/src/utils/analyticsDates.ts` -- `frontend/math-archive-web/src/utils/analyticsDates.test.ts` -- `frontend/math-archive-web/src/pages/admin/AnalyticsPage.tsx` -- `frontend/math-archive-web/src/pages/admin/AnalyticsPage.test.tsx` -- `frontend/math-archive-web/src/pages/DocumentDetailsPage.tsx` -- `frontend/math-archive-web/src/pages/DocumentDetailsPage.test.tsx` -- `frontend/math-archive-web/src/layouts/PublicLayout.tsx` -- `frontend/math-archive-web/src/layouts/AdminLayout.tsx` -- `frontend/math-archive-web/src/App.tsx` -- `docs/analytics.md` +## Refactor rationale + +The previous metric omitted the card/details download buttons and described explicit file opens as "views". The implementation's recorded live trace found that the existing keepalive transport returned 204 and persisted the open-link event; it did not reproduce a transport failure. The refactor expanded action coverage rather than changing PDF-viewer behavior or adding retries that could double-count events. + +## Implementation references + +- [Public dispatch and report types](../frontend/math-archive-web/src/api/analyticsApi.ts) +- [Card download action](../frontend/math-archive-web/src/components/DocumentCard.tsx) +- [Preview and details file actions](../frontend/math-archive-web/src/pages/DocumentDetailsPage.tsx) +- [Admin statistics UI](../frontend/math-archive-web/src/pages/admin/AnalyticsPage.tsx) +- [Application contracts](../backend/src/MathArchive.Application/Analytics/AnalyticsContracts.cs) +- [Validation and recording](../backend/src/MathArchive.Application/Analytics/AnalyticsService.cs) +- [Database aggregation](../backend/src/MathArchive.Infrastructure/Persistence/AnalyticsRepository.cs) +- [Event-name migration](../backend/src/MathArchive.Infrastructure/Migrations/20260831145701_RenameDocumentViewToDocumentDownload.cs) + +## Historical verification record + +The download-refactor implementation report recorded 87 passing backend tests (including PostgreSQL and migration tests), 73 passing frontend tests, successful builds, and no pending EF model changes. It also recorded a browser run in which each of the three MathArchive file controls sent one `DocumentDownload` POST with the selected document ID, received 204, and added one database event. Preview opening added only a preview event. + +These are historical implementation results, not a new test run or production deployment confirmation. The local rename migration was applied during that verification; production still requires coordinated backend/migration and frontend deployment. Run the commands above to verify the current checkout. diff --git a/docs/course-project/README.md b/docs/course-project/README.md index c106729..2dda391 100644 --- a/docs/course-project/README.md +++ b/docs/course-project/README.md @@ -67,6 +67,7 @@ Never perform steps 2-4 against the only production copy of a source document. ## Verification ```powershell +docker compose up -d postgres dotnet restore backend/MathArchive.sln dotnet build backend/MathArchive.sln --no-restore dotnet test backend/MathArchive.sln --no-build @@ -74,6 +75,7 @@ dotnet test backend/MathArchive.sln --no-build cd frontend/math-archive-web npm ci npm test +$env:VITE_API_BASE_URL='http://localhost:5293' npm run build ```