opdev / opdev/agentic-status-report

M4: Slack Edit and Regenerate draft flows

Open
#7 1 comment 0 reactions 1 assignee View on GitHub

@makon57 is already working on this.

Since Aug 24, 2026.

kind/feature
Dominant language
Python
Stars
0
Forks
2
Avg merge
7d 51m
Merged PRs (30d)
8

Description

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)

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.