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: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ coverage/
paket-files/

src/.vs/*
.vscode/
packages/

.fake
src/BikeTracking.Frontend/test-results/
Expand Down
8 changes: 4 additions & 4 deletions .specify/extensions/git/git-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ auto_commit:
enabled: false
message: "[Spec Kit] Save progress before planning"
before_tasks:
enabled: false
enabled: true
message: "[Spec Kit] Save progress before task generation"
before_implement:
enabled: false
enabled: true
message: "[Spec Kit] Save progress before implementation"
before_checklist:
enabled: false
Expand All @@ -46,10 +46,10 @@ auto_commit:
enabled: false
message: "[Spec Kit] Add implementation plan"
after_tasks:
enabled: false
enabled: true
message: "[Spec Kit] Add tasks"
after_implement:
enabled: false
enabled: true
message: "[Spec Kit] Implementation progress"
after_checklist:
enabled: false
Expand Down
3 changes: 3 additions & 0 deletions .specify/feature.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"feature_directory": "specs/015-bike-expense-tracking"
}
4 changes: 2 additions & 2 deletions .specify/memory/constitution.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ 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.
8. **Commit At Each TDD Gate**: Git 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").

Expand Down Expand Up @@ -285,7 +285,7 @@ All development follows Trunk-Based Development with git worktrees for parallel
1. Create a GitHub issue describing the work
2. Create a short-lived feature branch from `main` (e.g., `feature/issue-42-record-ride`)
3. Use `git worktree add` to work on the branch in a separate directory when parallel work is needed
4. Commit frequently with meaningful messages using `semantic commits or conventional commits` format; push to remote regularly
4. Git Commit frequently with meaningful messages using `semantic commits or conventional commits` format; push to remote regularly
5. Open a PR referencing the GitHub issue (e.g., "Closes #42") as soon as the first commit is ready (draft PR for work-in-progress)
6. Keep the branch up-to-date with `main` via rebase
7. Once CI passes and review feedback is addressed, the owner completes the PR
Expand Down
61 changes: 61 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ Local-first Bike Tracking application built with .NET Aspire orchestration, .NET
- Duplicate-name rejection using trimmed, case-insensitive normalization
- User identification with progressive retry delay (up to 30 seconds)
- User registration outbox with background retry until successful publication
- Manual bike expense entry with date, amount, optional note, and optional receipt
- Expense history with date filtering, inline edit, soft delete, and receipt management
- Dashboard expense summary with manual totals, oil-change savings, and net position

## Project Structure

Expand Down Expand Up @@ -162,6 +165,60 @@ For local-first deployment to end-user machines, the default persistence model i
- Before schema upgrades, create a safety backup copy of the SQLite file.
- Use SQL Server LocalDB or SQL Server Express only when local multi-user requirements exceed the single-user SQLite profile.

## Bike Expense Tracking

The expense tracking slice adds a full local-first workflow for bike ownership costs.

- Record manual expenses with required date and amount.
- Attach an optional receipt in JPEG, PNG, WEBP, or PDF format up to 5 MB.
- Browse expense history with date filters, inline edit, delete, and receipt replacement/removal.
- View dashboard totals that combine manual expenses with automatic oil-change savings.

Receipt upload failures are handled as non-fatal storage errors when recording a new expense. If receipt storage fails because of a non-writable path, permission issue, or disk/storage error, the expense is still saved and the UI shows that the receipt was not attached.

## Receipt Storage

Receipt files are stored in a `receipts/` folder next to the configured SQLite database file.

- Default development database: `src/BikeTracking.Api/biketracking.local.db`
- Default development receipt root: `src/BikeTracking.Api/receipts/`
- If `ConnectionStrings:BikeTracking` points to a different SQLite path, the receipt root moves with it.

The storage rule is:

- Database: `/path/to/biketracking.local.db`
- Receipts: `/path/to/receipts/`

For packaged installs, configure the SQLite database in a user-writable app-data directory and allow the app to create the sibling `receipts/` directory there.

Suggested packaged-install locations:

- Windows: `%LocalAppData%/CommuteBikeTracker/biketracking.local.db` and `%LocalAppData%/CommuteBikeTracker/receipts/`
- macOS: `~/Library/Application Support/CommuteBikeTracker/biketracking.local.db` and `~/Library/Application Support/CommuteBikeTracker/receipts/`
- Linux: `${XDG_DATA_HOME:-~/.local/share}/CommuteBikeTracker/biketracking.local.db` and `${XDG_DATA_HOME:-~/.local/share}/CommuteBikeTracker/receipts/`

When deploying outside development, prefer setting the database path explicitly with configuration such as `ConnectionStrings__BikeTracking` so both the database and receipt root land in the intended writable directory.

## Backup And Restore

Back up the SQLite database file and the sibling `receipts/` directory together. Keeping only one of them can leave expense records pointing at missing attachments or leave orphaned receipt files with no matching expense rows.

Recommended backup workflow:

1. Stop the application.
2. Copy `biketracking.local.db`.
3. Copy the entire `receipts/` directory from the same parent folder.
4. Store both copies together with the same timestamp.

Recommended restore workflow:

1. Stop the application.
2. Restore `biketracking.local.db` to the configured data directory.
3. Restore the matching `receipts/` directory to the same parent folder.
4. Start the application and verify expense history plus a few receipt downloads.

If the application is upgraded and migrations are about to run, make the backup before first startup on the new version.


## Automated Tests

Expand All @@ -172,6 +229,10 @@ frontend end-to-end tests: `npm run test:e2e` (Playwright)

backend tests: `dotnet test` from repo root (xUnit)

formatting: `dotnet csharpier format .`

frontend lint: `cd src/BikeTracking.Frontend && npm run lint`

These are ran in the .github\workflows\ci.yml pipeline on every PR


37 changes: 37 additions & 0 deletions specs/015-bike-expense-tracking/checklists/requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Specification Quality Checklist: Bike Expense Tracking

**Purpose**: Validate specification completeness and quality before proceeding to planning
**Created**: 2026-04-17
**Feature**: [spec.md](../spec.md)

## Content Quality

- [x] No implementation details (languages, frameworks, APIs)
- [x] Focused on user value and business needs
- [x] Written for non-technical stakeholders
- [x] All mandatory sections completed

## Requirement Completeness

- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Requirements are testable and unambiguous (all 5 ambiguities resolved in clarification session)
- [x] Success criteria are measurable
- [x] Success criteria are technology-agnostic (no implementation details)
- [x] All acceptance scenarios are defined
- [x] Edge cases are identified
- [x] Scope is clearly bounded
- [x] Dependencies and assumptions identified

## Feature Readiness

- [x] All functional requirements have clear acceptance criteria
- [x] User scenarios cover primary flows
- [x] Feature meets measurable outcomes defined in Success Criteria
- [x] No implementation details leak into specification

## Notes

- Automatic oil-change savings calculation rule is explicitly defined: `floor(total_ride_miles / 3000) × oil_change_price`
- Receipt attachment constraints (file size, accepted formats) are called out as required but specific values left to planning/implementation phase
- Expense note maximum length is called out as required but specific value left to planning/implementation phase (consistent with ride notes spec 014 which uses 500 characters)
- Dependency on spec 009 (user settings: oil change price) and spec 012 (dashboard stats) is captured via key entities and FR-018
Loading
Loading