This repository was archived by the owner on May 26, 2026. It is now read-only.
KR-INTENT-EMAIL-TO-SEA-TICKET — save ideas to Sea_Ticket from inbox#176
Merged
rafe-walker merged 1 commit intoMay 24, 2026
Merged
Conversation
…ickets Operator R3 Q8a: "I email Kora something I found that I want to save as an idea on a Sea_Ticket." First real product-value use of the inbound-email surface (parsing intact since KR-FEAT-EMAIL-INBOUND-IMAP ST2; auto-reply branch removed in #173 / Lock R3-8 (a)). Flow ---- Joshua emails Kora → identity filter (already gates to KORA_EMAIL_JOSHUA_ADDRESS) → regex intent recognition → sea__create_ticket via in-process IsoKron MCP client → Slack confirmation DM to KORA_SLACK_JOSHUA_USER_ID → audit row on every evaluated email. New module ---------- `kora_cli/intent/email_to_sea_ticket.py`: * `recognize_intent(subject, body, sender) -> EmailIntent` — pure regex matcher. No LLM call. * `process_email_intent(...)` — orchestrator called from the email handler. Always fail-soft (returns action=failed on unexpected exception; never propagates). * `write_sea_ticket_from_intent(intent)` — direct invocation of `sea__create_ticket` via `get_last_active_provider()._connection.get_mcp_client()` (sidesteps the MCP-tool Caller-auth surface — this is in-process operator-driven, not external-caller-gated). * `confirm_via_dm(...)` + `dm_failure(...)` — fail-soft Slack confirmation / failure DMs. Recognition patterns (regex, case-insensitive) --------------------------------------------- High confidence: * `subject_idea_prefix` / `subject_note_prefix` / `subject_todo_prefix` — subject starts with `Idea:` / `Note:` / `TODO:` * `explicit_save_bracket` — `[SAVE]` literal in subject or body * `explicit_save_phrase` — `save this` / `save to sea_ticket` / `save as idea` / `add to sea` / `add idea` in body or subject Medium confidence: * `fwd_with_url` — `Fwd:` / `Fw:` subject OR `----- Forwarded message` body marker AND a URL in body * `fwd_without_url` — same forward shape without a URL Unrecognized → audit logged_only; no MCP call, no DM. Configuration ------------- * `KORA_EMAIL_INTENT_MIN_CONFIDENCE` — `high` (default) or `medium`. Floor for write-through. * `KORA_EMAIL_INTENT_DRY_RUN` — `true` skips MCP write + DM. * `KORA_EMAIL_INTENT_HOURLY_CAP` — int (default 10). Sliding- window per-hour cap. 11th match in any 1-hour window is audited as `cap_exceeded` + DMs operator. Zero disables. Audit ----- New seam `intent.email_to_sea_ticket` (added to SeamName Literal next to `probe.wake_requested`). One entry per evaluated email with `action` ∈ {created, dry_run, logged_only, cap_exceeded, failed}; `caller_session_id` is `email:{message_id}` so future-bucket cockpit panels can xref to the inbound JSONL. Handler wire-up --------------- `EmailInboundHandler._handle_event_inner` now calls `process_email_intent(...)` after `_emit_received_event`. Wrapped in try/except so any intent-side failure leaves HANDLED_RECEIVED intact (belt-and-suspenders — the orchestrator itself is also fail-soft). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
rafe-walker
added a commit
that referenced
this pull request
May 24, 2026
…n + revert (#177) Operator now edits phrasebook from cockpit. PUT + revert + backups list endpoints (BE) + edit-mode UI in PhrasebookPage (FE). Validation: regex compiles, snapshot-path placeholders, catastrophic-backtracking check, no duplicate (pattern, category). Backup-on-write with env-tunable rotation. All 3 spec §4 STOP-ASK candidates resolved inline (static-list snapshot-path validation, audit seam Literal extension, free-form category v1). New audit seam phrasebook.updated with actor field deliberately shaped for future promotion-loop (kora_proposal_approved/pending). Sets UX foundation KR-FE-PROMOTION-REVIEW-PANEL will reuse. Rebased onto current feature/phase2-upgrades (post #173 / #176) to resolve SeamName Literal adjacent-addition conflict — both intent.email_to_sea_ticket (#176) and phrasebook.updated (#177) preserved in sequence. 76/76 phrasebook tests + tsc + vite build clean.
This was referenced May 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Operator R3 Q8a: "I email Kora something I found that I want to save as an idea on a Sea_Ticket." First real product-value use of Kora's inbound-email surface — flips the email path from receive-and-discard (post #173) to receive-recognize-write-confirm.
Joshua emails Kora → identity filter (already gates to
KORA_EMAIL_JOSHUA_ADDRESSper the existing handler) → regex intent recognition →sea__create_ticketvia in-process IsoKron MCP client → Slack confirmation DM toKORA_SLACK_JOSHUA_USER_ID→ audit row on every evaluated email.Bucket spec:
17_cc_bucket_prompts/KR-INTENT-EMAIL-TO-SEA-TICKET_save_idea_from_inbox.md.K-DG verdict: no STOP-ASK
The Sea_Ticket write surface IS accessible in-process.
mcp_tools.py:_execute_create_sea_ticket(Tool 8kora__create_sea_ticket) usesget_last_active_provider()._connection.get_mcp_client().invoke("sea__create_ticket", payload). The intent module calls that same path directly (sidesteps the MCP-ToolCallerauth shape — this is operator-driven from the handler, not external-caller-gated).Regex patterns shipped
All case-insensitive. Highest-confidence match wins; first non-matching tier falls through.
subject_idea_prefixIdea:(then content)subject_note_prefixNote:subject_todo_prefixTODO:explicit_save_bracket[SAVE]literal anywhere in subject or bodyexplicit_save_phrasesave this/save to sea_ticket/save as idea/add to sea/add ideain body or subjectfwd_with_urlFwd:/Fw:subject ORForwarded messagebody marker ANDhttp:///https:///www.URL in bodyfwd_without_urlunrecognizedSample DM confirmation text
Success — sent to
KORA_SLACK_JOSHUA_USER_IDafter a successful write:Cap-exceeded / failure variant:
Sample audit entry
{ "emitted_at": "2026-05-23T19:42:11.512000+00:00", "seam": "intent.email_to_sea_ticket", "details": { "action": "created", "pattern_matched": "subject_idea_prefix", "confidence": "high", "subject": "Idea: try the new Haiku model", "ticket_id": "STK-42", "tags": ["email", "idea"] }, "caller_session_id": "email:<m-1@example.com>", "source": "email" }action∈{created, dry_run, logged_only, cap_exceeded, failed}. Thecaller_session_idis wire-compatible with the engine-sideemail:{message_id}derivation so a future xref bucket can join inbound JSONL ↔ audit ↔ Sea_Ticket history by one key.Env vars added
KORA_EMAIL_INTENT_MIN_CONFIDENCEhighmediumto also act on forwards.KORA_EMAIL_INTENT_DRY_RUNKORA_EMAIL_INTENT_HOURLY_CAP100disables.KORA_SLACK_JOSHUA_USER_IDKORA_EMAIL_JOSHUA_ADDRESSEmailInboundHandlerfilter 5; this module trusts the upstream check).Files
kora_cli/intent/__init__.py+kora_cli/intent/email_to_sea_ticket.py(~600 lines)tests/kora_cli/intent/__init__.py+tests/kora_cli/intent/test_email_to_sea_ticket.py(34 tests)kora_cli/audit/jsonl_sink.py— addedintent.email_to_sea_tickettoSeamNameLiteralkora_cli/handlers/email_inbound_handler.py— callprocess_email_intentafter_emit_received_event(wrapped in try/except so the intent path can never disturb HANDLED_RECEIVED)Test plan
ruff checkclean on all changed filesCC#2 follow-on recommendation:
KR-FE-EMAIL-INTENT-LOG-PANELThe audit-JSONL seam is forensically rich enough today to drive a small cockpit panel without further backend work. Recommended cut:
/api/audit-events?seam=intent.email_to_sea_ticket— already serviceable by the existingread_audit_entries(seam=...)reader (PR feat(kora): KR-PROBE-AUDIT-AND-CONVERT — cheap-cron + wake-event + fix-envelope per probe #163 era). No new backend code needed.subject,pattern_matched,action, and a deep-link to the substrate Sea_Ticket whenticket_idis present.actionchip filter (created/dry_run/logged_only/cap_exceeded/failed) is the operator's primary lens —logged_onlyis the promotion-loop training-data view;failedis the triage view.createdactions, hosted next to therecent_error_count_5minband in HealthHero post-KR-SNAPSHOT-DAEMON-HEALTH — daemon's own listeners/uptime/errors (schema v4) #170.The panel can ship before any cockpit-side substrate read — the audit JSONL already carries the operator's full mental model (subject + pattern + outcome).
🤖 Generated with Claude Code