Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@ RUN dotnet tool restore && dotnet restore BikeTracking.slnx
# making postCreateCommand "npm ci" fast without re-downloading packages.
COPY src/BikeTracking.Frontend/package.json src/BikeTracking.Frontend/package-lock.json /tmp/npm-warmup/
RUN npm ci --prefix /tmp/npm-warmup \
&& npm exec --prefix /tmp/npm-warmup -- playwright install \
&& npm exec --prefix /tmp/npm-warmup -- playwright install --with-deps chromium \
&& rm -rf /tmp/npm-warmup

2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ From `src/BikeTracking.Frontend`:
- **Build:** `npm run build`
- **Lint:** `npm run lint` (ESLint + Stylelint)
- **Unit tests:** `npm run test:unit` (Vitest; use `--ui` flag for interactive mode)
- **E2E tests:** `npm run test:e2e` (Playwright; runs against live API/DB)
- **E2E tests:** `npm run test:e2e` (Playwright; runs against live API/DB). You must start the application with Aspire before running E2E tests.
- **Watch unit tests:** `npm run test:unit:watch`

### CI Validation
Expand Down
20 changes: 17 additions & 3 deletions .specify/memory/constitution.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Modified Sections:
- Compliance Audit Checklist: Added modular boundary and contract compatibility checks
- Guardrails: Added non-negotiable interface/contract boundary rules for cross-module integration
Status: Approved — modular architecture and contract-first parallel delivery are now constitutional requirements
Current Update (v1.12.2): Added mandatory spec-completion gate requiring database migrations to be applied and E2E tests to pass before a spec can be marked done.
Previous Updates:
- v1.11.0: Strengthened TDD mandate with a strict gated red-green-refactor workflow requiring explicit user confirmation of failing tests before implementation.
- v1.10.2: Codified a mandatory post-change verification command matrix so every change runs explicit checks before merge.
Expand Down Expand Up @@ -55,7 +56,7 @@ Domain logic isolated from infrastructure concerns via layered architecture alig

### II. Functional Programming (Pure & Impure Sandwich)

Core calculations and business logic implemented as pure functions: distance-to-distance conversions, expense-to-savings transformations, weather-to-recommendation mappings. Pure functions have no side effects—given the same input, always return the same output. Use immutable data structures. Impure edges (database reads/writes, external API calls, user input, system time) explicitly isolated at application boundaries. Handlers orchestrate pure logic within impure I/O boundaries. **F# discriminated unions and active patterns preferred for domain modeling** (domain layer uses F#); Railway Oriented Programming (Result<'T> type) for error handling; C# records used in API surface for interop.
Core calculations and business logic implemented as pure functions: distance-to-distance conversions, expense-to-savings transformations, weather-to-recommendation mappings. Pure functions have no side effects—given the same input, always return the same output. Use immutable data structures. Impure edges (database reads/writes, external API calls, user input, system time) explicitly isolated at application boundaries. Handlers orchestrate pure logic within impure I/O boundaries. **F# discriminated unions and active patterns preferred for domain modeling** (domain layer uses F#); Railway Oriented Programming (Result<'T> type) for error handling; C# records used in API surface for interop. **C# expected business/validation/conflict flows MUST use explicit Result-style return values and MUST NOT use exceptions for routine control flow. Exceptions are reserved for unexpected/exceptional failures only.**

**Rationale**: Pure functions are trivially testable, deterministic, and composable. Side effect isolation makes dataflow explicit and reduces debugging complexity. Immutable data structures preferred where practical. F# enforces immutability and pattern matching, reducing entire categories of bugs. Discriminated unions make invalid states unrepresentable.

Expand All @@ -76,6 +77,9 @@ Red-Green-Refactor cycle is **non-negotiable** and follows a strict, gate-contro
5. **Run After Each Change**: Tests are run after each meaningful implementation change to track incremental progress toward green.
6. **All Tests Pass**: Implementation is complete only when all tests pass. No merge occurs until the full test suite is green.
7. **Consider Refactoring**: Once tests are green, evaluate the implementation for clarity, duplication, and simplicity. Refactor while keeping tests green. Refactoring is optional but explicitly encouraged at this stage.
8. **Commit At Each TDD Gate**: Commits are mandatory at each TDD gate transition with clear gate intent in the message. Required checkpoints: (a) red baseline committed after failing tests are written and user confirms failures, (b) green implementation committed when approved tests pass, (c) refactor committed separately when refactoring is performed.

TDD commit messages must include gate and spec/task context (for example: "TDD-RED: spec-006 ride history edit conflict tests" or "TDD-GREEN: spec-006 make edit totals refresh pass").

Unit tests validate pure logic (target 85%+ coverage). Integration tests verify each vertical slice end-to-end. Contract tests ensure event schemas remain backwards compatible. Security tests validate OAuth isolation and data access. **Agent must suggest tests with rationale; user approval required before implementation. User must confirm test failures before implementation begins.**

Expand Down Expand Up @@ -141,6 +145,7 @@ System capabilities must be split into cohesive modules with explicit ownership
- **Framework**: .NET 10 Minimal API (latest stable)
- **Orchestration**: Microsoft Aspire (latest stable) for local and cloud development
- **Language (API Layer)**: C# (latest language features: records, pattern matching, async/await, follow .editorconfig for code formatting)
- **Language (API Layer)**: C# (latest language features: records, pattern matching, async/await, follow .editorconfig for code formatting); expected-flow outcomes MUST be represented with explicit Result objects rather than exception-driven control flow
- **Language (Domain Layer)**: F# (latest stable) for domain entities, events, value objects, services, and command handlers. Discriminated unions, active patterns, and Railway Oriented Programming pattern used for domain modeling and error handling.
- **NuGet Discipline**: All packages must be checked monthly for updates; security patches applied immediately; major versions reviewed for breaking changes before upgrade
- **Domain-Infrastructure Interop**: EF Core value converters (FSharpValueConverters) enable transparent mapping of F# discriminated unions to database columns
Expand Down Expand Up @@ -387,6 +392,8 @@ Tests suggested by agent must receive explicit user approval before implementati
12. **Local Deployment**: Slice deployed locally in containers via Aspire, tested manually with Playwright if E2E slice
13. **Azure Deployment**: Slice deployed to Azure Container Apps via GitHub Actions + azd
14. **User Acceptance**: User validates slice meets specification and data validation rules observed
15. **Phase Completion Commit**: Before starting the next phase, create a dedicated phase-completion commit that includes completed tasks and verification evidence for that phase
16. **Spec Completion Gate**: Before marking any specification as done, database migrations for that spec must be applied successfully to the target local runtime database and the spec's end-to-end (Playwright) tests must run green

### Compliance Audit Checklist

Expand All @@ -400,6 +407,10 @@ Tests suggested by agent must receive explicit user approval before implementati
- [ ] Module boundaries documented; cross-module integrations use approved interfaces/contracts only
- [ ] Contract compatibility tests executed for changed APIs/events (provider and consumer)
- [ ] Security issues recognized, explained, and remediated (or explicitly accepted by user)
- [ ] TDD gate commits created: red baseline commit, green commit, and separate refactor commit when applicable
- [ ] Phase completion commit created before moving to the next phase
- [ ] Database migrations for the spec are created and applied successfully to the runtime database used for validation
- [ ] Spec-level E2E (Playwright) suite executed and passing before spec marked complete
- [ ] All SAMPLE_/DEMO_ data removed from code before merge
- [ ] Secrets NOT committed; `.gitignore` verified; pre-commit hook prevents credential leakage
- [ ] Validation rule consistency: if field required in React form, enforced in API DTOs and database constraints
Expand Down Expand Up @@ -432,6 +443,9 @@ Tests suggested by agent must receive explicit user approval before implementati
Breaking these guarantees causes architectural decay and technical debt accrual:

- **TDD cycle is strictly gated and non-negotiable** — implementation code must never be written before failing tests exist, have been run, and the user has reviewed and confirmed the failures. The sequence is always: plan tests → write tests → run and prove failure → get user confirmation → implement → run after each change → verify all pass → consider refactoring. Skipping or reordering any step is prohibited.
- **Commit gates are mandatory for TDD and phase transitions** — every TDD gate transition requires a commit (red, green, and refactor when performed), and every completed phase requires a dedicated phase-completion commit before proceeding.
- **Spec completion requires migration + E2E gates** — a spec cannot be marked done until its database migrations are applied to the runtime database and its Playwright E2E scenarios pass.
- **Expected-flow C# logic uses Result, not exceptions** — validation, not-found, conflict, and authorization business outcomes must be returned via typed Result objects (including error code/message metadata). Throwing exceptions for these expected outcomes is prohibited; exceptions are only for truly unexpected failures.
- **Cross-module work is contract-first and interface-bound** — teams must integrate through explicit interfaces and versioned contracts only; direct coupling to another module's internal implementation is prohibited.
- **No Entity Framework DbContext in domain layer** — domain must remain infrastructure-agnostic. If domain needs persistence logic, use repository pattern abstracting EF.
- **Secrets management by deployment context** — **Cloud**: all secrets in Azure Key Vault; **Local**: User Secrets or environment variables. No connection strings, API keys, or OAuth secrets in appsettings.json, code, or GitHub. Pre-commit hooks enforce this. **⚠️ This repository is public on GitHub**: any committed secret is immediately and permanently exposed to the internet; treat any accidental secret commit as an immediate security incident requiring credential rotation.
Expand Down Expand Up @@ -518,7 +532,7 @@ All SpecKit templates must reflect this constitution:
### Runtime Guidance
Development workflow guidance documented in [README.md](../../README.md) and .github/prompts/ directory. This constitution establishes governance; runtime prompts add context and tool references.

Always commit before continuing to a new phase.
Always commit at each TDD gate and before continuing to a new phase.

### Related Documents
- **[DECISIONS.md](./DECISIONS.md)**: Amendment history, version changelog, rationale for major decisions
Expand All @@ -528,5 +542,5 @@ Always commit before continuing to a new phase.

---

**Version**: 1.12.0 | **Ratified**: 2026-03-03 | **Last Amended**: 2026-03-23
**Version**: 1.12.2 | **Ratified**: 2026-03-03 | **Last Amended**: 2026-03-27

6 changes: 5 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
},
"chat.tools.terminal.autoApprove": {
".specify/scripts/bash/": true,
".specify/scripts/powershell/": true
".specify/scripts/powershell/": true,
"/^cd /workspaces/neCodeBikeTracking && pwsh \\.specify/scripts/powershell/check-prerequisites\\.ps1 -Json$/": {
"approve": true,
"matchCommandLine": true
}
},
"sqltools.connections": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ components:
miles:
type: number
exclusiveMinimum: 0
maximum: 200
rideMinutes:
type: integer
nullable: true
Expand Down
2 changes: 1 addition & 1 deletion specs/004-create-the-record-ride-mvp/data-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Represents user-submitted form data sent to the API.
|-------|------|----------|------------|-------|
| riderId | integer | Yes | >= 1 | Derived from authenticated session context |
| rideDateTimeLocal | string (date-time) | Yes | Valid date-time | Exact user-entered value |
| miles | number | Yes | > 0 | Decimal precision up to 2 places |
| miles | number | Yes | > 0 and <= 200 | Decimal precision up to 2 places |
| rideMinutes | integer | No | > 0 when provided | Optional duration |
| temperature | number | No | none | Optional ambient temperature in existing app unit |

Expand Down
6 changes: 3 additions & 3 deletions specs/004-create-the-record-ride-mvp/plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

## Summary

Implement an authenticated Record Ride vertical slice that lets riders submit required date/time and miles, optionally submit minutes and temperature, and persist each successful submission as an immutable `RideRecorded` event payload via the existing outbox event pipeline. Deliver a React page with smart defaults (`now` for date/time and last ride values for optional defaults), plus Minimal API command/query endpoints and persistence support aligned with the current architecture.
Implement an authenticated Record Ride vertical slice that lets riders submit required date/time and miles, optionally submit minutes and temperature, and persist each successful submission as an immutable `RideRecorded` event payload via the existing outbox event pipeline. Deliver a React page with smart defaults (`now` for date/time and last ride values for optional defaults), plus Minimal API command/query endpoints and persistence support aligned with the current architecture. Validation includes miles > 0 and <= 200.

## Technical Context

Expand All @@ -16,7 +16,7 @@ Implement an authenticated Record Ride vertical slice that lets riders submit re
**Target Platform**: Linux DevContainer local development, browser frontend, containerized local orchestration via Aspire
**Project Type**: Web application (React frontend + .NET Minimal API backend)
**Performance Goals**: API response under 500ms p95 for ride record/defaults endpoints; defaults query should be single-latest lookup per rider
**Constraints**: Must preserve exact submitted values; miles > 0 and optional rideMinutes > 0 when provided; maintain auth isolation and retry-friendly UX
**Constraints**: Must preserve exact submitted values; miles > 0 and <= 200 and optional rideMinutes > 0 when provided; maintain auth isolation and retry-friendly UX
**Scale/Scope**: MVP feature for authenticated riders, single-user local-first profile, many rides per rider over time

## Constitution Check
Expand All @@ -43,7 +43,7 @@ No constitutional violations identified; no complexity exceptions required.
|------|--------|-------|
| Architecture and boundaries preserved | PASS | Data model and contracts keep read/write concerns separated (`/api/rides` and `/api/rides/defaults`). |
| Event contract discipline | PASS | Dedicated `RideRecorded` JSON schema and API contract defined in feature contracts. |
| Validation depth | PASS | Validation rules documented in data model and surfaced in API/UX quickstart steps. |
| Validation depth | PASS | Validation rules documented in data model and surfaced in API/UX quickstart steps, including miles upper bound <= 200. |
| UX consistency/accessibility | PASS | Route is authenticated, field semantics explicit, and success/error feedback required. |
| Verification and testing discipline | PASS WITH ACTION | Execution commands documented; strict red-green sequence remains required in tasks phase. |

Expand Down
4 changes: 2 additions & 2 deletions specs/004-create-the-record-ride-mvp/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ As a rider, I want to save a ride without duration or temperature when I do not
- **FR-006**: System MUST default optional ride minutes to the most recently saved ride minutes value for that rider when one exists.
- **FR-007**: System MUST default optional temperature to the most recently saved temperature value for that rider when one exists.
- **FR-008**: System MUST allow submission when optional ride minutes and optional temperature are blank.
- **FR-009**: System MUST validate that miles is greater than zero.
- **FR-009**: System MUST validate that miles is greater than zero and less than or equal to 200.
- **FR-010**: System MUST validate that optional ride minutes, when provided, is greater than zero.
- **FR-011**: System MUST persist each submitted ride to the database as a new ride event associated with the submitting rider.
- **FR-012**: System MUST preserve the exact submitted ride date/time and numeric values in persisted ride event data.
Expand Down Expand Up @@ -99,4 +99,4 @@ As a rider, I want to save a ride without duration or temperature when I do not
- **SC-003**: 100% of page loads default date/time to the current moment.
- **SC-004**: 100% of page loads for riders with prior data prefill miles from the rider's last saved ride.
- **SC-005**: 100% of successful submissions allow optional minutes and temperature to be omitted.
- **SC-006**: For invalid numeric input (non-positive miles or non-positive optional minutes), 100% of submissions are blocked with a visible validation message.
- **SC-006**: For invalid numeric input (non-positive miles, miles above 200, or non-positive optional minutes), 100% of submissions are blocked with a visible validation message.
8 changes: 4 additions & 4 deletions specs/004-create-the-record-ride-mvp/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Create foundational files, contracts, and folder structure. Verify no compilatio
- `ErrorResponse` (message, errors collection)

**Validation Rules** (embed in DTO):
- `miles` must be > 0 (use `[Range(0.01, double.MaxValue)]` or custom validator)
- `miles` must be > 0 and <= 200 (use `[Range(0.01, 200)]` or custom validator)
- `rideMinutes` nullable integer, > 0 when provided
- `temperature` nullable number
- `rideDateTimeLocal` required, valid date-time format
Expand Down Expand Up @@ -140,7 +140,7 @@ Define all tests for the feature. Tests must fail before implementation. Confirm
- Assert response is 201

3. **PostRecordRide_WithInvalidMiles_Returns400**
- POST `/api/rides` with miles <= 0
- POST `/api/rides` with miles <= 0 or miles > 200
- Assert response is 400
- Assert error message reflects validation failure

Expand Down Expand Up @@ -359,7 +359,7 @@ Implement backend services, persistence, and endpoints to turn failing tests gre
- `RecordRideRequest`
- Add `[Required]` to rideDateTimeLocal
- Add `[Required]` to miles
- Add `[Range(0.01, double.MaxValue)]` to miles
- Add `[Range(0.01, 200)]` to miles
- Add optional rideMinutes with `[Range(1, int.MaxValue)]` when provided
- Add optional temperature with no validation (any number allowed)

Expand Down Expand Up @@ -397,7 +397,7 @@ Implement backend services, persistence, and endpoints to turn failing tests gre
- Add `DbSet<RideEntity> Rides { get; set; }`
- Configure entity mapping in OnModelCreating:
- Mark Id as primary key
- Add check constraint: `Miles > 0`
- Add check constraint: `Miles > 0 AND Miles <= 200`
- Add check constraint: `RideMinutes > 0 OR RideMinutes IS NULL`
- Add foreign key to Users
- Create index on (RiderId, CreatedAtUtc) descending for efficient defaults query
Expand Down
Loading
Loading