Extract shared code that was duplicated across functions:
- taskJSON struct (MarshalJSON/UnmarshalJSON) to package-level type
- scanTask(scanner) helper for GetTask/GetTasks (~70 identical lines)
- monthNames map for parseMonthName/parseDayAndMonth
- applyNonDateAttribute helper for Apply/ApplyToNew
- resolveDisplayID calls replace inline loops in FormatTaskListWithFormat
Replace O(n²) bubble sorts with sort.Slice in all four report sort
functions (sortByUrgency, NewestReport, NextReport, OldestReport).
Remove dead code: formatTimeWithColor (unused, also used time.Now()
instead of timeNow()), getCurrentTimestamp (unnecessary wrapper).
Remove ~20 comments that restated the next line of code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add migration v2: source column on change_log to distinguish local
vs sync-originated entries, preventing the echo loop where synced
tasks get re-pushed as local changes
- PushChanges handler now skips save when server version is newer
- Client PushChanges/pushQueuedChanges collect and report marshal errors
instead of silently dropping them
- De-duplicate getLocalChanges/getLastSyncTime into exported sync
package functions
- Fix logConflict winner detection via pointer identity instead of
fragile UUID+timestamp comparison
- Fix sync down to actually parse, save, and tag-sync pulled changes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add annotations as JSON column on tasks table with Annotate/Denotate
methods and CLI commands. Add undo system backed by change_log with
lightweight undo_stack table (capped at 10 entries). All mutating CLI
commands (add, done, delete, modify, start, stop) now record undo
entries. Undo restores prior task state from change_log data.
Schema changes (in v1 migration):
- annotations TEXT column on tasks
- undo_stack table
- annotations field in change_log triggers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract CreateRecurringTask into engine package for reuse by both CLI
and API. Add POST /tasks/parse endpoint for CLI-style input parsing.
Remove FK constraint on change_log to preserve history after task
deletion. Update web frontend to filter completed tasks from view and
add mock mode support for development.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Separate configuration from data storage and make paths configurable
via environment variables and command-line flags. This improves
Unix/Linux compliance and supports both development and production
deployments.
Key changes:
- Separate config dir (opal.yml) from data dir (database, logs)
- Support XDG Base Directory specification
- Add --config-dir and --data-dir flags
- Environment variables: OPAL_CONFIG_DIR, OPAL_DATA_DIR, OPAL_DB_PATH
- Smart fallback: /etc/opal, /var/lib/opal -> ~/.config/opal, ~/.local/share/opal
- Server mode validates required OAuth/JWT environment variables
- Update naming from 'jade' to 'opal' throughout
- Update systemd service name to 'opal.service'
- Add migration guide in README
Default paths:
- Config: /etc/opal (fallback: ~/.config/opal)
- Data: /var/lib/opal (fallback: ~/.local/share/opal)
Files modified:
- internal/engine/config.go: New directory resolution logic
- internal/engine/database.go: Auto-create data directory
- cmd/root.go: Add global flags for directory overrides
- cmd/server.go: Add configuration validation
- cmd/sync.go, internal/sync/*: Use new path helper functions
- tests: Update to use directory overrides
- docs: Update deployment guide and README
- Add OAuth2 client for Authentik integration
- Implement JWT token generation and validation
- Add refresh token support with database storage
- Update database schema with oauth_subject, oauth_provider, and refresh_tokens table
- Create auth package with config, jwt, oauth, and token management
- Add OAuth endpoints: /auth/login, /auth/callback, /auth/refresh, /auth/logout
- Update AuthMiddleware to support both JWT and API key authentication
- Add user helper functions for OAuth user creation and retrieval
- Add .env.example with OAuth configuration template
API keys still work for CLI compatibility while JWT tokens support web/mobile clients.
Implement complete key:value format parsing for change log entries and fix
critical tag synchronization issue from server to client.
Key Changes:
1. Shared Key:Value Parser (NEW: internal/engine/parser.go)
- Created ParseKeyValueFormat() for both edit and sync operations
- Supports flexible whitespace: 'key:value' and 'key: value'
- Handles comment skipping for edit files
- Consolidates parsing logic (DRY principle)
2. Database Triggers - Tags Support (internal/engine/database.go)
- Added tags to track_task_create trigger
- Added tags to track_task_update trigger
- Tags sorted alphabetically via SQL ORDER BY
- Format: 'tags: alpha,bravo,charlie'
3. Task Creation - Tag Update Fix (internal/engine/task.go)
- CreateTaskWithModifier() now triggers update after adding tags
- Ensures tags appear in change log (UPDATE entry)
- Fixes missing tags in initial CREATE entries
4. Edit Command - Use Shared Parser (cmd/edit.go)
- Replaced custom parseEditedFile() with shared ParseKeyValueFormat()
- Added tag sorting in parseTags()
- Removed ~30 lines, improved maintainability
5. Sync Client - Complete Implementation (internal/sync/client.go)
- NEW: applyChangeDataToTask() - parses all fields from change log
- NEW: Helper functions for status, priority, tag parsing
- FIXED: parseChanges() - sort by timestamp+ID before grouping
- Added parent/child task ordering (prevents FK violations)
- Enhanced tag sync in merge loop with task reload
- Specific validation error messages per field
Critical Bug Fix:
- When CREATE and UPDATE have same timestamp, old code kept CREATE (no tags)
- New code sorts by ID as tiebreaker, ensuring UPDATE (with tags) is used
- Verified: Server->client tag sync now works correctly
Validation:
- Description must not be empty (both edit and sync)
- Recurrence validated (not negative, max 100 years)
- All timestamps parsed correctly (Unix epoch)
- Tags sorted alphabetically in all contexts
Testing:
- Fresh pull from server: ✅ All tags present
- API-created tasks: ✅ Tags sync correctly
- Local->server->client round-trip: ✅ No data loss
- Same-second CREATE+UPDATE: ✅ Correct entry processed
- Parent/child tasks: ✅ Correct ordering
Files Changed:
- NEW: internal/engine/parser.go (+44 lines)
- Modified: internal/engine/database.go (+10 lines)
- Modified: internal/engine/task.go (+8 lines)
- Modified: cmd/edit.go (-25 lines net)
- Modified: internal/sync/client.go (+280 lines)
- Modified: srv/README.md (+1 line)
Total: +318 lines added, -25 removed, net +293 lines
This completes Phase 6: Full bidirectional sync with complete tag support.