opdev / opdev/agentic-status-report
M4: Slack Edit and Regenerate draft flows
@makon57 is already working on this.
Since Aug 24, 2026.
- 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)
- User clicks Edit on draft DM
- Within 3 seconds:
ack()thenviews.openwith modal (must open before slow DB reads —trigger_idexpires in 3s) - 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_promptflag if present) - Free-text: leadership asks (aggregate or per-entry
askfields)
- One editable text field per epic (pre-filled with
- On submit:
- For changed outcomes: supersede current row, insert new row with
source = 'drafted_edited', incrementrevision, setsupersedes_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)
- For changed outcomes: supersede current row, insert new row with
- 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_blankonstatus_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 incrementsupersede_unconfirmed_draftslogic for per-epic grainEntrySource.DRAFTED_EDITEDenum already exists inmodels.py
Preserve: original drafted rows remain in DB (append-only); only is_current flips.
Regenerate flow requirements
UX
- User clicks Regenerate
ack()→ open modal with reason select (e.g. "Wrong epic grouping", "Missing work", "Tone/ wording", "Other" + optional text)- On submit (background task after ack):
- Re-run collector + drafter for
(person_id, week_ending)— reuserun_collect+draft_and_persistfromsrc/status/skills/drafter.py persist_draft_outputalready supersedes unconfirmed drafts idempotently- Update
participation.regenerated = true,regenerate_reason = <reason> - Replace Slack message with new draft blocks via
chat.update
- Re-run collector + drafter for
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 rowis_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
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.