diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md
index 9fe3b59..381a54a 100644
--- a/.planning/ROADMAP.md
+++ b/.planning/ROADMAP.md
@@ -29,11 +29,11 @@ Decimal phases appear between their surrounding integers in numeric order.
2. A local Kimai instance starts and is accessible in the browser
3. The Kimai instance contains seeded time entry data spanning multiple months
4. A plugin directory is symlinked/mounted so code changes are reflected without reinstallation
-**Plans**: TBD
+**Plans:** 2 plans
Plans:
-- [ ] 01-01: TBD
-- [ ] 01-02: TBD
+- [ ] 01-01-PLAN.md — Nix flake with devshell, process-compose config, and bootstrap script
+- [ ] 01-02-PLAN.md — Run setup, create plugin scaffold, verify end-to-end
### Phase 2: Plugin Scaffold + Data Layer
**Goal**: Plugin is recognized by Kimai, shows a widget on the dashboard, and serves aggregated daily time data via API
diff --git a/.planning/phases/01-dev-environment/01-01-PLAN.md b/.planning/phases/01-dev-environment/01-01-PLAN.md
new file mode 100644
index 0000000..3e2e4e3
--- /dev/null
+++ b/.planning/phases/01-dev-environment/01-01-PLAN.md
@@ -0,0 +1,241 @@
+---
+phase: 01-dev-environment
+plan: 01
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - flake.nix
+ - dev/process-compose.yaml
+ - dev/setup.sh
+ - .gitignore
+ - .envrc
+autonomous: true
+requirements:
+ - DEV-01
+must_haves:
+ truths:
+ - "nix develop drops into a shell with PHP 8.2+, Composer 2.x, Node 22.x, MariaDB, symfony-cli, and process-compose"
+ - "process-compose up starts MariaDB on port 3307 with a local data directory and Symfony dev server"
+ - "setup.sh clones Kimai, installs deps, creates DB, loads fixtures, and symlinks plugin"
+ artifacts:
+ - path: "flake.nix"
+ provides: "Nix devshell with all required packages and PHP extensions"
+ contains: "php82"
+ - path: "dev/process-compose.yaml"
+ provides: "MariaDB + Symfony dev server process definitions"
+ contains: "mariadb"
+ - path: "dev/setup.sh"
+ provides: "One-time Kimai bootstrap script"
+ contains: "kimai:reset:dev"
+ - path: ".gitignore"
+ provides: "Ignore dev artifacts (kimai clone, mariadb data, node_modules)"
+ contains: "dev/kimai"
+ key_links:
+ - from: "dev/process-compose.yaml"
+ to: "dev/.mariadb-data"
+ via: "mysqld --datadir"
+ pattern: "mariadb-data"
+ - from: "dev/setup.sh"
+ to: "dev/kimai"
+ via: "git clone kimai"
+ pattern: "git clone.*kimai"
+---
+
+
+Create the Nix flake, process-compose config, and bootstrap script for the Kimai dev environment.
+
+Purpose: Provide a reproducible, one-command dev setup that gives the developer PHP 8.2+, MariaDB, Composer, Node, and all tooling needed for Kimai plugin development.
+Output: flake.nix, dev/process-compose.yaml, dev/setup.sh, .gitignore
+
+
+
+@/home/toph/code/toph/kimai-heatmap/.claude/get-shit-done/workflows/execute-plan.md
+@/home/toph/code/toph/kimai-heatmap/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/phase-1/RESEARCH.md
+
+
+
+
+
+ Task 1: Create Nix flake with devshell
+ flake.nix, .envrc, .gitignore
+
+Create `flake.nix` with a devshell providing:
+
+**Packages:**
+- PHP 8.2 with extensions: gd, intl, mbstring, pdo, pdo_mysql, xml, xsl, zip, tokenizer (use `pkgs.php82.buildEnv { extensions = { enabled, all }: enabled ++ (with all; [ xsl pdo_mysql ]); }`)
+- `pkgs.php82Packages.composer` for Composer
+- `pkgs.nodejs` (22.x) for JS tooling
+- `pkgs.mariadb` (11.4.x) for database
+- `pkgs.symfony-cli` for Symfony dev server
+- `pkgs.process-compose` for multi-service management
+
+**Flake structure:**
+- Use `flake-utils.lib.eachDefaultSystem` for portability
+- Input: `nixpkgs` (use `github:NixOS/nixpkgs/nixpkgs-unstable`)
+- Single devShell output
+- Shell hook: print a short message with available commands (setup, start)
+
+**Also create:**
+- `.envrc` with `use flake` for direnv integration
+- `.gitignore` with entries for:
+ - `dev/kimai/` (cloned Kimai instance)
+ - `dev/.mariadb-data/` (MariaDB data directory)
+ - `dev/.mariadb.sock` (MariaDB socket)
+ - `dev/.mariadb.pid` (MariaDB PID file)
+ - `node_modules/`
+ - `vendor/`
+ - `.direnv/`
+ - `result` (nix build output)
+ - `*.js.map` (source maps)
+ - `dist/` (built JS output)
+
+
+ cd /home/toph/code/toph/kimai-heatmap && nix develop --command bash -c "php --version && composer --version && node --version && mysqld --version && symfony version && process-compose version" 2>&1 | head -20
+
+ nix develop provides PHP 8.2+, Composer, Node 22.x, MariaDB, symfony-cli, and process-compose. All commands resolve.
+
+
+
+ Task 2: Create process-compose config and setup script
+ dev/process-compose.yaml, dev/setup.sh
+
+**Create `dev/process-compose.yaml`:**
+
+```yaml
+version: "0.5"
+processes:
+ mariadb:
+ command: |
+ mysqld --datadir=$PWD/dev/.mariadb-data \
+ --socket=$PWD/dev/.mariadb.sock \
+ --port=3307 \
+ --skip-grant-tables \
+ --skip-networking=false \
+ --bind-address=127.0.0.1
+ readiness_probe:
+ exec:
+ command: mysqladmin --socket=$PWD/dev/.mariadb.sock ping
+ initial_delay_seconds: 2
+ period_seconds: 1
+ shutdown:
+ command: mysqladmin --socket=$PWD/dev/.mariadb.sock shutdown
+
+ kimai:
+ command: symfony server:start --no-tls --dir=$PWD/dev/kimai --port=8010
+ depends_on:
+ mariadb:
+ condition: process_healthy
+```
+
+Use port 8010 for Kimai to avoid conflicts. Use `$PWD` for paths so process-compose works from project root.
+
+**Create `dev/setup.sh`** (make executable with `chmod +x`):
+
+The script should:
+1. Check if already set up (if `dev/kimai` exists, skip clone)
+2. Initialize MariaDB data directory if not present:
+ ```bash
+ mysql_install_db --datadir=./dev/.mariadb-data --auth-root-authentication-method=normal
+ ```
+3. Start MariaDB temporarily for setup (background, wait for ready):
+ ```bash
+ mysqld --datadir=./dev/.mariadb-data --socket=./dev/.mariadb.sock --port=3307 --skip-grant-tables --bind-address=127.0.0.1 &
+ MARIADB_PID=$!
+ # Wait for MariaDB to be ready
+ for i in $(seq 1 30); do mysqladmin --socket=./dev/.mariadb.sock ping 2>/dev/null && break; sleep 1; done
+ ```
+4. Create the `kimai` database:
+ ```bash
+ mysql --socket=./dev/.mariadb.sock -e "CREATE DATABASE IF NOT EXISTS kimai;"
+ ```
+5. Clone Kimai 2.52.0:
+ ```bash
+ git clone -b 2.52.0 --depth 1 https://github.com/kimai/kimai.git dev/kimai
+ ```
+6. Configure Kimai `.env.local` (NOT .env -- .env.local overrides):
+ ```bash
+ cat > dev/kimai/.env.local << 'ENVEOF'
+ DATABASE_URL=mysql://root@127.0.0.1:3307/kimai?charset=utf8mb4&serverVersion=11.4.8-MariaDB
+ APP_SECRET=change-me-dev-only-not-production
+ APP_ENV=dev
+ ENVEOF
+ ```
+7. Run Composer install in Kimai:
+ ```bash
+ cd dev/kimai && composer install --no-interaction
+ ```
+8. Run Kimai install (creates schema):
+ ```bash
+ bin/console kimai:install -n
+ ```
+9. Load dev fixtures (seeds test data):
+ ```bash
+ bin/console kimai:reset:dev -n
+ ```
+10. Symlink plugin directory:
+ ```bash
+ mkdir -p var/plugins
+ ln -sf "$(cd ../.. && pwd)" var/plugins/KimaiHeatmapBundle
+ ```
+11. Clear Kimai cache:
+ ```bash
+ bin/console cache:clear
+ ```
+12. Stop temporary MariaDB:
+ ```bash
+ kill $MARIADB_PID 2>/dev/null
+ wait $MARIADB_PID 2>/dev/null
+ ```
+13. Print success message with instructions to run `process-compose -f dev/process-compose.yaml up`
+
+**Important details:**
+- Use `set -euo pipefail` at the top
+- Use `127.0.0.1` NOT `localhost` in DATABASE_URL (localhost triggers Unix socket mode on Linux)
+- The symlink target name MUST be `KimaiHeatmapBundle` (matches bundle class name)
+- Use `--skip-grant-tables` for MariaDB since this is local dev only
+- The script must be idempotent: skip steps that are already done (check for existing directories)
+
+
+ cd /home/toph/code/toph/kimai-heatmap && test -x dev/setup.sh && test -f dev/process-compose.yaml && head -5 dev/process-compose.yaml | grep -q "version" && echo "OK"
+
+ dev/process-compose.yaml defines MariaDB + Kimai processes with health checks. dev/setup.sh is executable and contains the full bootstrap sequence (clone, install, seed, symlink).
+
+
+
+
+
+## Trust Boundaries
+
+| Boundary | Description |
+|----------|-------------|
+| Local dev only | No external network exposure; MariaDB binds to 127.0.0.1, Kimai on localhost:8010 |
+
+## STRIDE Threat Register
+
+| Threat ID | Category | Component | Disposition | Mitigation Plan |
+|-----------|----------|-----------|-------------|-----------------|
+| T-01-01 | Information Disclosure | MariaDB --skip-grant-tables | accept | Local dev only, bound to 127.0.0.1, no production data. Acceptable for dev environment. |
+| T-01-02 | Information Disclosure | APP_SECRET in .env.local | accept | Dev-only value, .env.local is gitignored inside dev/kimai/ (Kimai's own .gitignore), not committed to plugin repo |
+
+
+
+1. `nix develop` provides all required tools (PHP 8.2+, Composer, Node, MariaDB, symfony-cli, process-compose)
+2. `dev/setup.sh` exists, is executable, and contains the complete bootstrap sequence
+3. `dev/process-compose.yaml` defines MariaDB and Kimai processes with dependency ordering
+4. `.gitignore` excludes dev artifacts
+
+
+
+Running `nix develop` enters a shell with all dev tools. The setup script and process-compose config are ready to be executed in Plan 02.
+
+
+
diff --git a/.planning/phases/01-dev-environment/01-02-PLAN.md b/.planning/phases/01-dev-environment/01-02-PLAN.md
new file mode 100644
index 0000000..5384bf4
--- /dev/null
+++ b/.planning/phases/01-dev-environment/01-02-PLAN.md
@@ -0,0 +1,186 @@
+---
+phase: 01-dev-environment
+plan: 02
+type: execute
+wave: 2
+depends_on:
+ - 01-01
+files_modified:
+ - KimaiHeatmapBundle.php
+ - composer.json
+autonomous: false
+requirements:
+ - DEV-02
+ - DEV-03
+must_haves:
+ truths:
+ - "Running dev/setup.sh bootstraps a working Kimai instance with seeded timesheet data"
+ - "The Kimai instance has timesheet entries spanning multiple months"
+ - "Kimai recognizes KimaiHeatmapBundle as a loaded plugin"
+ - "The Kimai dashboard is accessible in the browser at localhost:8010"
+ artifacts:
+ - path: "KimaiHeatmapBundle.php"
+ provides: "Symfony bundle class implementing Kimai PluginInterface"
+ contains: "implements PluginInterface"
+ - path: "composer.json"
+ provides: "Plugin metadata with kimai-plugin type and autoload config"
+ contains: "kimai-plugin"
+ key_links:
+ - from: "dev/kimai/var/plugins/KimaiHeatmapBundle"
+ to: "project root"
+ via: "symlink"
+ pattern: "KimaiHeatmapBundle"
+ - from: "KimaiHeatmapBundle.php"
+ to: "Kimai plugin loader"
+ via: "PluginInterface implementation"
+ pattern: "PluginInterface"
+---
+
+
+Run the setup script to bootstrap Kimai, create the minimal plugin scaffold, and verify the full dev environment works end-to-end.
+
+Purpose: Validate that the dev environment from Plan 01 actually produces a working Kimai instance with test data and a recognized plugin.
+Output: Working Kimai instance, minimal plugin bundle class and composer.json, human-verified browser access.
+
+
+
+@/home/toph/code/toph/kimai-heatmap/.claude/get-shit-done/workflows/execute-plan.md
+@/home/toph/code/toph/kimai-heatmap/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/phase-1/RESEARCH.md
+@.planning/phases/01-dev-environment/01-01-SUMMARY.md
+
+
+
+
+
+ Task 1: Create plugin scaffold and run setup
+ KimaiHeatmapBundle.php, composer.json
+
+**Step 1: Create the minimal plugin bundle class** at project root `KimaiHeatmapBundle.php`:
+
+```php
+ /home/toph/code/toph/kimai-heatmap` (absolute path, directory name matches bundle class).
+
+**Step 4: Verify seeded data exists:**
+
+```bash
+cd dev/kimai && bin/console doctrine:query:sql "SELECT COUNT(*) as cnt FROM kimai2_timesheet"
+```
+
+Should return a count of several hundred entries minimum.
+
+**Step 5: Verify plugin is loaded:**
+
+```bash
+cd dev/kimai && bin/console kimai:plugins
+```
+
+Should list "Activity Heatmap" (the plugin name from composer.json extra.kimai.name).
+
+
+ cd /home/toph/code/toph/kimai-heatmap/dev/kimai && bin/console kimai:plugins 2>&1 | grep -i "heatmap" && bin/console doctrine:query:sql "SELECT COUNT(*) as cnt FROM kimai2_timesheet" 2>&1
+
+ Kimai instance running with seeded data. Plugin listed in kimai:plugins output. Database contains timesheet entries.
+
+
+
+ Task 2: Verify dev environment in browser
+ Human verifies the complete dev environment works end-to-end by accessing Kimai in the browser and confirming the plugin is loaded.
+ Complete dev environment: Nix devshell with local Kimai instance, seeded test data, and plugin recognized by Kimai.
+
+ 1. From the project root, run: `process-compose -f dev/process-compose.yaml up`
+ 2. Wait for both MariaDB and Kimai to show as healthy/running
+ 3. Open browser to http://127.0.0.1:8010
+ 4. Log in with username `susan_super` password `password` (super admin from fixtures)
+ 5. Verify the dashboard loads (no errors)
+ 6. Navigate to System > Plugins -- verify "Activity Heatmap" appears in the list
+ 7. Stop process-compose with Ctrl+C
+
+ Human confirms Kimai dashboard loads and plugin is listed
+ Dashboard accessible, plugin visible in admin, seeded data present
+ Type "approved" or describe issues
+
+
+
+
+
+## Trust Boundaries
+
+| Boundary | Description |
+|----------|-------------|
+| Plugin -> Kimai | Plugin runs inside Kimai's PHP process with full access to Kimai internals |
+
+## STRIDE Threat Register
+
+| Threat ID | Category | Component | Disposition | Mitigation Plan |
+|-----------|----------|-----------|-------------|-----------------|
+| T-01-03 | Elevation of Privilege | PluginInterface | accept | Plugin runs with same privileges as Kimai itself. This is by design in Kimai's plugin architecture. Personal use, single user. |
+| T-01-04 | Information Disclosure | Fixture credentials (password: "password") | accept | Dev environment only. Fixtures create test users with known passwords. Not exposed beyond localhost. |
+
+
+
+1. `bin/console kimai:plugins` lists "Activity Heatmap"
+2. `SELECT COUNT(*) FROM kimai2_timesheet` returns > 100 entries
+3. Kimai dashboard loads in browser at http://127.0.0.1:8010
+4. Plugin appears in System > Plugins admin page
+
+
+
+Developer can run `nix develop`, execute `dev/setup.sh` once, then `process-compose -f dev/process-compose.yaml up` to get a working Kimai instance with seeded data and the heatmap plugin recognized. Dashboard accessible in browser, plugin listed in admin.
+
+
+