feat(email): add 1m poll interval option#164
Merged
claude-puntlabs merged 1 commit intomainfrom Apr 18, 2026
Merged
Conversation
5-minute minimum frustrated e2e testing and is too slow for low-latency use cases. Adds "1m" to the valid poll intervals alongside the existing 5m/10m/15m/30m/1h/2h options. beadle-0sv
There was a problem hiding this comment.
Pull request overview
Adds 1m as an allowed inbox polling interval across the email poller, MCP tooling, tests, and user-facing documentation.
Changes:
- Extend
validPollIntervals/ validation logic to accept1mand map it totime.Minute - Update MCP tool descriptions + runtime error strings to include
1min the allowed set - Update docs (command docs/README/DESIGN/CHANGELOG) and tests to cover
1m
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| internal/email/config.go | Adds 1m to the allowed poll interval map and updates inline docs. |
| internal/email/config_test.go | Adds test coverage asserting 1m is valid and maps to 1 minute. |
| internal/email/poller.go | Updates config/poller error messages to include 1m. |
| internal/mcp/poll_tools.go | Updates MCP tool descriptions and validation error message to include 1m. |
| commands/inbox.md | Updates /inbox command docs to recognize and document 1m (incl. cron mapping). |
| README.md | Updates tool/command documentation to list 1m as a supported interval. |
| DESIGN.md | Updates design documentation to include 1m as a valid persisted poll interval. |
| CHANGELOG.md | Records the addition of the 1m poll interval option. |
| .ethos/missions.jsonl | Adds mission log entries related to implementing/validating the change. |
| .beads/issues.jsonl | Adds bead beadle-0sv entry for this work. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| {"id":"beadle-06m","title":"Persistent inbox polling: /inbox \u003cinterval\u003e survives session restart","description":"When the user runs '/inbox 5m' (or any interval), persist the polling interval to .claude/beadle.local.md. Add a SessionStart hook that reads this file and calls CronCreate to re-register the /inbox cron automatically. Result: inbox polling resumes on every new session without the user needing to re-run the command. The /inbox status command should show whether polling is active and whether a cron is currently registered.","status":"closed","priority":2,"issue_type":"feature","owner":"claude@punt-labs.com","created_at":"2026-03-28T13:25:14.164675-07:00","created_by":"\"jmf-pobox\"","updated_at":"2026-04-01T19:12:53.451902-07:00","closed_at":"2026-04-01T19:12:53.451902-07:00","close_reason":"Closed"} | ||
| {"id":"beadle-07h","title":"fix(inbox): /inbox auto-poll cron — nonexistent description field, session-only","description":"## Problem\n\ncommands/inbox.md #123 claims /inbox 10m creates an autonomous CronCreate loop that runs /inbox every N minutes. Two bugs make it unreliable:\n\n1. **Nonexistent CronCreate.description field.** The spec says to tag the cron job with description /inbox auto-poll and later find it via CronList with that description. The CronCreate tool schema has no description parameter — only cron, prompt, recurring, durable. CronList renders as \u003cid\u003e — \u003cinterval\u003e (recurring) [session-only]: \u003cprompt\u003e. The only durable identifier is the prompt. Cleanup logic is broken and will either fail to find the job or create duplicates on repeated /inbox NNm invocations.\n\n2. **Session-only cron.** CronCreate defaults to durable=false. When the Claude session exits, the cron dies. Next session starts with server-side polling configured but no Claude-side loop to process it.\n\n## Fix\n\nRewrite commands/inbox.md argument-routing section to remove the description field references, match auto-poll jobs by prompt suffix : /inbox, use durable: true so the cron survives session restarts, and tighten delete-before-create ordering so two consecutive /inbox 10m calls leave exactly one cron.\n\n## Verification\n\n1. Fresh session: /inbox 10m → CronList shows a recurring durable job with prompt /inbox.\n2. /inbox 10m twice in a row → still exactly one job.\n3. /inbox n → job is deleted.\n4. Exit session, restart → CronList still shows the job.","status":"closed","priority":1,"issue_type":"bug","owner":"claude@punt-labs.com","created_at":"2026-04-11T07:57:41.105374138-07:00","created_by":"J F","updated_at":"2026-04-11T08:48:30.208683585-07:00","closed_at":"2026-04-11T08:48:30.208683585-07:00","close_reason":"Merged in PR #128 (squash 0d394a7). /inbox NNm now creates a durable CronCreate job with prompt-based cleanup; autonomous loop verified end-to-end on the post-merge main branch."} | ||
| {"id":"beadle-0he","title":"list_messages: sender email not discoverable for unknown contacts","description":"Beadle has no way to surface a sender's email address for someone who is not yet in the address book. This blocks contact bootstrap on a fresh install.\n\nFacts:\n- list_messages shows the display name from the From header (e.g. 'J Freeman') but NOT the email address.\n- check_trust returns trust level + permission state but NOT the email address.\n- show_mime returns the MIME part tree (type, size) but NOT the headers.\n- read_message refuses with 'permission denied: no read permission for sender \u003cemail\u003e' — the email is in the ERROR message but not in any success response for an unpermissioned sender.\n\nDES-012 claims redacted listings 'let the owner discover unknown senders and decide whether to add them to contacts.' In practice discovery is blocked: the owner can see that an unknown sender exists but cannot learn enough about them (email address) to add them to the address book.\n\nVerified 2026-04-08 on a fresh Linux install during beadle-1aj troubleshooting. With 285 unread messages in INBOX from 7 different display names, list_messages showed the display names, check_trust and show_mime returned nothing useful, and read_message on each returned the same error. The only way to learn the actual sender address was to parse the permission-denied error message.\n\nProposed fixes (any one would close the bead):\n\n1. list_messages includes the email address next to the display name in the FROM column. Format: 'J Freeman \u003cnotifications@github.com\u003e' or two columns. Today it only shows 'J Freeman'.\n\n2. A dedicated 'unknown_senders' MCP tool that returns a grouped list of (email, display_name_samples, message_count, first_seen, last_seen) for all senders not in contacts. Single tool call, everything needed to decide what permissions to grant.\n\n3. Loosen the permission gate on the From header specifically: list_messages always shows both name AND email for unpermissioned senders, while still redacting subject/body. The From header is not sensitive content — it is the identifier the owner needs to add a contact.\n\nOption 3 is the minimum viable fix and matches the intent of DES-012. Option 2 is the ergonomic fix. Option 1 is the middle ground.\n\nRelated: the display name being misleading is filed as a separate bead (notifications@github.com shows up as 'J Freeman', 'Copilot', etc. based on who triggered the event).","status":"closed","priority":2,"issue_type":"bug","owner":"claude@punt-labs.com","created_at":"2026-04-08T10:48:04.173973766-07:00","created_by":"J F","updated_at":"2026-04-08T16:47:37.888482467-07:00","closed_at":"2026-04-08T16:47:37.888482467-07:00","close_reason":"Merged in PR #102 (squash 53cc01a)","labels":["bug","contacts","ux"]} | ||
| {"id":"beadle-0sv","title":"Add 1m poll interval option","status":"in_progress","priority":2,"issue_type":"task","owner":"claude@punt-labs.com","created_at":"2026-04-18T05:34:23.144683375-07:00","created_by":"J F","updated_at":"2026-04-18T05:34:26.381483-07:00"} |
There was a problem hiding this comment.
The new bead entry beadle-0sv is left with status":"in_progress", but this PR implements the feature and even attributes it in CHANGELOG. Consider marking it closed (and adding closed_at/close_reason fields consistent with other entries) to keep bead tracking in sync with the shipped change.
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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
1m(1 minute) as a valid poll interval alongside the existing 5m/10m/15m/30m/1h/2h optionsvalidPollIntervalsmap, all error messages, MCP tool descriptions, command docs, README, DESIGN.md, CHANGELOGTest plan
go vet ./...cleango test -race -count=1 ./internal/email/ ./internal/mcp/— all passValidPollInterval("1m")returns truePollDurationreturns 1 minute for "1m"Bead: beadle-0sv
Note
Low Risk
Low risk: adds a new allowed value to polling interval validation and updates related messaging/docs, with small unit test adjustments.
Overview
Adds
1mas an accepted inbox polling interval end-to-end (config parsing/validation, poller startup + interval errors, and theset_poll_intervalMCP tool validation).Updates
/inboxcommand docs, README, DESIGN, and CHANGELOG to list the new interval, and extends config unit tests to cover the1mcase.Reviewed by Cursor Bugbot for commit afe0e85. Bugbot is set up for automated code reviews on this repo. Configure here.