←
TRCKR-2347
Created: 2026-01-02
•
Updated: 2026-01-02
Relationships
Loading...
Attachments
Loading...
Comments (2)
agent
·
2026-01-02
## Implementation Complete
### Problem
Different endpoints used different patterns for handling partial updates:
- `issues.py` correctly used `model_fields_set` to distinguish null from omitted
- `projects.py` and `milestones.py` used `if field is not None` which prevented clearing nullable fields
- `sync.py` had a local `_filter_update_params()` that wasn't consistent with the other approaches
### Solution
1. **Created `shared/update_merge.py`** with:
- `extract_update_fields(model)` - for Pydantic models (REST endpoints)
- `extract_update_fields_from_dict(data, valid_fields)` - for raw dicts (sync)
- `filter_update_params(entity_type, data)` - main entry point for sync
- `VALID_UPDATE_FIELDS` dict with field sets for all entity types
- `NON_UPDATEABLE_FIELDS` set for fields that should never be updated
2. **Updated `projects.py`** and **`milestones.py`** to use `model_fields_set` pattern (matching issues.py)
3. **Updated `sync.py`** to use the shared `filter_update_params()` function
### Testing
- Added 27 comprehensive tests in `tests/test_shared_update_merge.py` covering:
- Field set detection for Pydantic models
- Null value preservation
- Dict field filtering
- Edge cases (empty string, empty list, boolean false, zero)
- All entity types
All sync update tests pass. Pre-existing test failures in other areas are unrelated.
### Files Changed
- `shared/update_merge.py:1-186` - New utility module
- `shared/__init__.py:18-24,72-77` - Added exports
- `server/routes/projects.py:193-222` - Updated to use model_fields_set
- `server/routes/milestones.py:156-172` - Updated to use model_fields_set
- `server/routes/sync.py:31,38-47` - Replaced local implementation with shared utility
- `tests/test_shared_update_merge.py:1-422` - New test file
triage-agent
·
2026-01-02
## Feature Triage Complete
**Status:** PARTIALLY IMPLEMENTED
**Summary:** The codebase already has multiple separate implementations for null/unset handling:
- Client store uses _UNSET sentinel pattern
- Issues API uses Pydantic's model_fields_set
- Sync endpoint uses _filter_update_params (doesn't distinguish null vs omitted)
- CLI uses Click's ParameterSource
**Gap:** No shared utility exists, and sync endpoint doesn't distinguish null from omitted.
**Implementation Location:**
- NEW: shared/update_merge.py - Central utility
- MODIFY: server/routes/sync.py - Use shared utility
- MODIFY: Projects/milestones endpoints - Adopt model_fields_set pattern
- ADD: tests/test_shared_update_merge.py
**Complexity:** Medium (8-12 files, 1 new file, ~200-300 lines)
See attached triage report at /tmp/triage-TRCKR-2347.md