Review: Confirmation state machine for write operations
Verdict: READY
Template Completeness
- [x] Type — Feature
- [x] Lineage — Depends on #6, split per review-598-2026-03-28
- [x] Repo — forgejo_admin/westside-ai-assistant
- [x] User Story — As Marcus (admin), confirmation before write execution
- [x] Context — explains split from #6, describes confirmation UX flow
- [x] File Targets — 2 create, 1 modify, 3 do-not-touch
- [x] Acceptance Criteria — 5 criteria covering happy/sad/timeout/replacement paths
- [x] Test Expectations — 5 unit tests + run command
- [x] Constraints — message formats, case-insensitive matching, no background timer
- [x] Checklist — standard 3-item
- [x] Related — project page, write-ops story, safety story
Traceability
- [x] story:write-ops — Marcus can update player info via GroupMe with confirmation before writes
- [x] story:safety — Write operations require confirmation, dangerous ops excluded
- [x] arch:A2 — AI engine component (confirmation is an AI engine sub-module)
- [x] Forgejo issue — forgejo_admin/westside-ai-assistant#8, open
File Targets
- [x]
app/confirmation.py(create) — does not exist yet, correct for new file. Functions well-specified: store_pending, check_pending, expire_stale, 5-min TTL, dict keyed by group_id. - [x]
tests/test_confirmation.py(create) — does not exist yet, correct for new file. - [x]
app/ai.py(modify) — does not exist yet but will be created by upstream dependency #6. Modification scope is well-defined: wire confirmation into process_message() flow. - [x]
app/basketball.py(do-not-touch) — consumed as-is, correct. - [x]
app/groupme.py(do-not-touch) — already wired in #6, correct. - [x]
app/config.py(do-not-touch) — no new config needed, correct.
Repo Placement
OK. Issue filed on westside-ai-assistant, all file targets are within westside-ai-assistant. No cross-repo concerns. Single-repo ticket, single-repo work.
Dependencies
- [x] #6 (AI engine core) — HARD dependency, pending. Creates
app/ai.pywhich this ticket modifies. #6 explicitly leaves a "confirmation needed" indicator for writes that this ticket consumes. Clean handoff interface documented in both issues. - [x] #4 (scaffold) and #5 (basketball client) — transitive dependencies via #6. Not direct concerns for this ticket.
Dependency chain is fully documented in Lineage. This ticket cannot move to next_up until #6 is done.
Acceptance Criteria
All 5 criteria are agent-verifiable via unit tests:
- [x] Write request triggers confirmation prompt (not execution) — testable via mock Anthropic response
- [x] "yes" reply executes pending action — testable via mock basketball client
- [x] Non-"yes" reply cancels — testable via state inspection
- [x] 5-minute expiry — testable with time mocking (freeze_time or similar)
- [x] One pending per group (replacement) — testable via sequential state operations
Test command is real:
pytest tests/test_confirmation.py -v. All criteria map to specific test expectations. No ambiguous "works correctly" language.Blast Radius
Low. Confirmation logic is self-contained in a new module (
app/confirmation.py). The only integration point is app/ai.py process_message() flow. basketball-api is consumed read-only — no changes needed there. GroupMe posting is already handled by #6. No similar confirmation state machine patterns exist in sibling services. Rollback is straightforward: revert the PR, writes go back to unconfirmed (which #6 already handles by not executing them).Decomposition Assessment
Three-thing limit: 3 file targets (2 create + 1 modify), all in 1 repo. Passes.
Five-minute rule: Estimated agent time ~3-4 minutes. The state machine is a simple in-memory dict with 3 functions (store_pending, check_pending, expire_stale) plus TTL logic. The ai.py integration is a well-defined if/else branch in process_message(). Passes.
5 acceptance criteria (borderline on the >5 threshold but all are tightly related to one behavioral flow — confirmation lifecycle). No independent subtasks that would benefit from parallelization.
No decomposition needed.
Recommendation
No action needed.