Skip to content

M4: Slack Edit and Regenerate draft flows #7

Description

@yashoza19

Summary

Implement M4: Slack Edit and Regenerate flows. M3 stubs these with ephemeral placeholders in src/status/slack/handlers.py (lines 107–123). After M4, users can correct drafts without leaving Slack; edits create new ledger revisions with source = 'drafted_edited'.

Design reference: docs/DESIGN.md §5.3 (Slackbot)

Current stubs (replace these)

# src/status/slack/handlers.py
@app.action(ACTION_EDIT)
def on_edit(...):
    ack()
    client.chat_postEphemeral(..., text="Edit flow is coming in the next milestone.")

@app.action(ACTION_REGENERATE)
def on_regenerate(...):
    ack()
    client.chat_postEphemeral(..., text="Regenerate flow is coming in the next milestone.")

Action IDs (already wired in blocks): status_edit, status_regenerate — see src/status/slack/blocks.py.

Edit flow requirements

UX (from design doc)

  1. User clicks Edit on draft DM
  2. Within 3 seconds: ack() then views.open with modal (must open before slow DB reads — trigger_id expires in 3s)
  3. Modal fields:
    • One editable text field per epic (pre-filled with outcome)
    • Checkbox per epic to drop entry from draft
    • Free-text: unticketed work (from unticketed_prompt flag if present)
    • Free-text: leadership asks (aggregate or per-entry ask fields)
  4. On submit:
    • For changed outcomes: supersede current row, insert new row with source = 'drafted_edited', increment revision, set supersedes_entry_id
    • For dropped epics: mark current row is_current = false (or equivalent — preserve audit chain)
    • Update Slack message via chat.update (reuse _update_message)
  5. Participation stays sent (not confirmed) until user clicks Looks right

Modal constraints

  • Slack modal block limit ~100 blocks (~20 epics max)
  • If more epics: cap modal, note remainder in modal description
  • Validate non-blank outcomes (CheckConstraint outcome_not_blank on status_entry)

DB work needed

New module or functions in src/status/db/ (suggested: src/status/db/edit.py):

def apply_edited_entries(
    session,
    person_id: str,
    week_ending: date,
    edits: list[EditSpec],  # epic_key, new_outcome, drop: bool
    *,
    unticketed: str | None,
    leadership_ask: str | None,
) -> list[StatusEntry]:
    ...

Reuse patterns from src/status/db/draft.py:

  • _status_entry_from_draft / revision increment
  • supersede_unconfirmed_drafts logic for per-epic grain
  • EntrySource.DRAFTED_EDITED enum already exists in models.py

Preserve: original drafted rows remain in DB (append-only); only is_current flips.

Regenerate flow requirements

UX

  1. User clicks Regenerate
  2. ack() → open modal with reason select (e.g. "Wrong epic grouping", "Missing work", "Tone/ wording", "Other" + optional text)
  3. On submit (background task after ack):
    • Re-run collector + drafter for (person_id, week_ending) — reuse run_collect + draft_and_persist from src/status/skills/drafter.py
    • persist_draft_output already supersedes unconfirmed drafts idempotently
    • Update participation.regenerated = true, regenerate_reason = <reason>
    • Replace Slack message with new draft blocks via chat.update

Slack timing

  • Ack interactivity immediately
  • Heavy work (collect + skill call) in background thread/async task
  • On failure: ephemeral error to user; do not leave message in broken state

Files to create/modify

File Changes
src/status/slack/handlers.py Replace stubs; add modal open/submit handlers
src/status/slack/blocks.py Optional: modal view builders, regenerate reason blocks
src/status/db/edit.py New: revision logic for edited entries
src/status/db/confirm.py Optional: helper to reset participation on regenerate
tests/test_slack_edit.py New: unit tests for edit revision logic
tests/test_slack_regenerate.py New: mock drafter re-run + supersede

Tests

Follow existing patterns in tests/test_slack_confirm.py and tests/test_draft_persist.py:

  • Edit one epic outcome → new row, source='drafted_edited', old row is_current=false
  • Drop epic → no current row for that epic grain
  • Regenerate → superseded_count > 0, new drafts, participation.regenerated=true
  • Golden tests on JSON/DB shape, not prose wording

Run: pytest tests/test_slack_edit.py tests/test_slack_regenerate.py tests/test_draft_persist.py

Acceptance criteria

  • Edit modal opens within Slack timeout; submit persists revisions
  • Regenerate re-runs drafter and updates DM
  • Original draft rows preserved in ledger (audit chain intact)
  • Looks right still works on edited drafts
  • Unit tests pass; no regression in test_slack_confirm.py

Branch / PR

  • Branch: feat/m4-edit-regenerate
  • Base: main (after M3.5 or M0–M3 merge — can parallel with M3.5)
  • PR title: feat(slack): edit and regenerate draft flows (M4)

Depends on

  • M0–M3 merged to main (Slack confirm + draft persistence)

Out of scope

  • OpenShift changes (bot Deployment from M3.5 works unchanged)
  • Synthesizer / report generation (M5)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

kind/featureCategorizes issue or PR as related to a new feature.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions