**Implementation Plan: Add Vote Value 0 for Retraction**
**Objective**
Enable the `POST /votes/{target_type}/{target_uid}` endpoint to accept `{"value": 0}` as a valid request, allowing clients to retract a vote in a single call. The core logic (`apply_vote`) already handles `value=0`; only the Pydantic validator blocks it.
---
### Changes Required
| File | Change | Rationale |
|------|--------|-----------|
| `devplacepy/models.py:442` | Expand the validator tuple from `(1, -1)` to `(-1, 0, 1)` (order irrelevant) | This is the single root cause – the form-level Pydantic field validator rejects `0`. |
| `devplacepy/services/devii/actions/catalog/engagement.py:25-28` | Update the `value` parameter description to include `0` for retraction (e.g., `"Vote value: 1 to upvote, -1 to downvote, 0 to retract."`) | Keeps the action catalog consistent with the new behaviour. |
| `tests/api/votes.py` | Add a new test function `test_explicit_zero_retracts_vote` that:
1. Inserts an upvote (value=1).
2. Sends `{"value": 0}`.
3. Validates the response has `"value": 0` and `"net": 0` (or unchanged from initial).
4. Optionally verifies the row is soft-deleted or updated correctly. | Explicit `value=0` has no test coverage. Adding one ensures the new path is exercised. |
**Do not modify any other files.** No database, content logic, template, or documentation changes are required.
---
### Execution Steps (for a competent engineer)
1. **Check out a clean branch** from the current base (e.g. `git checkout -b vote-zero-retraction` off `main` or `develop`).
*Do not attempt to rebase onto a branch that has unresolved conflicts.*
2. **Apply the three changes above** using your editor or `sed`.
3. **Run lint**:
```bash
pip install -q ruff && ruff check .
```
No new violations should appear. (Existing warnings outside changed files are acceptable.)
4. **Run unit tests**:
```bash
pip install -e '.[dev]' -q && make test-unit
```
All tests must pass. The pre-existing `test_owns_instance_true_for_creator` failure observed in earlier attempts is **not caused by these changes** (no file touched affects `content.py`). If that test fails, it indicates a pre-existing issue in the base branch or environment; the engineer should flag it but not consider it a blocker for this change unless it is newly introduced. (Verification of this commit on a clean base should produce a clean run.)
5. **Commit** with a message like `Allow vote value 0 for retraction` and push.
---
### Definition of Done
- [ ] The Pydantic validator at `devplacepy/models.py:442` accepts value `0` in addition to `1` and `-1`.
- [ ] The Devii action catalog description at `devplacepy/services/devii/actions/catalog/engagement.py:25` mentions `0` as a valid vote value for retraction.
- [ ] A new test `test_explicit_zero_retracts_vote` exists in `tests/api/votes.py` and passes.
- [ ] `ruff check .` passes with no new violations.
- [ ] `make test-unit` passes (all tests green, ignoring any pre-existing failures unrelated to this change).
- [ ] No other files are modified.