docs(phase-1): research dev environment setup
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
fba1ad4ab1
commit
29b54a340f
1 changed files with 361 additions and 0 deletions
361
.planning/phase-1/RESEARCH.md
Normal file
361
.planning/phase-1/RESEARCH.md
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
# Phase 1: Dev Environment - Research
|
||||
|
||||
**Researched:** 2026-04-08
|
||||
**Domain:** Nix flake devshell, Kimai 2.x local instance, MariaDB, PHP 8.2+, data seeding
|
||||
**Confidence:** HIGH
|
||||
|
||||
## Summary
|
||||
|
||||
Phase 1 is about getting a reproducible dev environment where `nix develop` gives you a working Kimai instance with test data ready for plugin development. The key findings are:
|
||||
|
||||
1. **Kimai requires MySQL/MariaDB** -- SQLite support was removed years ago. The Nix flake must provision a local MariaDB instance (11.4.x available in nixpkgs). This is the single biggest complexity driver for this phase.
|
||||
2. **Kimai 2.52.0** is the current latest stable release (March 2025), requiring PHP 8.1-8.5 and Symfony 6.4. PHP 8.2.29 is already available in the system nixpkgs as `php82`.
|
||||
3. **Kimai ships built-in fixtures** (`bin/console kimai:reset:dev` loads Doctrine fixtures that create ~100-1000 timesheet entries per user across 3 years of data). No need to hand-roll a seed script.
|
||||
4. **Plugins live in `var/plugins/`** with a symlink from the project directory. The namespace must be `KimaiPlugin\<BundleName>` and the bundle class must implement `App\Plugin\PluginInterface`.
|
||||
|
||||
**Primary recommendation:** Use a shell script (`dev/start.sh`) invoked from the Nix devshell that starts MariaDB in a local data directory, runs Kimai install + fixture loading, symlinks the plugin, and starts the Symfony dev server. Use `process-compose` to manage MariaDB + web server lifecycle.
|
||||
|
||||
<phase_requirements>
|
||||
## Phase Requirements
|
||||
|
||||
| ID | Description | Research Support |
|
||||
|----|-------------|------------------|
|
||||
| DEV-01 | Nix flake/devshell provides a running local Kimai instance | Nix flake with php82, mariadb, composer, nodejs, symfony-cli, process-compose; shell hook bootstraps Kimai |
|
||||
| DEV-02 | Local Kimai has a seeded database with realistic time entry data | `bin/console kimai:reset:dev` loads built-in Doctrine fixtures with thousands of timesheet entries |
|
||||
| DEV-03 | Plugin is loadable in the local Kimai instance for manual testing | Symlink project dir into Kimai's `var/plugins/KimaiHeatmapBundle`, clear cache |
|
||||
</phase_requirements>
|
||||
|
||||
## Standard Stack
|
||||
|
||||
### Core (Nix Flake Packages)
|
||||
|
||||
| Package | Nixpkgs Attr | Version | Purpose |
|
||||
|---------|-------------|---------|---------|
|
||||
| PHP | `php82` | 8.2.29 | Kimai runtime (with extensions: gd, intl, mbstring, pdo_mysql, xml, xsl, zip, tokenizer) | [VERIFIED: nix search nixpkgs php82] |
|
||||
| MariaDB | `mariadb` | 11.4.8 | Database server (Kimai dropped SQLite support) | [VERIFIED: nix search nixpkgs mariadb] |
|
||||
| Composer | `php82Packages.composer` | 2.8.x | PHP dependency management | [VERIFIED: system composer --version] |
|
||||
| Node.js | `nodejs` | 22.x | JS tooling for d3/esbuild (later phases) | [VERIFIED: system node --version] |
|
||||
| npm | (bundled with nodejs) | 10.x | JS package manager | [VERIFIED: system npm --version] |
|
||||
| symfony-cli | `symfony-cli` | 5.16.0 | Dev web server with PHP integration | [VERIFIED: nix search nixpkgs symfony-cli] |
|
||||
| process-compose | `process-compose` | 1.78.0 | Process manager for MariaDB + web server | [VERIFIED: nix search nixpkgs process-compose] |
|
||||
|
||||
### Kimai
|
||||
|
||||
| Property | Value | Source |
|
||||
|----------|-------|--------|
|
||||
| Latest stable | 2.52.0 | [VERIFIED: github.com/kimai/kimai/releases] |
|
||||
| PHP support | 8.1 - 8.5 | [VERIFIED: composer.json require] |
|
||||
| Symfony version | 6.4 LTS | [VERIFIED: composer.json extra] |
|
||||
| Database | MySQL 8.3+ or MariaDB 11.1+ | [CITED: kimai.org/documentation/developers.html] |
|
||||
| SQLite | NOT supported (removed 2021) | [VERIFIED: kimai.org/en/blog/2021/sqlite-and-ftp-support-removed] |
|
||||
|
||||
### Required PHP Extensions
|
||||
|
||||
From Kimai's `composer.json` [VERIFIED]:
|
||||
|
||||
`ext-gd`, `ext-intl`, `ext-json`, `ext-mbstring`, `ext-pdo`, `ext-tokenizer`, `ext-xml`, `ext-xsl`, `ext-zip`
|
||||
|
||||
Plus `pdo_mysql` for MariaDB connectivity.
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### Nix Flake Structure
|
||||
|
||||
```
|
||||
flake.nix # PHP, MariaDB, Composer, Node, symfony-cli, process-compose
|
||||
flake.lock
|
||||
dev/
|
||||
process-compose.yaml # MariaDB + symfony web server process definitions
|
||||
setup.sh # One-time: clone Kimai, composer install, DB setup, fixtures, symlink plugin
|
||||
seed.sh # Re-run fixtures without full setup
|
||||
```
|
||||
|
||||
### Pattern 1: Local MariaDB with Data Directory in Project
|
||||
|
||||
**What:** MariaDB runs from a local data directory (`./dev/.mariadb-data/`) using `mysqld --datadir` without needing root or system-level MySQL installation. [ASSUMED]
|
||||
**When to use:** Always for this project -- avoids polluting the system.
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
# Initialize MariaDB data directory
|
||||
mysql_install_db --datadir=./dev/.mariadb-data --auth-root-authentication-method=normal
|
||||
|
||||
# Start MariaDB on a non-standard port (to avoid conflicts)
|
||||
mysqld --datadir=./dev/.mariadb-data --socket=./dev/.mariadb.sock --port=3307 --skip-grant-tables
|
||||
```
|
||||
|
||||
### Pattern 2: Kimai Clone as Git-Ignored Dependency
|
||||
|
||||
**What:** Clone Kimai into `./dev/kimai/` (git-ignored), run `composer install` there, then symlink the plugin directory into `./dev/kimai/var/plugins/KimaiHeatmapBundle`.
|
||||
**Why:** Keeps the host application separate from plugin source. Kimai is a dependency, not part of the plugin repo.
|
||||
|
||||
```
|
||||
dev/
|
||||
kimai/ # .gitignore'd -- cloned Kimai instance
|
||||
var/
|
||||
plugins/
|
||||
KimaiHeatmapBundle -> ../../../../ # symlink to project root
|
||||
```
|
||||
|
||||
### Pattern 3: Plugin Symlink for Live Development
|
||||
|
||||
**What:** Symlink the project root (which IS the plugin bundle) into Kimai's `var/plugins/` directory. [CITED: kimai.org/documentation/plugins.html]
|
||||
**Why:** Code changes in the plugin are immediately reflected. No copy/install step needed.
|
||||
|
||||
```bash
|
||||
ln -sf "$(pwd)" ./dev/kimai/var/plugins/KimaiHeatmapBundle
|
||||
```
|
||||
|
||||
**Critical:** The symlink target directory name MUST match the bundle class name (`KimaiHeatmapBundle`).
|
||||
|
||||
### Pattern 4: process-compose for Multi-Service Dev
|
||||
|
||||
**What:** `process-compose` manages MariaDB and Symfony dev server as a single dev stack.
|
||||
**Why:** Both services need to run simultaneously. process-compose handles startup order, log aggregation, and clean shutdown. Better than manual terminal management.
|
||||
|
||||
```yaml
|
||||
# dev/process-compose.yaml
|
||||
version: "0.5"
|
||||
processes:
|
||||
mariadb:
|
||||
command: mysqld --datadir=./dev/.mariadb-data --socket=./dev/.mariadb.sock --port=3307
|
||||
readiness_probe:
|
||||
exec:
|
||||
command: mysqladmin --socket=./dev/.mariadb.sock ping
|
||||
initial_delay_seconds: 2
|
||||
period_seconds: 1
|
||||
kimai:
|
||||
command: symfony server:start --no-tls --dir=./dev/kimai
|
||||
depends_on:
|
||||
mariadb:
|
||||
condition: process_healthy
|
||||
```
|
||||
|
||||
### Anti-Patterns to Avoid
|
||||
|
||||
- **Using SQLite for dev:** Kimai dropped SQLite support. Migrations will fail. No workaround. [VERIFIED]
|
||||
- **Installing Kimai globally or via Composer require:** Kimai is the host application, not a library. Clone it.
|
||||
- **Trying to Nixify Composer dependencies:** Let Composer manage `vendor/` normally inside the Kimai clone. Nix provides the PHP binary and extensions only.
|
||||
- **Running MariaDB as a system service:** Use a local data directory. No root, no systemd, no conflicts with existing databases.
|
||||
|
||||
## Don't Hand-Roll
|
||||
|
||||
| Problem | Don't Build | Use Instead | Why |
|
||||
|---------|-------------|-------------|-----|
|
||||
| Test data seeding | Custom SQL insert scripts | `bin/console kimai:reset:dev` | Kimai ships Doctrine fixtures that create users, customers, projects, activities, and 100-1000+ timesheet entries per user spanning 3 years [VERIFIED: TimesheetFixtures.php] |
|
||||
| PHP dev web server | nginx/Apache config | `symfony server:start` | Handles PHP-FPM, routing, and `.env` loading automatically [VERIFIED: nixpkgs symfony-cli 5.16.0] |
|
||||
| Process management | Shell scripts with `&` and `trap` | `process-compose` | Handles startup ordering, health checks, log aggregation, clean shutdown [VERIFIED: nixpkgs process-compose 1.78.0] |
|
||||
| MariaDB initialization | Manual SQL DDL | `mysql_install_db` + `kimai:install` | Standard tooling that handles system tables, migrations, and schema creation |
|
||||
|
||||
## Database Schema (for Seeding Context)
|
||||
|
||||
Key tables for timesheet data [VERIFIED: Kimai source Entity classes]:
|
||||
|
||||
| Table | Key Columns | Notes |
|
||||
|-------|-------------|-------|
|
||||
| `kimai2_timesheet` | `id`, `start_time`, `end_time`, `duration`, `timezone`, `date_tz`, `user` (FK), `project_id` (FK), `activity_id` (FK), `rate`, `billable`, `exported` | Duration in seconds |
|
||||
| `kimai2_customers` | `id`, `name`, `country`, `currency`, `timezone`, `visible` | Required parent for projects |
|
||||
| `kimai2_projects` | `id`, `name`, `customer_id` (FK), `visible`, `billable` | Required parent for activities |
|
||||
| `kimai2_activities` | `id`, `name`, `project_id` (FK, nullable), `visible`, `billable` | Can be global (null project) or project-specific |
|
||||
| `kimai2_users` | `id`, `username`, `email`, `roles`, `password`, `timezone` | Created via `kimai:user:create` |
|
||||
|
||||
**The `kimai:reset:dev` command** loads Doctrine fixtures that populate ALL these tables with realistic data, including:
|
||||
- 5 users (clara_customer, john_user, tony_teamlead, anna_admin, susan_super) -- all with password "password" [CITED: kimai.org/documentation/developers.html]
|
||||
- Multiple customers, projects, and activities
|
||||
- 100-1000 timesheet entries per user spanning ~3 years back [VERIFIED: TimesheetFixtures.php source]
|
||||
|
||||
## Kimai Setup Commands (Exact Sequence)
|
||||
|
||||
```bash
|
||||
# 1. Clone Kimai at specific version
|
||||
git clone -b 2.52.0 --depth 1 https://github.com/kimai/kimai.git dev/kimai
|
||||
|
||||
# 2. Configure environment
|
||||
cp dev/kimai/.env.dist dev/kimai/.env
|
||||
# Edit .env: set DATABASE_URL, APP_SECRET
|
||||
|
||||
# 3. Install PHP dependencies (--no-dev is fine for plugin dev; add --dev if you need Kimai's own test tools)
|
||||
cd dev/kimai && composer install --no-dev --optimize-autoloader
|
||||
|
||||
# 4. Create database and run migrations
|
||||
bin/console kimai:install -n
|
||||
|
||||
# 5. Load demo fixtures (drops and recreates schema with test data!)
|
||||
bin/console kimai:reset:dev
|
||||
|
||||
# 6. Symlink plugin
|
||||
ln -sf "$(pwd)/../../" var/plugins/KimaiHeatmapBundle
|
||||
|
||||
# 7. Clear cache (required after adding plugin)
|
||||
bin/console cache:clear
|
||||
```
|
||||
|
||||
**DATABASE_URL format for local MariaDB:**
|
||||
```
|
||||
DATABASE_URL=mysql://root@127.0.0.1:3307/kimai?charset=utf8mb4&serverVersion=11.4.8-MariaDB
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
### Pitfall 1: MariaDB Socket vs TCP Connection
|
||||
|
||||
**What goes wrong:** Kimai tries to connect via Unix socket (default MySQL behavior) but the local MariaDB is listening on a custom socket path or TCP port.
|
||||
**Why it happens:** Default MySQL client config looks for `/var/run/mysqld/mysqld.sock` which doesn't exist in a Nix devshell.
|
||||
**How to avoid:** Use TCP connection (`127.0.0.1:3307`) in DATABASE_URL, not `localhost` (which triggers socket mode on Linux). Or set `--socket` path explicitly.
|
||||
**Warning signs:** "Can't connect to local MySQL server through socket" errors.
|
||||
|
||||
### Pitfall 2: Kimai Cache Issues After Plugin Symlink
|
||||
|
||||
**What goes wrong:** After symlinking the plugin, Kimai doesn't see it or throws container compilation errors.
|
||||
**Why it happens:** Symfony caches the container, routes, and bundle list. A new plugin requires cache clear.
|
||||
**How to avoid:** Always run `bin/console cache:clear` after symlinking. In dev, set `APP_ENV=dev` to enable auto-recompilation.
|
||||
**Warning signs:** Plugin not listed in admin, or DI errors referencing old cached container.
|
||||
|
||||
### Pitfall 3: PHP Extension Mismatch in Nix
|
||||
|
||||
**What goes wrong:** Kimai fails at runtime with "extension not found" errors even though you listed extensions in `flake.nix`.
|
||||
**Why it happens:** Nix PHP needs explicit extension configuration via `php.withExtensions` or `php.buildEnv`. Simply adding `php82` doesn't include all extensions.
|
||||
**How to avoid:** Build a custom PHP with required extensions:
|
||||
```nix
|
||||
php = pkgs.php82.buildEnv {
|
||||
extensions = { enabled, all }: enabled ++ (with all; [ xsl ]);
|
||||
};
|
||||
```
|
||||
The default `enabled` set includes most common extensions (gd, intl, mbstring, pdo, xml, zip, etc.) but `xsl` is often missing. [ASSUMED]
|
||||
**Warning signs:** `composer install` fails, or Kimai throws "class not found" for Intl/GD operations.
|
||||
|
||||
### Pitfall 4: kimai:reset:dev Drops Everything
|
||||
|
||||
**What goes wrong:** Running `kimai:reset:dev` drops the entire database schema and reloads fixtures. Developers run it thinking it adds data on top of existing data.
|
||||
**Why it happens:** The command runs `doctrine:fixtures:load` which purges all tables first.
|
||||
**How to avoid:** Only run during initial setup or when you explicitly want a clean slate. For adding entries, write a separate seed script or use Kimai's API.
|
||||
**Warning signs:** Custom data disappears after running the command.
|
||||
|
||||
### Pitfall 5: Plugin Directory Name Must Match Bundle Class
|
||||
|
||||
**What goes wrong:** Symlink named `heatmap-plugin` or `KimaiHeatmap` instead of `KimaiHeatmapBundle`. Kimai's plugin loader silently ignores it.
|
||||
**Why it happens:** Kimai expects the directory name in `var/plugins/` to match the bundle class name exactly.
|
||||
**How to avoid:** Name the symlink `KimaiHeatmapBundle` to match the class `KimaiHeatmapBundle.php`.
|
||||
**Warning signs:** Plugin not appearing in Kimai admin, no errors in logs.
|
||||
|
||||
## Validation Architecture
|
||||
|
||||
### Test Framework
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Framework | PHPUnit (match Kimai's version) |
|
||||
| Config file | None yet -- Wave 0 task |
|
||||
| Quick run command | `php vendor/bin/phpunit --filter TestName` |
|
||||
| Full suite command | `php vendor/bin/phpunit` |
|
||||
|
||||
### Phase Requirements to Test Map
|
||||
|
||||
| Req ID | Behavior | Test Type | Automated Command | File Exists? |
|
||||
|--------|----------|-----------|-------------------|-------------|
|
||||
| DEV-01 | Nix devshell provides PHP, Composer, Node | smoke | `nix develop --command bash -c "php --version && composer --version && node --version"` | N/A (manual) |
|
||||
| DEV-02 | Seeded database has timesheet entries | smoke | `cd dev/kimai && bin/console doctrine:query:sql "SELECT COUNT(*) FROM kimai2_timesheet"` | N/A (manual) |
|
||||
| DEV-03 | Plugin is loadable | smoke | `cd dev/kimai && bin/console kimai:plugins` (should list the plugin) | N/A (manual) |
|
||||
|
||||
### Wave 0 Gaps
|
||||
|
||||
- [ ] None for this phase -- DEV-01/02/03 are infrastructure tasks verified by smoke commands, not unit tests.
|
||||
|
||||
## Environment Availability
|
||||
|
||||
| Dependency | Required By | Available | Version | Fallback |
|
||||
|------------|------------|-----------|---------|----------|
|
||||
| Nix | Flake/devshell | Yes | 2.93.3 (Lix) | -- |
|
||||
| PHP 8.2 | Kimai runtime | Yes (system) | 8.2.29 | Provided by Nix flake |
|
||||
| Composer | PHP deps | Yes (system) | 2.8.12 | Provided by Nix flake |
|
||||
| Node.js | JS tooling | Yes (system) | 22.21.1 | Provided by Nix flake |
|
||||
| MariaDB | Database | No (not installed) | -- | Provided by Nix flake (11.4.8 available) |
|
||||
| symfony-cli | Dev server | No (not installed) | -- | Provided by Nix flake (5.16.0 available) |
|
||||
| process-compose | Process mgmt | No (not installed) | -- | Provided by Nix flake (1.78.0 available) |
|
||||
|
||||
**Missing dependencies with no fallback:** None -- all provided by the Nix flake.
|
||||
|
||||
## Plugin Loading Mechanism
|
||||
|
||||
Kimai discovers plugins automatically from `var/plugins/`. Key details [CITED: kimai.org/documentation/plugins.html]:
|
||||
|
||||
1. Plugin directory must contain a bundle class that `implements PluginInterface`
|
||||
2. Namespace must be `KimaiPlugin\<BundleName>` (the `KimaiPlugin` vendor prefix is mandatory)
|
||||
3. Routes auto-load from `Resources/config/routes.{php,yaml}` inside the plugin directory
|
||||
4. Plugins load in ALL environments without modifying `config/bundles.php`
|
||||
5. The `composer.json` must have `"type": "kimai-plugin"` and an `extra.kimai` section with `require` (minimum Kimai version as integer: major*10000 + minor*100 + patch) and `name`
|
||||
|
||||
### Minimum Plugin composer.json
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "kimai-plugin/heatmap-bundle",
|
||||
"type": "kimai-plugin",
|
||||
"description": "GitHub-style activity heatmap dashboard widget",
|
||||
"license": "MIT",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"KimaiPlugin\\KimaiHeatmapBundle\\": ""
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"kimai": {
|
||||
"require": 25200,
|
||||
"name": "Activity Heatmap"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Assumptions Log
|
||||
|
||||
| # | Claim | Section | Risk if Wrong |
|
||||
|---|-------|---------|---------------|
|
||||
| A1 | MariaDB can run with `--datadir` pointing to a local project directory without root | Architecture Patterns | HIGH -- would need alternative DB setup approach (Docker, system service) |
|
||||
| A2 | Nix php82 default `enabled` extensions cover most of Kimai's requirements except `xsl` | Common Pitfalls | MEDIUM -- may need to explicitly list more extensions |
|
||||
| A3 | `symfony server:start` works with the Nix-provided PHP binary | Architecture Patterns | LOW -- symfony-cli auto-detects PHP; fallback is `php -S` |
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **Exact `php.buildEnv` extension list needed**
|
||||
- What we know: Kimai requires gd, intl, mbstring, pdo_mysql, xml, xsl, zip, tokenizer, json
|
||||
- What's unclear: Which of these are in Nix's default `enabled` set vs. needing explicit addition
|
||||
- Recommendation: Start with `enabled ++ [xsl pdo_mysql]` and iterate if `composer install` fails
|
||||
|
||||
2. **MariaDB version compatibility with Kimai migrations**
|
||||
- What we know: Kimai docs say MariaDB 11.1+; nixpkgs has 11.4.8
|
||||
- What's unclear: Whether any migration uses MySQL-specific syntax that breaks on MariaDB 11.4
|
||||
- Recommendation: Use 11.4.8 (latest in nixpkgs); if issues arise, try 10.11.14 (`mariadb_1011`)
|
||||
|
||||
## Sources
|
||||
|
||||
### Primary (HIGH confidence)
|
||||
- [GitHub kimai/kimai releases](https://github.com/kimai/kimai/releases) -- version 2.52.0 confirmed
|
||||
- [Kimai composer.json](https://raw.githubusercontent.com/kimai/kimai/refs/heads/main/composer.json) -- PHP/Symfony versions, extensions
|
||||
- [Kimai Entity/Timesheet.php](https://github.com/kimai/kimai/blob/main/src/Entity/Timesheet.php) -- database schema
|
||||
- [Kimai Entity/Customer.php](https://github.com/kimai/kimai/blob/main/src/Entity/Customer.php) -- customer table schema
|
||||
- [Kimai Entity/Project.php](https://github.com/kimai/kimai/blob/main/src/Entity/Project.php) -- project table schema
|
||||
- [Kimai Entity/Activity.php](https://github.com/kimai/kimai/blob/main/src/Entity/Activity.php) -- activity table schema
|
||||
- [Kimai WidgetInterface.php](https://github.com/kimai/kimai/blob/main/src/Widget/WidgetInterface.php) -- widget API
|
||||
- [Kimai TimesheetFixtures.php](https://github.com/kimai/kimai/blob/main/src/DataFixtures/TimesheetFixtures.php) -- fixture data details
|
||||
- [Kimai DemoBundle](https://github.com/kimai/DemoBundle) -- reference plugin implementation
|
||||
|
||||
### Secondary (MEDIUM confidence)
|
||||
- [Kimai plugin documentation](https://www.kimai.org/documentation/plugins.html) -- plugin loading mechanism
|
||||
- [Kimai installation docs](https://www.kimai.org/documentation/installation.html) -- setup commands
|
||||
- [Kimai developer docs](https://www.kimai.org/documentation/developers.html) -- dev environment, fixture commands
|
||||
- [Kimai SQLite removal announcement](https://www.kimai.org/en/blog/2021/sqlite-and-ftp-support-removed) -- database requirements
|
||||
- [loophp/nix-shell](https://github.com/loophp/nix-shell) -- Nix PHP packaging reference
|
||||
|
||||
### Tertiary (LOW confidence)
|
||||
- None
|
||||
|
||||
## Metadata
|
||||
|
||||
**Confidence breakdown:**
|
||||
- Standard stack: HIGH -- versions verified against nixpkgs and Kimai source
|
||||
- Architecture: HIGH for Kimai plugin mechanics (verified against source), MEDIUM for Nix MariaDB setup (assumed pattern)
|
||||
- Pitfalls: HIGH -- based on verified Kimai constraints (no SQLite, cache clearing, fixture behavior)
|
||||
|
||||
**Research date:** 2026-04-08
|
||||
**Valid until:** 2026-05-08 (Kimai releases roughly monthly; pin to 2.52.0)
|
||||
Loading…
Add table
Reference in a new issue