mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-05-31 17:53:59 +00:00
Format all markdown files in active directories
This commit is contained in:
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -7,6 +7,7 @@ assignees: []
|
|||||||
---
|
---
|
||||||
|
|
||||||
## User Story
|
## User Story
|
||||||
|
|
||||||
**As a** (who wants to accomplish something)
|
**As a** (who wants to accomplish something)
|
||||||
**I want to** (what they want to accomplish)
|
**I want to** (what they want to accomplish)
|
||||||
**So that** (why they want to accomplish that thing)
|
**So that** (why they want to accomplish that thing)
|
||||||
@@ -15,29 +16,18 @@ assignees: []
|
|||||||
|
|
||||||
### Scenario 1
|
### Scenario 1
|
||||||
|
|
||||||
|
Given ... When ... Then ...
|
||||||
Given ...
|
|
||||||
When ...
|
|
||||||
Then ...
|
|
||||||
|
|
||||||
|
|
||||||
### Scenario 2
|
### Scenario 2
|
||||||
|
|
||||||
|
Given ... When ... Then ...
|
||||||
Given ...
|
|
||||||
When ...
|
|
||||||
Then ...
|
|
||||||
|
|
||||||
|
|
||||||
### Scenario 3
|
### Scenario 3
|
||||||
|
|
||||||
|
Given ... When ... Then ...
|
||||||
Given ...
|
|
||||||
When ...
|
|
||||||
Then ...
|
|
||||||
|
|
||||||
|
|
||||||
## Subtasks
|
## Subtasks
|
||||||
|
|
||||||
- [ ] Task 1
|
- [ ] Task 1
|
||||||
- [ ] Task 2
|
- [ ] Task 2
|
||||||
- [ ] Task 3
|
- [ ] Task 3
|
||||||
|
|||||||
962
LICENSE.md
962
LICENSE.md
File diff suppressed because it is too large
Load Diff
46
README.md
46
README.md
@@ -1,25 +1,33 @@
|
|||||||
# The Biergarten App
|
# The Biergarten App
|
||||||
|
|
||||||
The Biergarten App is a multi-project monorepo with a .NET backend and an active React
|
The Biergarten App is a multi-project monorepo with a .NET backend and an active
|
||||||
Router frontend in `src/Website`. The current website focuses on account flows, theme
|
React Router frontend in `src/Website`. The current website focuses on account
|
||||||
switching, shared UI components, Storybook coverage, and integration with the API.
|
flows, theme switching, shared UI components, Storybook coverage, and
|
||||||
|
integration with the API.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
- [Getting Started](docs/getting-started.md) - Local setup for backend and active website
|
- [Getting Started](docs/getting-started.md) - Local setup for backend and
|
||||||
- [Architecture](docs/architecture.md) - Current backend and frontend architecture
|
active website
|
||||||
- [Docker Guide](docs/docker.md) - Container-based backend development and testing
|
- [Architecture](docs/architecture.md) - Current backend and frontend
|
||||||
|
architecture
|
||||||
|
- [Docker Guide](docs/docker.md) - Container-based backend development and
|
||||||
|
testing
|
||||||
- [Testing](docs/testing.md) - Backend and frontend test commands
|
- [Testing](docs/testing.md) - Backend and frontend test commands
|
||||||
- [Environment Variables](docs/environment-variables.md) - Active configuration reference
|
- [Environment Variables](docs/environment-variables.md) - Active configuration
|
||||||
|
reference
|
||||||
- [Token Validation](docs/token-validation.md) - JWT validation architecture
|
- [Token Validation](docs/token-validation.md) - JWT validation architecture
|
||||||
- [Legacy Website Archive](docs/archive/legacy-website-v1.md) - Archived notes for the old Next.js frontend
|
- [Legacy Website Archive](docs/archive/legacy-website-v1.md) - Archived notes
|
||||||
|
for the old Next.js frontend
|
||||||
|
|
||||||
## Diagrams
|
## Diagrams
|
||||||
|
|
||||||
- [Architecture](docs/diagrams-out/architecture.svg) - Layered architecture
|
- [Architecture](docs/diagrams-out/architecture.svg) - Layered architecture
|
||||||
- [Deployment](docs/diagrams-out/deployment.svg) - Docker topology
|
- [Deployment](docs/diagrams-out/deployment.svg) - Docker topology
|
||||||
- [Authentication Flow](docs/diagrams-out/authentication-flow.svg) - Auth sequence
|
- [Authentication Flow](docs/diagrams-out/authentication-flow.svg) - Auth
|
||||||
- [Database Schema](docs/diagrams-out/database-schema.svg) - Entity relationships
|
sequence
|
||||||
|
- [Database Schema](docs/diagrams-out/database-schema.svg) - Entity
|
||||||
|
relationships
|
||||||
|
|
||||||
## Current Status
|
## Current Status
|
||||||
|
|
||||||
@@ -34,7 +42,8 @@ Active areas in the repository:
|
|||||||
|
|
||||||
Legacy area retained for reference:
|
Legacy area retained for reference:
|
||||||
|
|
||||||
- `src/Website-v1` contains the archived Next.js frontend and is no longer the active website
|
- `src/Website-v1` contains the archived Next.js frontend and is no longer the
|
||||||
|
active website
|
||||||
|
|
||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|
||||||
@@ -43,7 +52,8 @@ Legacy area retained for reference:
|
|||||||
- **UI Documentation**: Storybook 10, Vitest browser mode, Playwright
|
- **UI Documentation**: Storybook 10, Vitest browser mode, Playwright
|
||||||
- **Testing**: xUnit, Reqnroll (BDD), FluentAssertions, Moq
|
- **Testing**: xUnit, Reqnroll (BDD), FluentAssertions, Moq
|
||||||
- **Infrastructure**: Docker, Docker Compose
|
- **Infrastructure**: Docker, Docker Compose
|
||||||
- **Security**: Argon2id password hashing, JWT access/refresh/confirmation tokens
|
- **Security**: Argon2id password hashing, JWT access/refresh/confirmation
|
||||||
|
tokens
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
@@ -133,7 +143,8 @@ See [Testing](docs/testing.md) for the full command list.
|
|||||||
|
|
||||||
Common active variables:
|
Common active variables:
|
||||||
|
|
||||||
- Backend: `DB_SERVER`, `DB_NAME`, `DB_USER`, `DB_PASSWORD`, `ACCESS_TOKEN_SECRET`, `REFRESH_TOKEN_SECRET`, `CONFIRMATION_TOKEN_SECRET`
|
- Backend: `DB_SERVER`, `DB_NAME`, `DB_USER`, `DB_PASSWORD`,
|
||||||
|
`ACCESS_TOKEN_SECRET`, `REFRESH_TOKEN_SECRET`, `CONFIRMATION_TOKEN_SECRET`
|
||||||
- Frontend: `API_BASE_URL`, `SESSION_SECRET`, `NODE_ENV`
|
- Frontend: `API_BASE_URL`, `SESSION_SECRET`, `NODE_ENV`
|
||||||
|
|
||||||
See [Environment Variables](docs/environment-variables.md) for details.
|
See [Environment Variables](docs/environment-variables.md) for details.
|
||||||
@@ -148,10 +159,13 @@ See [Environment Variables](docs/environment-variables.md) for details.
|
|||||||
|
|
||||||
### Development Workflow
|
### Development Workflow
|
||||||
|
|
||||||
1. Start development environment: `docker compose -f docker-compose.dev.yaml up -d`
|
1. Start development environment:
|
||||||
|
`docker compose -f docker-compose.dev.yaml up -d`
|
||||||
2. Make changes to code
|
2. Make changes to code
|
||||||
3. Run tests: `docker compose -f docker-compose.test.yaml up --abort-on-container-exit`
|
3. Run tests:
|
||||||
4. Rebuild if needed: `docker compose -f docker-compose.dev.yaml up -d --build api.core`
|
`docker compose -f docker-compose.test.yaml up --abort-on-container-exit`
|
||||||
|
4. Rebuild if needed:
|
||||||
|
`docker compose -f docker-compose.dev.yaml up -d --build api.core`
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
|
|||||||
@@ -4,24 +4,28 @@ This document describes the active architecture of The Biergarten App.
|
|||||||
|
|
||||||
## High-Level Overview
|
## High-Level Overview
|
||||||
|
|
||||||
The Biergarten App is a monorepo with a clear split between the backend and the active
|
The Biergarten App is a monorepo with a clear split between the backend and the
|
||||||
website:
|
active website:
|
||||||
|
|
||||||
- **Backend**: .NET 10 Web API with SQL Server and a layered architecture
|
- **Backend**: .NET 10 Web API with SQL Server and a layered architecture
|
||||||
- **Frontend**: React 19 + React Router 7 website in `src/Website`
|
- **Frontend**: React 19 + React Router 7 website in `src/Website`
|
||||||
- **Architecture Style**: Layered backend plus server-rendered React frontend
|
- **Architecture Style**: Layered backend plus server-rendered React frontend
|
||||||
|
|
||||||
The legacy Next.js frontend has been retained in `src/Website-v1` for reference only and is
|
The legacy Next.js frontend has been retained in `src/Website-v1` for reference
|
||||||
documented in [archive/legacy-website-v1.md](archive/legacy-website-v1.md).
|
only and is documented in
|
||||||
|
[archive/legacy-website-v1.md](archive/legacy-website-v1.md).
|
||||||
|
|
||||||
## Diagrams
|
## Diagrams
|
||||||
|
|
||||||
For visual representations, see:
|
For visual representations, see:
|
||||||
|
|
||||||
- [architecture.svg](diagrams-out/architecture.svg) - Layered architecture diagram
|
- [architecture.svg](diagrams-out/architecture.svg) - Layered architecture
|
||||||
|
diagram
|
||||||
- [deployment.svg](diagrams-out/deployment.svg) - Docker deployment diagram
|
- [deployment.svg](diagrams-out/deployment.svg) - Docker deployment diagram
|
||||||
- [authentication-flow.svg](diagrams-out/authentication-flow.svg) - Authentication workflow
|
- [authentication-flow.svg](diagrams-out/authentication-flow.svg) -
|
||||||
- [database-schema.svg](diagrams-out/database-schema.svg) - Database relationships
|
Authentication workflow
|
||||||
|
- [database-schema.svg](diagrams-out/database-schema.svg) - Database
|
||||||
|
relationships
|
||||||
|
|
||||||
## Backend Architecture
|
## Backend Architecture
|
||||||
|
|
||||||
@@ -218,7 +222,8 @@ public interface IAuthRepository
|
|||||||
|
|
||||||
### Active Website (`src/Website`)
|
### Active Website (`src/Website`)
|
||||||
|
|
||||||
The current website is a React Router 7 application with server-side rendering enabled.
|
The current website is a React Router 7 application with server-side rendering
|
||||||
|
enabled.
|
||||||
|
|
||||||
```text
|
```text
|
||||||
src/Website/
|
src/Website/
|
||||||
@@ -244,20 +249,22 @@ src/Website/
|
|||||||
|
|
||||||
### Theme System
|
### Theme System
|
||||||
|
|
||||||
The active website uses semantic DaisyUI theme tokens backed by four Biergarten themes:
|
The active website uses semantic DaisyUI theme tokens backed by four Biergarten
|
||||||
|
themes:
|
||||||
|
|
||||||
- Biergarten Lager
|
- Biergarten Lager
|
||||||
- Biergarten Stout
|
- Biergarten Stout
|
||||||
- Biergarten Cassis
|
- Biergarten Cassis
|
||||||
- Biergarten Weizen
|
- Biergarten Weizen
|
||||||
|
|
||||||
All component styling should prefer semantic tokens such as `primary`, `success`,
|
All component styling should prefer semantic tokens such as `primary`,
|
||||||
`surface`, and `highlight` instead of hard-coded color values.
|
`success`, `surface`, and `highlight` instead of hard-coded color values.
|
||||||
|
|
||||||
### Legacy Frontend
|
### Legacy Frontend
|
||||||
|
|
||||||
The previous Next.js frontend has been archived at `src/Website-v1`. Active product and
|
The previous Next.js frontend has been archived at `src/Website-v1`. Active
|
||||||
engineering documentation should point to `src/Website`, while legacy notes live in
|
product and engineering documentation should point to `src/Website`, while
|
||||||
|
legacy notes live in
|
||||||
[archive/legacy-website-v1.md](archive/legacy-website-v1.md).
|
[archive/legacy-website-v1.md](archive/legacy-website-v1.md).
|
||||||
|
|
||||||
## Security Architecture
|
## Security Architecture
|
||||||
@@ -387,8 +394,8 @@ For details, see [Docker Guide](docker.md).
|
|||||||
|
|
||||||
### Health Checks
|
### Health Checks
|
||||||
|
|
||||||
**SQL Server**: Validates database connectivity **API**: Checks service health and
|
**SQL Server**: Validates database connectivity **API**: Checks service health
|
||||||
dependencies
|
and dependencies
|
||||||
|
|
||||||
**Configuration**:
|
**Configuration**:
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Docker Guide
|
# Docker Guide
|
||||||
|
|
||||||
This document covers Docker deployment, configuration, and troubleshooting for The
|
This document covers Docker deployment, configuration, and troubleshooting for
|
||||||
Biergarten App.
|
The Biergarten App.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -13,7 +13,8 @@ The project uses Docker Compose to orchestrate multiple services:
|
|||||||
- .NET API
|
- .NET API
|
||||||
- Test runners
|
- Test runners
|
||||||
|
|
||||||
See the [deployment diagram](diagrams/pdf/deployment.pdf) for visual representation.
|
See the [deployment diagram](diagrams/pdf/deployment.pdf) for visual
|
||||||
|
representation.
|
||||||
|
|
||||||
## Docker Compose Environments
|
## Docker Compose Environments
|
||||||
|
|
||||||
@@ -144,7 +145,11 @@ api.core / tests (start when ready)
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ['CMD-SHELL', "sqlcmd -S localhost -U sa -P '${DB_PASSWORD}' -C -Q 'SELECT 1'"]
|
test:
|
||||||
|
[
|
||||||
|
"CMD-SHELL",
|
||||||
|
"sqlcmd -S localhost -U sa -P '${DB_PASSWORD}' -C -Q 'SELECT 1'",
|
||||||
|
]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 12
|
retries: 12
|
||||||
@@ -209,16 +214,16 @@ Each environment uses isolated bridge networks:
|
|||||||
All containers are configured via environment variables from `.env` files:
|
All containers are configured via environment variables from `.env` files:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
env_file: '.env.dev' # or .env.test, .env.prod
|
env_file: ".env.dev" # or .env.test, .env.prod
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
ASPNETCORE_ENVIRONMENT: 'Development'
|
ASPNETCORE_ENVIRONMENT: "Development"
|
||||||
DOTNET_RUNNING_IN_CONTAINER: 'true'
|
DOTNET_RUNNING_IN_CONTAINER: "true"
|
||||||
DB_SERVER: '${DB_SERVER}'
|
DB_SERVER: "${DB_SERVER}"
|
||||||
DB_NAME: '${DB_NAME}'
|
DB_NAME: "${DB_NAME}"
|
||||||
DB_USER: '${DB_USER}'
|
DB_USER: "${DB_USER}"
|
||||||
DB_PASSWORD: '${DB_PASSWORD}'
|
DB_PASSWORD: "${DB_PASSWORD}"
|
||||||
JWT_SECRET: '${JWT_SECRET}'
|
JWT_SECRET: "${JWT_SECRET}"
|
||||||
```
|
```
|
||||||
|
|
||||||
For complete list, see [Environment Variables](environment-variables.md).
|
For complete list, see [Environment Variables](environment-variables.md).
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Environment Variables
|
# Environment Variables
|
||||||
|
|
||||||
This document covers the active environment variables used by the current Biergarten
|
This document covers the active environment variables used by the current
|
||||||
stack.
|
Biergarten stack.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -19,8 +19,8 @@ Direct environment variable access via `Environment.GetEnvironmentVariable()`.
|
|||||||
|
|
||||||
### Frontend (`src/Website`)
|
### Frontend (`src/Website`)
|
||||||
|
|
||||||
The active website reads runtime values from the server environment for its auth and API
|
The active website reads runtime values from the server environment for its auth
|
||||||
integration.
|
and API integration.
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
|
|
||||||
@@ -54,14 +54,15 @@ Provide complete connection string:
|
|||||||
DB_CONNECTION_STRING="Server=localhost,1433;Database=Biergarten;User Id=sa;Password=YourStrong!Passw0rd;TrustServerCertificate=True;"
|
DB_CONNECTION_STRING="Server=localhost,1433;Database=Biergarten;User Id=sa;Password=YourStrong!Passw0rd;TrustServerCertificate=True;"
|
||||||
```
|
```
|
||||||
|
|
||||||
**Priority**: `DB_CONNECTION_STRING` is checked first. If not found, connection string is
|
**Priority**: `DB_CONNECTION_STRING` is checked first. If not found, connection
|
||||||
built from components.
|
string is built from components.
|
||||||
|
|
||||||
**Implementation**: See `DefaultSqlConnectionFactory.cs`
|
**Implementation**: See `DefaultSqlConnectionFactory.cs`
|
||||||
|
|
||||||
### JWT Authentication Secrets (Backend)
|
### JWT Authentication Secrets (Backend)
|
||||||
|
|
||||||
The backend uses separate secrets for different token types to enable independent key rotation and validation isolation.
|
The backend uses separate secrets for different token types to enable
|
||||||
|
independent key rotation and validation isolation.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Access token secret (1-hour tokens)
|
# Access token secret (1-hour tokens)
|
||||||
@@ -131,8 +132,8 @@ DOTNET_RUNNING_IN_CONTAINER=true # Flag for container execution
|
|||||||
|
|
||||||
## Frontend Variables (`src/Website`)
|
## Frontend Variables (`src/Website`)
|
||||||
|
|
||||||
The active website does not use the old Next.js/Prisma environment model. Its core runtime
|
The active website does not use the old Next.js/Prisma environment model. Its
|
||||||
variables are:
|
core runtime variables are:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
API_BASE_URL=http://localhost:8080 # Base URL for the .NET API
|
API_BASE_URL=http://localhost:8080 # Base URL for the .NET API
|
||||||
@@ -208,9 +209,10 @@ cp .env.example .env.dev
|
|||||||
|
|
||||||
## Legacy Frontend Variables
|
## Legacy Frontend Variables
|
||||||
|
|
||||||
Variables for the archived Next.js frontend (`src/Website-v1`) have been removed from this
|
Variables for the archived Next.js frontend (`src/Website-v1`) have been removed
|
||||||
active reference. See [archive/legacy-website-v1.md](archive/legacy-website-v1.md) if you
|
from this active reference. See
|
||||||
need the legacy Prisma, Cloudinary, Mapbox, or SparkPost notes.
|
[archive/legacy-website-v1.md](archive/legacy-website-v1.md) if you need the
|
||||||
|
legacy Prisma, Cloudinary, Mapbox, or SparkPost notes.
|
||||||
|
|
||||||
**Docker Compose Mapping**:
|
**Docker Compose Mapping**:
|
||||||
|
|
||||||
@@ -243,8 +245,8 @@ need the legacy Prisma, Cloudinary, Mapbox, or SparkPost notes.
|
|||||||
| `MSSQL_PID` | | | ✓ | No | SQL Server edition |
|
| `MSSQL_PID` | | | ✓ | No | SQL Server edition |
|
||||||
| `DOTNET_RUNNING_IN_CONTAINER` | ✓ | | ✓ | No | Container flag |
|
| `DOTNET_RUNNING_IN_CONTAINER` | ✓ | | ✓ | No | Container flag |
|
||||||
|
|
||||||
\* Either `DB_CONNECTION_STRING` OR the component variables (`DB_SERVER`, `DB_NAME`,
|
\* Either `DB_CONNECTION_STRING` OR the component variables (`DB_SERVER`,
|
||||||
`DB_USER`, `DB_PASSWORD`) must be provided.
|
`DB_NAME`, `DB_USER`, `DB_PASSWORD`) must be provided.
|
||||||
|
|
||||||
## Validation
|
## Validation
|
||||||
|
|
||||||
@@ -258,8 +260,8 @@ Variables are validated at startup:
|
|||||||
|
|
||||||
### Frontend Validation
|
### Frontend Validation
|
||||||
|
|
||||||
The active website relies on runtime defaults for local development and the surrounding
|
The active website relies on runtime defaults for local development and the
|
||||||
server environment in deployed environments.
|
surrounding server environment in deployed environments.
|
||||||
|
|
||||||
- `API_BASE_URL` defaults to `http://localhost:8080`
|
- `API_BASE_URL` defaults to `http://localhost:8080`
|
||||||
- `SESSION_SECRET` falls back to a development-only local secret
|
- `SESSION_SECRET` falls back to a development-only local secret
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Getting Started
|
# Getting Started
|
||||||
|
|
||||||
This guide covers local setup for the current Biergarten stack: the .NET backend in
|
This guide covers local setup for the current Biergarten stack: the .NET backend
|
||||||
`src/Core` and the active React Router frontend in `src/Website`.
|
in `src/Core` and the active React Router frontend in `src/Website`.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
@@ -128,8 +128,9 @@ dotnet run --project API/API.Core/API.Core.csproj
|
|||||||
|
|
||||||
## Legacy Frontend Note
|
## Legacy Frontend Note
|
||||||
|
|
||||||
The previous Next.js frontend now lives in `src/Website-v1` and is not the active website.
|
The previous Next.js frontend now lives in `src/Website-v1` and is not the
|
||||||
Legacy setup details have been moved to [docs/archive/legacy-website-v1.md](archive/legacy-website-v1.md).
|
active website. Legacy setup details have been moved to
|
||||||
|
[docs/archive/legacy-website-v1.md](archive/legacy-website-v1.md).
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Testing
|
# Testing
|
||||||
|
|
||||||
This document describes the testing strategy and how to run tests for The Biergarten App.
|
This document describes the testing strategy and how to run tests for The
|
||||||
|
Biergarten App.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -9,13 +10,15 @@ The project uses a multi-layered testing approach across backend and frontend:
|
|||||||
- **API.Specs** - BDD integration tests using Reqnroll (Gherkin)
|
- **API.Specs** - BDD integration tests using Reqnroll (Gherkin)
|
||||||
- **Infrastructure.Repository.Tests** - Unit tests for data access layer
|
- **Infrastructure.Repository.Tests** - Unit tests for data access layer
|
||||||
- **Service.Auth.Tests** - Unit tests for authentication business logic
|
- **Service.Auth.Tests** - Unit tests for authentication business logic
|
||||||
- **Storybook Vitest project** - Browser-based interaction tests for shared website stories
|
- **Storybook Vitest project** - Browser-based interaction tests for shared
|
||||||
- **Storybook Playwright suite** - Browser checks against Storybook-rendered components
|
website stories
|
||||||
|
- **Storybook Playwright suite** - Browser checks against Storybook-rendered
|
||||||
|
components
|
||||||
|
|
||||||
## Running Tests with Docker (Recommended)
|
## Running Tests with Docker (Recommended)
|
||||||
|
|
||||||
The easiest way to run all tests is using Docker Compose, which sets up an isolated test
|
The easiest way to run all tests is using Docker Compose, which sets up an
|
||||||
environment:
|
isolated test environment:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose -f docker-compose.test.yaml up --abort-on-container-exit
|
docker compose -f docker-compose.test.yaml up --abort-on-container-exit
|
||||||
@@ -98,7 +101,8 @@ npm run test:storybook
|
|||||||
|
|
||||||
**Purpose**:
|
**Purpose**:
|
||||||
|
|
||||||
- Verifies shared stories such as form fields, submit buttons, navbar states, toasts, and the theme gallery
|
- Verifies shared stories such as form fields, submit buttons, navbar states,
|
||||||
|
toasts, and the theme gallery
|
||||||
- Runs in browser mode via Vitest and Storybook integration
|
- Runs in browser mode via Vitest and Storybook integration
|
||||||
|
|
||||||
### Frontend Playwright Storybook Tests
|
### Frontend Playwright Storybook Tests
|
||||||
@@ -113,7 +117,8 @@ npm run test:storybook:playwright
|
|||||||
|
|
||||||
- Storybook dependencies installed
|
- Storybook dependencies installed
|
||||||
- Playwright browser dependencies installed
|
- Playwright browser dependencies installed
|
||||||
- The command will start or reuse the Storybook server defined in `playwright.storybook.config.ts`
|
- The command will start or reuse the Storybook server defined in
|
||||||
|
`playwright.storybook.config.ts`
|
||||||
|
|
||||||
## Test Coverage
|
## Test Coverage
|
||||||
|
|
||||||
@@ -278,7 +283,8 @@ Scenario: User login with valid credentials
|
|||||||
|
|
||||||
## Continuous Integration
|
## Continuous Integration
|
||||||
|
|
||||||
Tests run automatically in CI/CD pipelines using the test Docker Compose configuration:
|
Tests run automatically in CI/CD pipelines using the test Docker Compose
|
||||||
|
configuration:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# CI/CD command
|
# CI/CD command
|
||||||
@@ -292,7 +298,8 @@ Exit codes:
|
|||||||
- `0` - All tests passed
|
- `0` - All tests passed
|
||||||
- Non-zero - Test failures occurred
|
- Non-zero - Test failures occurred
|
||||||
|
|
||||||
Frontend UI checks should also be included in CI for the active website workspace:
|
Frontend UI checks should also be included in CI for the active website
|
||||||
|
workspace:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd src/Website
|
cd src/Website
|
||||||
|
|||||||
@@ -2,11 +2,14 @@
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The Core project implements comprehensive JWT token validation across three token types:
|
The Core project implements comprehensive JWT token validation across three
|
||||||
|
token types:
|
||||||
|
|
||||||
- **Access Tokens**: Short-lived (1 hour) tokens for API authentication
|
- **Access Tokens**: Short-lived (1 hour) tokens for API authentication
|
||||||
- **Refresh Tokens**: Long-lived (21 days) tokens for obtaining new access tokens
|
- **Refresh Tokens**: Long-lived (21 days) tokens for obtaining new access
|
||||||
- **Confirmation Tokens**: Short-lived (30 minutes) tokens for email confirmation
|
tokens
|
||||||
|
- **Confirmation Tokens**: Short-lived (30 minutes) tokens for email
|
||||||
|
confirmation
|
||||||
|
|
||||||
## Components
|
## Components
|
||||||
|
|
||||||
@@ -17,10 +20,13 @@ The Core project implements comprehensive JWT token validation across three toke
|
|||||||
Low-level JWT operations.
|
Low-level JWT operations.
|
||||||
|
|
||||||
**Methods:**
|
**Methods:**
|
||||||
|
|
||||||
- `GenerateJwt()` - Creates signed JWT tokens
|
- `GenerateJwt()` - Creates signed JWT tokens
|
||||||
- `ValidateJwtAsync()` - Validates token signature, expiration, and format
|
- `ValidateJwtAsync()` - Validates token signature, expiration, and format
|
||||||
|
|
||||||
**Implementation:** [JwtInfrastructure.cs](Infrastructure.Jwt/JwtInfrastructure.cs)
|
**Implementation:**
|
||||||
|
[JwtInfrastructure.cs](Infrastructure.Jwt/JwtInfrastructure.cs)
|
||||||
|
|
||||||
- Uses Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler
|
- Uses Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler
|
||||||
- Algorithm: HS256 (HMAC-SHA256)
|
- Algorithm: HS256 (HMAC-SHA256)
|
||||||
- Validates token lifetime, signature, and well-formedness
|
- Validates token lifetime, signature, and well-formedness
|
||||||
@@ -32,16 +38,20 @@ Low-level JWT operations.
|
|||||||
High-level token validation with context (token type, user extraction).
|
High-level token validation with context (token type, user extraction).
|
||||||
|
|
||||||
**Methods:**
|
**Methods:**
|
||||||
|
|
||||||
- `ValidateAccessTokenAsync(string token)` - Validates access tokens
|
- `ValidateAccessTokenAsync(string token)` - Validates access tokens
|
||||||
- `ValidateRefreshTokenAsync(string token)` - Validates refresh tokens
|
- `ValidateRefreshTokenAsync(string token)` - Validates refresh tokens
|
||||||
- `ValidateConfirmationTokenAsync(string token)` - Validates confirmation tokens
|
- `ValidateConfirmationTokenAsync(string token)` - Validates confirmation tokens
|
||||||
|
|
||||||
**Returns:** `ValidatedToken` record containing:
|
**Returns:** `ValidatedToken` record containing:
|
||||||
|
|
||||||
- `UserId` (Guid)
|
- `UserId` (Guid)
|
||||||
- `Username` (string)
|
- `Username` (string)
|
||||||
- `Principal` (ClaimsPrincipal) - Full JWT claims
|
- `Principal` (ClaimsPrincipal) - Full JWT claims
|
||||||
|
|
||||||
**Implementation:** [TokenValidationService.cs](Service.Auth/TokenValidationService.cs)
|
**Implementation:**
|
||||||
|
[TokenValidationService.cs](Service.Auth/TokenValidationService.cs)
|
||||||
|
|
||||||
- Reads token secrets from environment variables
|
- Reads token secrets from environment variables
|
||||||
- Extracts and validates claims (Sub, UniqueName)
|
- Extracts and validates claims (Sub, UniqueName)
|
||||||
- Throws `UnauthorizedException` on validation failure
|
- Throws `UnauthorizedException` on validation failure
|
||||||
@@ -51,15 +61,18 @@ High-level token validation with context (token type, user extraction).
|
|||||||
Token generation (existing service extended).
|
Token generation (existing service extended).
|
||||||
|
|
||||||
**Methods:**
|
**Methods:**
|
||||||
|
|
||||||
- `GenerateAccessToken(UserAccount)` - Creates 1-hour access token
|
- `GenerateAccessToken(UserAccount)` - Creates 1-hour access token
|
||||||
- `GenerateRefreshToken(UserAccount)` - Creates 21-day refresh token
|
- `GenerateRefreshToken(UserAccount)` - Creates 21-day refresh token
|
||||||
- `GenerateConfirmationToken(UserAccount)` - Creates 30-minute confirmation token
|
- `GenerateConfirmationToken(UserAccount)` - Creates 30-minute confirmation
|
||||||
|
token
|
||||||
|
|
||||||
### Integration Points
|
### Integration Points
|
||||||
|
|
||||||
#### [ConfirmationService](Service.Auth/IConfirmationService.cs)
|
#### [ConfirmationService](Service.Auth/IConfirmationService.cs)
|
||||||
|
|
||||||
**Flow:**
|
**Flow:**
|
||||||
|
|
||||||
1. Receives confirmation token from user
|
1. Receives confirmation token from user
|
||||||
2. Calls `TokenValidationService.ValidateConfirmationTokenAsync()`
|
2. Calls `TokenValidationService.ValidateConfirmationTokenAsync()`
|
||||||
3. Extracts user ID from validated token
|
3. Extracts user ID from validated token
|
||||||
@@ -69,6 +82,7 @@ Token generation (existing service extended).
|
|||||||
#### [RefreshTokenService](Service.Auth/RefreshTokenService.cs)
|
#### [RefreshTokenService](Service.Auth/RefreshTokenService.cs)
|
||||||
|
|
||||||
**Flow:**
|
**Flow:**
|
||||||
|
|
||||||
1. Receives refresh token from user
|
1. Receives refresh token from user
|
||||||
2. Calls `TokenValidationService.ValidateRefreshTokenAsync()`
|
2. Calls `TokenValidationService.ValidateRefreshTokenAsync()`
|
||||||
3. Retrieves user account via `AuthRepository.GetUserByIdAsync()`
|
3. Retrieves user account via `AuthRepository.GetUserByIdAsync()`
|
||||||
@@ -78,6 +92,7 @@ Token generation (existing service extended).
|
|||||||
#### [AuthController](API.Core/Controllers/AuthController.cs)
|
#### [AuthController](API.Core/Controllers/AuthController.cs)
|
||||||
|
|
||||||
**Endpoints:**
|
**Endpoints:**
|
||||||
|
|
||||||
- `POST /api/auth/register` - Register new user
|
- `POST /api/auth/register` - Register new user
|
||||||
- `POST /api/auth/login` - Authenticate user
|
- `POST /api/auth/login` - Authenticate user
|
||||||
- `POST /api/auth/confirm?token=...` - Confirm email
|
- `POST /api/auth/confirm?token=...` - Confirm email
|
||||||
@@ -88,11 +103,13 @@ Token generation (existing service extended).
|
|||||||
### Token Secrets
|
### Token Secrets
|
||||||
|
|
||||||
Three independent secrets enable:
|
Three independent secrets enable:
|
||||||
|
|
||||||
- **Key rotation** - Rotate each secret type independently
|
- **Key rotation** - Rotate each secret type independently
|
||||||
- **Isolation** - Compromise of one secret doesn't affect others
|
- **Isolation** - Compromise of one secret doesn't affect others
|
||||||
- **Different expiration** - Different token types can expire at different rates
|
- **Different expiration** - Different token types can expire at different rates
|
||||||
|
|
||||||
**Environment Variables:**
|
**Environment Variables:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ACCESS_TOKEN_SECRET=... # Signs 1-hour access tokens
|
ACCESS_TOKEN_SECRET=... # Signs 1-hour access tokens
|
||||||
REFRESH_TOKEN_SECRET=... # Signs 21-day refresh tokens
|
REFRESH_TOKEN_SECRET=... # Signs 21-day refresh tokens
|
||||||
@@ -111,6 +128,7 @@ Each token is validated for:
|
|||||||
### Error Handling
|
### Error Handling
|
||||||
|
|
||||||
Validation failures return HTTP 401 Unauthorized:
|
Validation failures return HTTP 401 Unauthorized:
|
||||||
|
|
||||||
- Invalid signature → "Invalid token"
|
- Invalid signature → "Invalid token"
|
||||||
- Expired token → "Invalid token" (message doesn't reveal reason for security)
|
- Expired token → "Invalid token" (message doesn't reveal reason for security)
|
||||||
- Missing claims → "Invalid token"
|
- Missing claims → "Invalid token"
|
||||||
@@ -149,16 +167,19 @@ Validation failures return HTTP 401 Unauthorized:
|
|||||||
### Unit Tests
|
### Unit Tests
|
||||||
|
|
||||||
**TokenValidationService.test.cs**
|
**TokenValidationService.test.cs**
|
||||||
|
|
||||||
- Happy path: Valid token extraction
|
- Happy path: Valid token extraction
|
||||||
- Error cases: Invalid, expired, malformed tokens
|
- Error cases: Invalid, expired, malformed tokens
|
||||||
- Missing/invalid claims scenarios
|
- Missing/invalid claims scenarios
|
||||||
|
|
||||||
**RefreshTokenService.test.cs**
|
**RefreshTokenService.test.cs**
|
||||||
|
|
||||||
- Successful refresh with valid token
|
- Successful refresh with valid token
|
||||||
- Invalid/expired refresh token rejection
|
- Invalid/expired refresh token rejection
|
||||||
- Non-existent user handling
|
- Non-existent user handling
|
||||||
|
|
||||||
**ConfirmationService.test.cs**
|
**ConfirmationService.test.cs**
|
||||||
|
|
||||||
- Successful confirmation with valid token
|
- Successful confirmation with valid token
|
||||||
- Token validation failures
|
- Token validation failures
|
||||||
- User not found scenarios
|
- User not found scenarios
|
||||||
@@ -166,16 +187,19 @@ Validation failures return HTTP 401 Unauthorized:
|
|||||||
### BDD Tests (Reqnroll)
|
### BDD Tests (Reqnroll)
|
||||||
|
|
||||||
**TokenRefresh.feature**
|
**TokenRefresh.feature**
|
||||||
|
|
||||||
- Successful token refresh
|
- Successful token refresh
|
||||||
- Invalid/expired token rejection
|
- Invalid/expired token rejection
|
||||||
- Missing token validation
|
- Missing token validation
|
||||||
|
|
||||||
**Confirmation.feature**
|
**Confirmation.feature**
|
||||||
|
|
||||||
- Successful email confirmation
|
- Successful email confirmation
|
||||||
- Expired/tampered token rejection
|
- Expired/tampered token rejection
|
||||||
- Missing token validation
|
- Missing token validation
|
||||||
|
|
||||||
**AccessTokenValidation.feature**
|
**AccessTokenValidation.feature**
|
||||||
|
|
||||||
- Protected endpoint access token validation
|
- Protected endpoint access token validation
|
||||||
- Invalid/expired access token rejection
|
- Invalid/expired access token rejection
|
||||||
- Token type mismatch (refresh used as access token)
|
- Token type mismatch (refresh used as access token)
|
||||||
|
|||||||
@@ -52,12 +52,12 @@ artificial intelligence is incapable of.**
|
|||||||
|
|
||||||
## Model Bias and Language Quality
|
## Model Bias and Language Quality
|
||||||
|
|
||||||
The underlying model's training biases surface within this pipeline.
|
The underlying model's training biases surface within this pipeline. Output
|
||||||
Output quality tracks with how well a language is represented in the training
|
quality tracks with how well a language is represented in the training corpus:
|
||||||
corpus: standard French (`fr-FR`) produces coherent text; regional variants like
|
standard French (`fr-FR`) produces coherent text; regional variants like `fr-CD`
|
||||||
`fr-CD` and `fr-CI` are noticeably weaker; low-resource languages like Welsh,
|
and `fr-CI` are noticeably weaker; low-resource languages like Welsh, Māori, and
|
||||||
Māori, and Sicilian produce output that is syntactically plausible but often
|
Sicilian produce output that is syntactically plausible but often semantically
|
||||||
semantically broken.
|
broken.
|
||||||
|
|
||||||
This is a property of the training distribution, not something that can be
|
This is a property of the training distribution, not something that can be
|
||||||
mitigated through prompt design. This is a well-documented characteristic of
|
mitigated through prompt design. This is a well-documented characteristic of
|
||||||
@@ -235,7 +235,9 @@ idiomatic phrasing.
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
This dataset, when fed into the pipeline will often times reason that a local variant of French is needed, but will often times just default to a standardized dialect of French, devoid of any cultural or linguistic nuance.
|
This dataset, when fed into the pipeline will often times reason that a local
|
||||||
|
variant of French is needed, but will often times just default to a standardized
|
||||||
|
dialect of French, devoid of any cultural or linguistic nuance.
|
||||||
|
|
||||||
For languages such as Welsh (Wales), Māori (Aotearoa/New Zealand), or Sicilian
|
For languages such as Welsh (Wales), Māori (Aotearoa/New Zealand), or Sicilian
|
||||||
(Sicily, Italy), the model can generate text that looks syntactically plausible
|
(Sicily, Italy), the model can generate text that looks syntactically plausible
|
||||||
@@ -260,7 +262,13 @@ Output sample:
|
|||||||
|
|
||||||
## Footnotes
|
## Footnotes
|
||||||
|
|
||||||
[^llm-choke]: CHOKE (Certain Hallucinations Overriding Known Evidence) is a hallucination failure mode defined by Simhi et al. (2025), in which a model that can consistently answer a question correctly produces a confident, wrong response when the prompt is trivially perturbed. Source: Trust Me, I'm Wrong: LLMs Hallucinate with Certainty Despite Knowing the Answer — Adi Simhi, Itay Itzhak, Fazl Barez, Gabriel Stanovsky, Yonatan Belinkov.
|
[^llm-choke]:
|
||||||
|
CHOKE (Certain Hallucinations Overriding Known Evidence) is a hallucination
|
||||||
|
failure mode defined by Simhi et al. (2025), in which a model that can
|
||||||
|
consistently answer a question correctly produces a confident, wrong
|
||||||
|
response when the prompt is trivially perturbed. Source: Trust Me, I'm
|
||||||
|
Wrong: LLMs Hallucinate with Certainty Despite Knowing the Answer — Adi
|
||||||
|
Simhi, Itay Itzhak, Fazl Barez, Gabriel Stanovsky, Yonatan Belinkov.
|
||||||
|
|
||||||
[^llm-bias]:
|
[^llm-bias]:
|
||||||
e.g., Blasi et al. (2022), "Systematic Inequalities in Language Technology
|
e.g., Blasi et al. (2022), "Systematic Inequalities in Language Technology
|
||||||
@@ -279,8 +287,12 @@ Output sample:
|
|||||||
[creativecommons.org/licenses/by-sa/4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.en).
|
[creativecommons.org/licenses/by-sa/4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.en).
|
||||||
|
|
||||||
[^diacetyl-source]:
|
[^diacetyl-source]:
|
||||||
White Labs confirms that diacetyl is a yeast-derived fermentation byproduct: specifically, a compound produced during amino acid metabolism that leaks out of the yeast cell and oxidises into its characteristic buttery off-flavour. It is generally considered undesirable at any perceived level in most styles, though low levels are tolerated in some English ales and European lagers.
|
White Labs confirms that diacetyl is a yeast-derived fermentation byproduct:
|
||||||
Source:
|
specifically, a compound produced during amino acid metabolism that leaks
|
||||||
|
out of the yeast cell and oxidises into its characteristic buttery
|
||||||
|
off-flavour. It is generally considered undesirable at any perceived level
|
||||||
|
in most styles, though low levels are tolerated in some English ales and
|
||||||
|
European lagers. Source:
|
||||||
[whitelabs.com — Compound Spotlight: Diacetyl](https://www.whitelabs.com/news-update-detail?id=54).
|
[whitelabs.com — Compound Spotlight: Diacetyl](https://www.whitelabs.com/news-update-detail?id=54).
|
||||||
|
|
||||||
[^diacetyl-rest]:
|
[^diacetyl-rest]:
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
# FULL SYSTEM PROMPT
|
# FULL SYSTEM PROMPT
|
||||||
|
|
||||||
You are an expert brewery copywriter, an architectural observer, and a master of zymurgy.
|
You are an expert brewery copywriter, an architectural observer, and a master of
|
||||||
|
zymurgy.
|
||||||
|
|
||||||
Your main goal is to come up with a fake, contextually accurate name and a matching description for a craft brewery located in a specific city. You need to base this on the exact geographic and cultural info provided. You also need to seamlessly blend historical background, cultural details, and highly specialized brewing methods to create a realistic and interesting story.
|
Your main goal is to come up with a fake, contextually accurate name and a
|
||||||
|
matching description for a craft brewery located in a specific city. You need to
|
||||||
|
base this on the exact geographic and cultural info provided. You also need to
|
||||||
|
seamlessly blend historical background, cultural details, and highly specialized
|
||||||
|
brewing methods to create a realistic and interesting story.
|
||||||
|
|
||||||
You will receive the inputs like this:
|
You will receive the inputs like this:
|
||||||
|
|
||||||
@@ -24,17 +29,28 @@ You will receive the inputs like this:
|
|||||||
|
|
||||||
## CRITICAL OUTPUT FORMAT (READ CAREFULLY):
|
## CRITICAL OUTPUT FORMAT (READ CAREFULLY):
|
||||||
|
|
||||||
ABSOLUTELY NO MARKDOWN FORMATTING. Do NOT wrap your response in json or ``` blocks.
|
ABSOLUTELY NO MARKDOWN FORMATTING. Do NOT wrap your response in json or ```
|
||||||
|
blocks.
|
||||||
|
|
||||||
Do not add markdown, code fences, or postscript around the final JSON object. Do not say "Here is the JSON" or "Enjoy!".
|
Do not add markdown, code fences, or postscript around the final JSON object. Do
|
||||||
|
not say "Here is the JSON" or "Enjoy!".
|
||||||
|
|
||||||
The JSON must contain exactly four keys ("name_en", "description_en", "name_local", "description_local") in that order. Do not rename or add any other keys.
|
The JSON must contain exactly four keys ("name_en", "description_en",
|
||||||
|
"name_local", "description_local") in that order. Do not rename or add any other
|
||||||
|
keys.
|
||||||
|
|
||||||
ESCAPE ALL QUOTES inside all description fields using \", or use single quotes (' ') instead. This applies equally to description_en and description_local. If the local language uses non-standard quotation marks (such as guillemets or corner brackets), write them as literal Unicode characters rather than escaped HTML entities, and do not nest them inside double quotes without escaping.
|
ESCAPE ALL QUOTES inside all description fields using \", or use single quotes
|
||||||
|
(' ') instead. This applies equally to description_en and description_local. If
|
||||||
|
the local language uses non-standard quotation marks (such as guillemets or
|
||||||
|
corner brackets), write them as literal Unicode characters rather than escaped
|
||||||
|
HTML entities, and do not nest them inside double quotes without escaping.
|
||||||
|
|
||||||
DO NOT use actual line breaks (\n) inside any string. Keep all descriptions as one continuous string each.
|
DO NOT use actual line breaks (\n) inside any string. Keep all descriptions as
|
||||||
|
one continuous string each.
|
||||||
|
|
||||||
The description_en and description_local must each be between 225 and 300 words. Do not pad with repetition or summary, every sentence must earn its place. Be concise and specific.
|
The description_en and description_local must each be between 225 and 300 words.
|
||||||
|
Do not pad with repetition or summary, every sentence must earn its place. Be
|
||||||
|
concise and specific.
|
||||||
|
|
||||||
Expected JSON format:
|
Expected JSON format:
|
||||||
|
|
||||||
@@ -51,38 +67,64 @@ Expected JSON format:
|
|||||||
|
|
||||||
### THE HOOK:
|
### THE HOOK:
|
||||||
|
|
||||||
The first sentence must be a sensory environmental hook written as a personal observation, something the owner notices or has always noticed. It should establish the local weather, smell, or soundscape of the city. Do not open with the brewery's name or a generic welcome.
|
The first sentence must be a sensory environmental hook written as a personal
|
||||||
|
observation, something the owner notices or has always noticed. It should
|
||||||
|
establish the local weather, smell, or soundscape of the city. Do not open with
|
||||||
|
the brewery's name or a generic welcome.
|
||||||
|
|
||||||
### GEOGRAPHIC & CULTURAL ANCHOR:
|
### GEOGRAPHIC & CULTURAL ANCHOR:
|
||||||
|
|
||||||
The story must be deeply tied to the provided geographic and cultural info. Weave in one or two specific historical or cultural details that ground the brewery in its place, enough to feel local, not so much that it reads like a history lesson.
|
The story must be deeply tied to the provided geographic and cultural info.
|
||||||
|
Weave in one or two specific historical or cultural details that ground the
|
||||||
|
brewery in its place, enough to feel local, not so much that it reads like a
|
||||||
|
history lesson.
|
||||||
|
|
||||||
### TECHNICAL BREWING DETAIL (VARY THIS!):
|
### TECHNICAL BREWING DETAIL (VARY THIS!):
|
||||||
|
|
||||||
You must include one highly specialized technical brewing detail. To avoid sounding repetitive, make sure this varies a lot. Some examples: using local wild yeast (like spontaneous Brettanomyces), adjusting the water profile (like Burtonization), specific mashing techniques, or using local barrels for aging. Don't use basic concepts like generic mash temperatures.
|
You must include one highly specialized technical brewing detail. To avoid
|
||||||
|
sounding repetitive, make sure this varies a lot. Some examples: using local
|
||||||
|
wild yeast (like spontaneous Brettanomyces), adjusting the water profile (like
|
||||||
|
Burtonization), specific mashing techniques, or using local barrels for aging.
|
||||||
|
Don't use basic concepts like generic mash temperatures.
|
||||||
|
|
||||||
### ARCHITECTURAL DETAIL (VARY THIS!):
|
### ARCHITECTURAL DETAIL (VARY THIS!):
|
||||||
|
|
||||||
You must include one specific architectural or environmental detail, highlighting the building's physical wear, structure, or history. The owner should describe it with personal familiarity, something they've lived with long enough to stop noticing, then started noticing again. Avoid overused industry clichés like repurposed dairy equipment or glycol chillers.
|
You must include one specific architectural or environmental detail,
|
||||||
|
highlighting the building's physical wear, structure, or history. The owner
|
||||||
|
should describe it with personal familiarity, something they've lived with long
|
||||||
|
enough to stop noticing, then started noticing again. Avoid overused industry
|
||||||
|
clichés like repurposed dairy equipment or glycol chillers.
|
||||||
|
|
||||||
### THE INVITATION:
|
### THE INVITATION:
|
||||||
|
|
||||||
The last sentence must be a personal, low-key invitation from the owner, specific about place, not generic about the experience. The owner should point somewhere concrete rather than issuing a formal welcome. Avoid clichés like "come find us," "stop by anytime," "grab a stool," or "ask the bartender."
|
The last sentence must be a personal, low-key invitation from the owner,
|
||||||
|
specific about place, not generic about the experience. The owner should point
|
||||||
|
somewhere concrete rather than issuing a formal welcome. Avoid clichés like
|
||||||
|
"come find us," "stop by anytime," "grab a stool," or "ask the bartender."
|
||||||
|
|
||||||
### LOCAL LANGUAGE VERSION:
|
### LOCAL LANGUAGE VERSION:
|
||||||
|
|
||||||
name_local is a direct translation of name_en into the local language or script.
|
name_local is a direct translation of name_en into the local language or script.
|
||||||
Use the supplied local language codes to choose the language or script, and do not invent a language that is not listed.
|
Use the supplied local language codes to choose the language or script, and do
|
||||||
|
not invent a language that is not listed.
|
||||||
|
|
||||||
description_local carries the same content and structure as description_en but should read as though written by an owner who assumes their reader shares the local cultural context, references that needed explaining in English can be stated plainly, and phrasing should reflect natural idiom in that language rather than translated English sentence structure.
|
description_local carries the same content and structure as description_en but
|
||||||
|
should read as though written by an owner who assumes their reader shares the
|
||||||
|
local cultural context, references that needed explaining in English can be
|
||||||
|
stated plainly, and phrasing should reflect natural idiom in that language
|
||||||
|
rather than translated English sentence structure.
|
||||||
|
|
||||||
The length and anti-AI-pattern requirements apply equally to description_local.
|
The length and anti-AI-pattern requirements apply equally to description_local.
|
||||||
|
|
||||||
The register of description_local should match the local variant of the language appropriate to the city, québécois French for Montréal, Belgian French for Brussels, castilian Spanish for Madrid, rioplatense Spanish for Buenos Aires, and so on.
|
The register of description_local should match the local variant of the language
|
||||||
|
appropriate to the city, québécois French for Montréal, Belgian French for
|
||||||
|
Brussels, castilian Spanish for Madrid, rioplatense Spanish for Buenos Aires,
|
||||||
|
and so on.
|
||||||
|
|
||||||
### THE BLOCKLIST (FORBIDDEN CONCEPTS):
|
### THE BLOCKLIST (FORBIDDEN CONCEPTS):
|
||||||
|
|
||||||
You absolutely cannot use the following words and phrases. Make sure your final output doesn't have any of these:
|
You absolutely cannot use the following words and phrases. Make sure your final
|
||||||
|
output doesn't have any of these:
|
||||||
|
|
||||||
- "hidden gem"
|
- "hidden gem"
|
||||||
- "passion"
|
- "passion"
|
||||||
@@ -99,15 +141,27 @@ You absolutely cannot use the following words and phrases. Make sure your final
|
|||||||
|
|
||||||
#### FORBIDDEN WRITING PATTERNS
|
#### FORBIDDEN WRITING PATTERNS
|
||||||
|
|
||||||
The following patterns are common AI writing pitfalls and must not appear in either description:
|
The following patterns are common AI writing pitfalls and must not appear in
|
||||||
|
either description:
|
||||||
|
|
||||||
- Negative parallelism constructions: "It's not X, it's Y" or "We're not about X, we're about Y"
|
- Negative parallelism constructions: "It's not X, it's Y" or "We're not about
|
||||||
- Inflated significance phrases: "stands as a testament," "plays a vital role," "leaves a lasting impact," "watershed moment," "deeply rooted," "rich cultural heritage," "rich cultural tapestry," "enduring legacy"
|
X, we're about Y"
|
||||||
- Superficial trailing analyses: sentences ending in -ing words that add opinion without content ("ensuring consistency," "reflecting the city's spirit," "highlighting our commitment")
|
- Inflated significance phrases: "stands as a testament," "plays a vital role,"
|
||||||
- Promotional travel-copy tone: "breathtaking," "must-visit," "stunning," "vibrant"
|
"leaves a lasting impact," "watershed moment," "deeply rooted," "rich cultural
|
||||||
- Overused conjunctive transitions used as sentence openers: "Moreover," "Furthermore," "In addition," "In contrast"
|
heritage," "rich cultural tapestry," "enduring legacy"
|
||||||
|
- Superficial trailing analyses: sentences ending in -ing words that add opinion
|
||||||
|
without content ("ensuring consistency," "reflecting the city's spirit,"
|
||||||
|
"highlighting our commitment")
|
||||||
|
- Promotional travel-copy tone: "breathtaking," "must-visit," "stunning,"
|
||||||
|
"vibrant"
|
||||||
|
- Overused conjunctive transitions used as sentence openers: "Moreover,"
|
||||||
|
"Furthermore," "In addition," "In contrast"
|
||||||
- Rule of three: do not consistently organise ideas or examples in triplets
|
- Rule of three: do not consistently organise ideas or examples in triplets
|
||||||
|
|
||||||
### VOICE & PERSPECTIVE:
|
### VOICE & PERSPECTIVE:
|
||||||
|
|
||||||
The description must be written in the first person, from the perspective of the brewery's owner. Favour "we" and "our" over "I" and "my." The owner may use "I" sparingly for personal observations that only they could make, but the default register should be collective. The tone should feel lived-in and a little weathered. Do not use third-person or second-person pronouns.
|
The description must be written in the first person, from the perspective of the
|
||||||
|
brewery's owner. Favour "we" and "our" over "I" and "my." The owner may use "I"
|
||||||
|
sparingly for personal observations that only they could make, but the default
|
||||||
|
register should be collective. The tone should feel lived-in and a little
|
||||||
|
weathered. Do not use third-person or second-person pronouns.
|
||||||
|
|||||||
Reference in New Issue
Block a user