Skip to content

Commit a84831e

Browse files
suyog19claude
andauthored
feat: capture REGENERATE reason as epic planning memory (#38)
* feat: add Re-run Analysis button to project detail page Adds POST /admin/ui/projects/{repo_slug}/rescan route that creates a new onboarding run and enqueues it, then redirects to the live status page — same flow as the wizard but without touching the Jira mapping. Adds CSRF-protected button in the project detail page header. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: add rescan route to CLAUDE.md API endpoints table Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: update CLAUDE.md for monorepo detection, file landmarks, ambiguity check, rescan - repo_profiler: document _scan_for_subdir_stacks() and monorepo detection - claude_client: document file_landmark_map in arch tool, detect_epic_missing_specifics (Haiku), plan_epic_breakdown full-context params - Capability Profiles: add Monorepo detection note - Clarification Loop: add Missing specifics trigger (Epic) row - Project knowledge injection: document file_landmarks bullet - Dashboard: document Re-run Analysis button Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: allow optional trailing text after APPROVE/REJECT/REGENERATE run_id Users naturally append reason text (e.g. "REGENERATE 1 caption was wrong"). Align _APPROVAL_RE with _CLARIFICATION_RE which already supports this. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: capture REGENERATE reason as epic memory for next planning run When user sends "REGENERATE 1 <reason>", the trailing text is now stored as an epic-scoped manual_note so Claude incorporates the feedback on the regenerated breakdown instead of ignoring it. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 759c909 commit a84831e

2 files changed

Lines changed: 15 additions & 5 deletions

File tree

app/telegram.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,24 @@
66

77
logger = logging.getLogger("orchestrator")
88

9-
_APPROVAL_RE = re.compile(r"^(APPROVE|REJECT|REGENERATE)\s+(\d+)(?:\s+.*)?$", re.IGNORECASE | re.DOTALL)
9+
_APPROVAL_RE = re.compile(r"^(APPROVE|REJECT|REGENERATE)\s+(\d+)(?:\s+(.+))?$", re.IGNORECASE | re.DOTALL)
1010
_CLARIFICATION_RE = re.compile(
1111
r"^(ANSWER|CANCEL|CLARIFY)\s+(\d+)(?:\s+(.+))?$",
1212
re.IGNORECASE | re.DOTALL,
1313
)
1414

1515

16-
def parse_approval_command(text: str) -> tuple[str, int] | None:
16+
def parse_approval_command(text: str) -> tuple[str, int, str | None] | None:
1717
"""Parse an approval command from Telegram message text.
1818
19-
Returns ("APPROVE"|"REJECT"|"REGENERATE", run_id) or None if not a valid command.
19+
Returns ("APPROVE"|"REJECT"|"REGENERATE", run_id, reason_or_None) or None.
20+
Trailing text after the run_id is captured as an optional reason/feedback.
2021
"""
2122
m = _APPROVAL_RE.match(text.strip())
2223
if not m:
2324
return None
24-
return m.group(1).upper(), int(m.group(2))
25+
reason = m.group(3).strip() if m.group(3) else None
26+
return m.group(1).upper(), int(m.group(2)), reason
2527

2628

2729
def parse_clarification_command(text: str) -> tuple[str, int, str | None] | None:

app/webhooks.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ async def telegram_webhook(request: Request):
251251
logger.info("Telegram webhook: non-approval message ignored (%r)", text[:60])
252252
return {"ok": True}
253253

254-
action, run_id = cmd
254+
action, run_id, reason = cmd
255255

256256
# Sanity check run_id (must be positive and reasonable)
257257
if run_id <= 0 or run_id > 10_000_000:
@@ -323,6 +323,14 @@ async def telegram_webhook(request: Request):
323323
request_regeneration(run_id)
324324
n_events = record_planning_feedback(run_id)
325325
logger.info("Planning run %s REGENERATE_REQUESTED for %s — %d feedback events recorded", run_id, issue_key, n_events)
326+
327+
# Store user feedback as an epic-scoped manual note so Claude picks it up on the next run
328+
if reason:
329+
from app.database import add_manual_memory
330+
note = f"User feedback on previous breakdown: {reason}"
331+
add_manual_memory(scope_type="epic", scope_key=issue_key, content=note)
332+
logger.info("REGENERATE feedback stored as epic manual_note for %s: %r", issue_key, reason[:120])
333+
326334
new_run_id = create_planning_run(
327335
issue_key=issue_key,
328336
workflow_type=run["workflow_type"],

0 commit comments

Comments
 (0)