Skip to content

feat(automation): suggest starter rules#22

Merged
BjornMelin merged 5 commits intomainfrom
feat/automation-rule-suggestions
May 7, 2026
Merged

feat(automation): suggest starter rules#22
BjornMelin merged 5 commits intomainfrom
feat/automation-rule-suggestions

Conversation

@BjornMelin
Copy link
Copy Markdown
Owner

Summary

  • add automation rules suggest to generate disabled starter rules from recurring local mailbox evidence
  • keep suggestions read-only: no Gmail mutation and no .mailroom/automation.toml writes
  • document the suggested workflow before validate/rollout/run/apply

Safety

  • suggestions are limited to older INBOX list/bulk patterns
  • generated rules are disabled and archive-only
  • trash and unsubscribe execution remain out of scope

Verification

  • rtk test cargo fmt --check
  • rtk err cargo clippy --all-targets --all-features -- -D warnings
  • rtk test cargo test
  • rtk test cargo run -- paths --json
  • rtk test cargo run -- doctor --json
  • rtk git diff --check

Copilot AI review requested due to automatic review settings May 7, 2026 09:02
@qodo-code-review
Copy link
Copy Markdown

ⓘ You've reached your Qodo monthly free-tier limit. Reviews pause until next month — upgrade your plan to continue now, or link your paid account if you already have one.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 47b8e61650

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/automation/suggestions.rs Outdated
Comment thread src/automation/suggestions.rs
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: afdcc889-f6a8-450f-b045-98c53bd0d073

📥 Commits

Reviewing files that changed from the base of the PR and between 8fd8f65 and 04b3231.

📒 Files selected for processing (1)
  • src/automation/suggestions.rs
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: rust
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.rs: Use anyhow for command dispatch and top-level context in src/lib.rs; prefer typed thiserror errors in Gmail, workflow, and store layers in Rust
Keep error enums local to the layer that owns the failure semantics; do not introduce a repo-wide catch-all error enum unless it clearly reduces total code and cognitive load
For new CLI JSON contracts, normalize success and failure to one top-level shape: { "success": true, "data": ... } for success and { "success": false, "error": { "code": ..., "message": ..., "kind": ..., "operation": ..., "causes": [...] } } for failure
Keep error.code stable and operator-oriented, keep error.kind for deeper subsystem detail, and keep error.causes to an ordered message chain only in JSON error contracts
Do not include debug or backtrace payloads in the JSON error contract; use stderr and Rust backtrace env vars for deep diagnostics instead
Preserve existing human-facing error text unless it is misleading, ambiguous, or missing required operator action
When adding or changing CLI failures, keep exit codes in a small stable bucket set rather than creating one-off codes per variant
Keep blocking SQLite and filesystem work behind tokio::task::spawn_blocking; do not treat running spawn_blocking work as abortable
Add focused error-path tests for every new failure class; if CLI JSON or exit-code behavior changes, add contract tests for the new output and exit mapping in the same pass

Files:

  • src/automation/suggestions.rs
🔍 Remote MCP GitHub Copilot

Perfect! Now I have comprehensive context. Let me compile the additional context findings for you.

Summary of Additional Relevant Context

Based on my investigation using GitHub Copilot, gh_grep, and Firecrawl across multiple sources, here are the key facts and concrete information that would help in reviewing PR #22 effectively:

PR Scope & Safety Design

This PR adds a new read-only CLI command automation rules suggest that generates disabled starter rules from recurring local mailbox patterns. Notably:

  • No Gmail mutations or writes to .mailroom/automation.toml
  • Rules generated are disabled by default and archive-only
  • Suggestions limited to older INBOX patterns with list/bulk sender evidence
  • The command is idempotent and side-effect-free

Technology Stack Context

The project uses:

  • Rust 1.89 (MSRV in Cargo.toml)
  • clap 4.5.39 with derive for CLI parsing (allows declarative command-line parser via struct attributes)
  • serde 1.0.228 with derive for serialization/deserialization
  • toml 0.9.8 for TOML handling (correctly uses toml::to_string_pretty() for formatting)

Deterministic Rule ID Generation Strategy

The implementation uses:

  • Format: {prefix}-{slugified_value} (e.g., archive-list-digest-example-com)
  • Prefixes: archive-list for List-ID rules (priority 200), archive-sender for sender rules (priority 150)
  • Slug normalization: Converts to lowercase, replaces special chars (., _, -, @) with hyphens, limits to 48 characters, trims trailing dashes, defaults to "unknown" if empty
  • Test validation: <Digest.Example_Com>digest-example-com; !!!unknown
  • Collision handling: Appends numeric suffixes with TOML re-rendering to keep serialized output consistent

Suggestion Eligibility Criteria

Threads must meet ALL criteria:

  1. Have INBOX label (case-insensitive)
  2. Are older than configured threshold (default 14 days)
  3. Have EITHER:
    • List-Unsubscribe header AND List-ID header (high confidence: prefix archive-list)
    • From address AND Precedence header matching bulk/list/junk (medium confidence: prefix archive-sender)

Tests explicitly validate filtering of new threads, non-INBOX labels, and groups below minimum threshold.

Request Validation & Error Handling

Four parameters are validated with specific errors:

  • limit > 0InvalidSuggestionLimit
  • min_thread_count > 0InvalidSuggestionMinThreadCount
  • older_than_days > 0InvalidSuggestionOlderThanDays
  • sample_limit > 0InvalidSuggestionSampleLimit

All map to ErrorCode::ValidationFailed with kind: "automation.validation". Note: CodeRabbit reviews identified that while --limit 0 has end-to-end CLI contract tests, the other three validation errors currently lack matching contract tests in src/cli_output/tests.rs (they have unit tests but need e2e CLI validation).

Sample Retention Strategy

Uses bounded sample collection keyed by (internal_date_epoch_ms, thread_id, message_id) tuples. When the sample buffer reaches sample_limit, it replaces the oldest entry if the new sample is newer. This ensures:

  • Latest N samples are retained (deterministic across runs)
  • Test sample_limit_keeps_latest_samples_independent_of_input_order validates input-order independence

Output Rendering (Dual Mode)

  • JSON: Uses crate::cli_output::write_json_success() for serialized report
  • Plain text:
    • Header with operation=rules_suggest, account_id, rules_path, thread counts
    • TSV section with columns: rule_id, confidence, matched_thread_count, source, match_fields, sample_subjects (sanitized for tabs/control chars)
    • TOML snippets section (prefixed with # rule_id=...)
    • Warnings, next_steps, and command_plan sections

Related PR Dependencies

PR #11 established the foundational automation subsystem with rule-file driven planning, SQLite persistence for runs/candidates/events, and preview/apply/validate CLI surfaces. This PR extends that foundation in an additive manner without breaking changes.

Documentation Updates

Docs were updated to clarify that automation rules suggest is the only command that does NOT require an existing .mailroom/automation.toml file (though it still requires authenticated Gmail access and local sync evidence). The verification-and-hardening.md operations runbook now includes automation rules suggest in both Phase 5 (after copying template) and Commands recap sections.

Test Coverage Status

Comprehensive unit tests in suggestions.rs cover:

  • List-ID vs. sender rule suggestions
  • Filtering (new threads, non-INBOX, small groups)
  • Deterministic limiting and sampling
  • Slug normalization edge cases
  • TOML roundtrip validity
  • Rule ID collision deduplication

End-to-end CLI tests in cli_output/tests.rs cover error mapping for --limit 0 validation failure in both JSON and human modes (exit code 2).

🔇 Additional comments (7)
src/automation/suggestions.rs (7)

16-78: LGTM!

Clean enum design with const fn methods for compile-time evaluation. The separation of priority (for rule execution order) and rank (for suggestion sorting) is clear and the Ord derives on supporting types enable deterministic grouping.


80-148: LGTM!

The pipeline is well-structured: eligibility filtering → deterministic grouping → suggestion building → stable sorting with rule_id tiebreaker → truncation → deduplication. The warnings provide actionable guidance for empty cache and no-match scenarios.


191-221: LGTM - Past concern addressed.

The bounded-memory sample retention correctly keeps the most recent samples by replacing the oldest entry when at capacity. The deterministic sample_rank_key tuple (timestamp, thread_id, message_id) ensures consistent selection regardless of input order, addressing the prior review feedback.


223-254: LGTM!

The eligibility and key-extraction logic correctly prioritizes List-ID evidence over sender-based signals, and the evidence enum ensures distinct grouping for the same sender with different signals (List-Unsubscribe vs Precedence header).


256-352: LGTM!

The suggestion builder correctly constructs matchers based on evidence type, and the deduplication logic handles slug collisions gracefully by appending numeric suffixes and re-rendering TOML to keep the serialized output consistent with the updated rule ID.


385-432: LGTM!

The normalized_list_id function safely extracts identifiers using rfind with proper bounds checking. The slugify function handles edge cases well (empty strings, all-special-character inputs) with the "unknown" fallback.


434-854: LGTM!

Comprehensive test coverage including: List-ID vs sender rule generation, evidence alignment verification, slug collision deduplication, eligibility filtering, deterministic limiting, and importantly the sample_limit_keeps_latest_samples_independent_of_input_order test that validates the sample retention fix. The test helpers are well-factored with proper TempDir lifecycle management.


Walkthrough

This PR adds an "automation rules suggest" command that generates disabled starter automation rules from local cached thread candidates. It introduces request/report types and defaults, precedence/header helpers, a deterministic grouping/ranking pipeline that renders single-rule TOML, a suggest_rules service entry (with validation and error variants), CLI subcommand and handler wiring, JSON/plain output rendering, tests for validation/CLI contracts, and documentation updates in README and runbooks.

Possibly related PRs

  • BjornMelin/mailroom#2: The main PR builds directly on the bootstrap changes by extending the CLI and library entrypoints added in the foundation PR—adding an Automation rules "suggest" subcommand, new automation modules, and related handlers that augment the earlier CLI/dispatch scaffolding.
  • BjornMelin/mailroom#19: Both PRs touch the same CLI/handlers and cli_output layers—retrieved PR adds/reshapes handlers/automation.rs and cli_output modules, and the main PR further modifies handlers/automation.rs to add the "suggest" subcommand and extends cli_output/errors.rs with new AutomationServiceError variants, so they are related.
  • BjornMelin/mailroom#11: The main PR (automation rule suggestions) builds on and extends the same automation subsystem introduced in the retrieved PR (review-first automation/run/apply), adding a new suggest_rules API, suggestions module, CLI subcommand, output, and precedence-header helpers that integrate with the automation headers and store/CLI wiring added in the retrieved PR.
🚥 Pre-merge checks | ✅ 8 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.17% which is insufficient. The required threshold is 50.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (8 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(automation): suggest starter rules' follows conventional commits format with a semantic type (feat), scope (automation), and descriptive message. It accurately summarizes the main change: adding a new feature to suggest starter automation rules.
Description check ✅ Passed The description clearly relates to the changeset, providing a summary of the new automation rules suggest feature, safety constraints, and verification steps that align with the file changes across documentation and implementation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
React + Next.Js Best Practices ✅ Passed Check not applicable: This PR modifies a Rust backend project (mailroom), not a React/Next.js application. No React, TypeScript, Next.js, or frontend code is present in the changes.
Docstring & Research Standards ✅ Passed This PR is a Rust project (Cargo.toml, 126 .rs files). The custom check targets TypeScript/JavaScript code standards (Biome, TSDoc, Promises). Not applicable.
Web Interface Guidelines ✅ Passed PR is backend/CLI-only (Rust code, CLI commands, docs). Zero web interface changes. "Web Interface Guidelines" check is not applicable.
Google Python Style Compliance ✅ Passed The custom check enforces Google Python Style Guide compliance. This PR is a Rust project containing only .rs and .md files with zero Python files. The check is not applicable to non-Python code.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/automation-rule-suggestions
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/automation-rule-suggestions

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new read-only CLI workflow (automation rules suggest) that generates disabled “starter” automation rules based on recurring patterns found in the local mailbox cache, plus supporting output, error classification, tests, and documentation updates.

Changes:

  • Introduces automation rules suggest CLI subcommand wired through handlers and command metadata.
  • Implements suggestion generation (grouping, ranking, TOML snippet rendering) and a new JSON/plain-text report surface.
  • Extends CLI error classification + adds tests and docs covering the new workflow.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/lib.rs Adds operation metadata mapping for automation.rules.suggest.
src/handlers/automation.rs Wires the new CLI subcommand to automation::suggest_rules(...).
src/cli.rs Adds the automation rules suggest Clap subcommand and flags/defaults.
src/cli_output/tests.rs Adds error mapping + end-to-end CLI validation tests for suggest.
src/cli_output/errors.rs Classifies new suggestion validation errors (and TOML serialization) into stable error buckets.
src/automation/suggestions.rs Implements candidate grouping, suggestion ranking, rule/TOML snippet generation, and unit tests.
src/automation/service.rs Adds suggest_rules service entrypoint + request validation + new error variants.
src/automation/output.rs Adds plain-text + JSON output routing for AutomationRulesSuggestReport.
src/automation/model.rs Introduces request/report/suggestion model types + default constants for suggest.
src/automation/mod.rs Exposes the new suggest API + constants from the automation module.
README.md Documents the new automation rules suggest command in the quick command list and roadmap text.
docs/roadmap/v1-search-triage-draft-queue.md Notes the new “starter rule suggestions” capability in roadmap material.
docs/operations/verification-and-hardening.md Adds automation rules suggest to the operational runbook.
docs/operations/automation-rules-and-bulk-actions.md Documents usage, constraints, and workflow positioning of rule suggestions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/automation/suggestions.rs
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/operations/verification-and-hardening.md`:
- Line 162: The Commands recap omits the new subcommand "automation rules
suggest" causing inconsistency; update the recap block in
verification-and-hardening.md to include the exact command invocation "cargo run
-- automation rules suggest --json" in the list between "cargo run -- sync run
--profile deep-audit --json" and "cargo run -- automation rules validate --json"
so the docs reflect the current binary surface and Phase 5 commands.

In `@src/automation/service.rs`:
- Around line 680-696: The code validates four suggestion parameters in
validate_suggest_request but only the InvalidSuggestionLimit case has a CLI
contract test; add three new contract tests mirroring
automation_rules_suggest_zero_limit_fails_in_json_and_human_modes() to cover
InvalidSuggestionMinThreadCount (pass --min-thread-count 0),
InvalidSuggestionOlderThanDays (pass --older-than-days 0), and
InvalidSuggestionSampleLimit (pass --sample-limit 0). Implement each test in
src/cli_output/tests.rs following the existing pattern: run the CLI in both
human and JSON modes, assert non-zero exit code, and for JSON assert the error
JSON shape contains error.kind matching the corresponding AutomationServiceError
variant (InvalidSuggestionMinThreadCount / InvalidSuggestionOlderThanDays /
InvalidSuggestionSampleLimit). Ensure test names clearly reference the flag and
failure (e.g.,
automation_rules_suggest_zero_min_thread_count_fails_in_json_and_human_modes)
and re-use the same assertions for exit code and JSON structure as the
zero-limit test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c326a292-aaf4-472d-a3f3-b818262ba5ec

📥 Commits

Reviewing files that changed from the base of the PR and between 2bd7eea and 47b8e61.

📒 Files selected for processing (14)
  • README.md
  • docs/operations/automation-rules-and-bulk-actions.md
  • docs/operations/verification-and-hardening.md
  • docs/roadmap/v1-search-triage-draft-queue.md
  • src/automation/mod.rs
  • src/automation/model.rs
  • src/automation/output.rs
  • src/automation/service.rs
  • src/automation/suggestions.rs
  • src/cli.rs
  • src/cli_output/errors.rs
  • src/cli_output/tests.rs
  • src/handlers/automation.rs
  • src/lib.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,jsx,ts,tsx,md,txt,json}

📄 CodeRabbit inference engine (Custom checks)

Reject Unicode EM DASH (code point U+2014) in comments and string literals. Use -- instead. Hard FAIL if any newly introduced content includes U+2014.

Files:

  • docs/roadmap/v1-search-triage-draft-queue.md
  • docs/operations/verification-and-hardening.md
  • README.md
  • docs/operations/automation-rules-and-bulk-actions.md
docs/**

📄 CodeRabbit inference engine (AGENTS.md)

Keep docs concrete and aligned with the current binary surface

Files:

  • docs/roadmap/v1-search-triage-draft-queue.md
  • docs/operations/verification-and-hardening.md
  • docs/operations/automation-rules-and-bulk-actions.md
docs/{operations,workflows}/**

📄 CodeRabbit inference engine (AGENTS.md)

docs/{operations,workflows}/**: Put operator procedures in docs/operations/ or docs/workflows/
Update operator-facing docs in the same change whenever CLI error contracts, JSON envelopes, or exit-code behavior change

Files:

  • docs/operations/verification-and-hardening.md
  • docs/operations/automation-rules-and-bulk-actions.md
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.rs: Use anyhow for command dispatch and top-level context in src/lib.rs; prefer typed thiserror errors in Gmail, workflow, and store layers in Rust
Keep error enums local to the layer that owns the failure semantics; do not introduce a repo-wide catch-all error enum unless it clearly reduces total code and cognitive load
For new CLI JSON contracts, normalize success and failure to one top-level shape: { "success": true, "data": ... } for success and { "success": false, "error": { "code": ..., "message": ..., "kind": ..., "operation": ..., "causes": [...] } } for failure
Keep error.code stable and operator-oriented, keep error.kind for deeper subsystem detail, and keep error.causes to an ordered message chain only in JSON error contracts
Do not include debug or backtrace payloads in the JSON error contract; use stderr and Rust backtrace env vars for deep diagnostics instead
Preserve existing human-facing error text unless it is misleading, ambiguous, or missing required operator action
When adding or changing CLI failures, keep exit codes in a small stable bucket set rather than creating one-off codes per variant
Keep blocking SQLite and filesystem work behind tokio::task::spawn_blocking; do not treat running spawn_blocking work as abortable
Add focused error-path tests for every new failure class; if CLI JSON or exit-code behavior changes, add contract tests for the new output and exit mapping in the same pass

Files:

  • src/cli.rs
  • src/lib.rs
  • src/handlers/automation.rs
  • src/cli_output/errors.rs
  • src/cli_output/tests.rs
  • src/automation/output.rs
  • src/automation/model.rs
  • src/automation/service.rs
  • src/automation/mod.rs
  • src/automation/suggestions.rs
🪛 LanguageTool
docs/operations/automation-rules-and-bulk-actions.md

[style] ~37-~37: Consider using a more formal/concise alternative here.
Context: ...utomation.toml ``` Automation commands other than automation rules suggest require: - ...

(OTHER_THAN)

🔍 Remote MCP Firecrawl, GitHub Copilot

Perfect! Now I have comprehensive context from all sources. Let me compile my findings into a structured summary.

Summary of Additional Context Findings

Based on my investigation using Firecrawl, GitHub Copilot, and gh_grep, here are the relevant facts and context for reviewing this PR:

Automation Subsystem Foundation

PR #11 established the foundational automation subsystem with rule-file driven planning, SQLite persistence for runs/candidates/events, and preview/apply/validate CLI surfaces. This PR extends that foundation by adding rule suggestion capabilities based on mailbox evidence.

Technology Stack and Dependencies

The repository uses:

  • Rust 1.89 (MSRV specified in Cargo.toml)
  • clap 4.5.39 with derive feature for CLI parsing
  • serde 1.0.228 with derive feature for serialization
  • toml 0.9.8 for TOML file handling

TOML Serialization Best Practices

Serializing Rust structs into TOML format uses the serde crate alongside the toml library by deriving Serialize and Deserialize traits, then leveraging functions like toml::to_string_pretty to convert struct instances into TOML-formatted strings. The implementation correctly follows this pattern with #[derive(Serialize, Deserialize)] on AutomationRuleSet and uses toml::to_string_pretty() for formatting.

Deterministic Rule ID Generation

The PR implements deterministic rule IDs through:

  • Format: {prefix}-{slugified_value} (e.g., archive-list-digest-example-com)
  • Prefixes: archive-list for List-ID rules (priority 200), archive-sender for sender rules (priority 150)
  • Slug normalization: Converts to lowercase, replaces special characters (., _, -, @) with hyphens, limits to 48 characters, trims trailing dashes, defaults to "unknown" if empty
  • Sample test cases: Validates <Digest.Example_Com>digest-example-com and !!!unknown

This deterministic approach enables consistent rule generation across runs (tests verify this with limits_suggestions_and_samples_deterministically).

Suggestion Eligibility Criteria

Rules are suggested only for threads that:

  1. Have INBOX label (case-insensitive)
  2. Are older than configured days (default: 14 days, configurable via --older-than-days)
  3. Have either:
    • List-Unsubscribe header with List-ID header (high confidence), OR
    • From address with bulk/list/junk Precedence header (medium confidence)

Non-eligible threads are explicitly ignored with test coverage for new threads and non-INBOX labels.

Error Handling & Validation

Suggestion validation enforces:

  • limit > 0 (InvalidSuggestionLimit)
  • min_thread_count > 0 (InvalidSuggestionMinThreadCount)
  • older_than_days > 0 (InvalidSuggestionOlderThanDays)
  • sample_limit > 0 (InvalidSuggestionSampleLimit)

All validation errors map to ErrorCode::ValidationFailed with kind: "automation.validation". End-to-end test verifies both JSON and human-readable error output for --limit 0.

CLI Integration Pattern

clap with derive macros allows declarative command-line parser definition using struct fields with attributes for validation, default values, and help text. The PR correctly follows this pattern with:

  • Default values sourced from module constants
  • Help text describing parameters
  • Both --json and human output modes

Output Rendering Strategy

The suggestion report supports dual output modes:

  • JSON: Uses crate::cli_output::write_json_success() for serialized output
  • Plain text: TSV format for suggestions with sanitized fields, followed by TOML snippets, warnings, next steps, and command plan

Related PR Context

PR #11 modified files in the automation module including model.rs, service.rs, output.rs, mod.rs, and cli.rs for the core automation feature. This PR modifies the same files while being backward-compatible and additive-only.

Test Coverage

The PR includes comprehensive unit tests validating:

  • List-ID vs. sender rule suggestions
  • Filtering logic (ignores new/non-INBOX threads, groups below minimum threshold)
  • Deterministic limiting and sampling
  • Slug normalization edge cases
  • TOML roundtrip validity
  • End-to-end CLI integration with error handling
🔇 Additional comments (13)
src/lib.rs (1)

222-227: Suggest command metadata mapping looks correct.

The new metadata branch cleanly propagates json and the expected operation id for error/report envelopes.

docs/roadmap/v1-search-triage-draft-queue.md (1)

64-64: Roadmap update is clear and aligned with the new command behavior.

src/handlers/automation.rs (1)

18-37: Suggest command dispatch wiring is solid.

The request construction and output path are consistent with the rest of the automation handlers.

README.md (1)

76-76: README updates are aligned with the new suggest workflow and command surface.

Also applies to: 223-223

src/cli.rs (1)

356-373: The new automation rules suggest CLI shape looks good.

Flags/defaults are well-scoped and consistent with existing command design.

src/cli_output/errors.rs (1)

121-124: Error classification additions are correctly bucketed.

The new suggestion validation variants and TOML serialization mapping preserve the established error contract behavior.

Also applies to: 141-143

src/cli_output/tests.rs (1)

182-196: Great coverage for the new suggest validation failure path.

These tests appropriately lock JSON/human output behavior and exit-code mapping for the new command.

Also applies to: 198-269

src/automation/output.rs (1)

55-122: LGTM – AutomationRulesSuggestReport output rendering is correct.

TSV column order matches the header, sanitize() covers tab/control characters in all interpolated values, JSON path correctly delegates to write_json_success, and the write/print structure is consistent with every other report in this file.

docs/operations/automation-rules-and-bulk-actions.md (1)

37-68: LGTM – prerequisite differentiation and example commands are accurate.

The distinction that automation rules suggest does not require an existing rules file is correctly stated and consistent with the suggest_rules implementation in service.rs. The example flag invocations match the CLI defaults exported from model.rs.

src/automation/mod.rs (1)

5-17: LGTM – additive module surface expansion is clean.

src/automation/suggestions.rs (2)

73-140: LGTM – suggestion pipeline is correct and deterministic.

BTreeMap keying, the two-level sort (rank descending, then rule_id ascending as tiebreak), and sample_limit-bounded sample collection all produce stable output across runs. The test limits_suggestions_and_samples_deterministically explicitly validates this contract.


340-372: LGTM – slugify length bound is correct.

The break fires after pushing (so the pre-trim slug is at most RULE_ID_SOURCE_LIMIT = 48 characters), and trim_matches('-') handles any trailing dash left by the break. The two test vectors ("<Digest.Example_Com>""digest-example-com", "!!!""unknown") validate the main paths.

src/automation/model.rs (1)

33-104: LGTM – new request/response types are consistent with existing model conventions.

AutomationRulesSuggestRequest correctly omits Serialize/Deserialize (input-only), all report types derive Serialize, and DEFAULT_AUTOMATION_SUGGESTION_OLDER_THAN_DAYS is typed u32 to match the field it backs.

Comment thread docs/operations/verification-and-hardening.md
Comment thread src/automation/service.rs
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/automation/suggestions.rs`:
- Around line 184-186: The current push-then-truncate behavior
(group.samples.push(sample) while group.samples.len() < sample_limit) makes
selection order-dependent; change this to a bounded-memory selection that always
retains the most-recent samples regardless of input order — e.g., maintain a
min-heap (or sorted buffer) of at most request.sample_limit keyed by each
sample's timestamp and on each new sample compare/replace the min entry, or
implement reservoir sampling if true uniform sampling is required; update the
logic around group.samples, sample_limit and sample so that the later ranking
code (the block that ranks by latest sampled timestamp at the code referenced
around lines 323–333) operates on a correctly chosen representative set.
- Around line 369-375: In normalized_precedence, matching uses substring
contains which wrongly accepts values like "notbulk"; change to token-based
matching instead: after trimming and to_ascii_lowercase, split the header into
tokens on non-alphanumeric characters (e.g. header.split(|c: char|
!c.is_alphanumeric()).filter(|t| !t.is_empty())) and check if any token exactly
equals one of the expected values ["bulk","list","junk"]; return that exact
token as before. This keeps the function name normalized_precedence and its
input handling but replaces contains with exact-token equality to avoid false
positives.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8223448f-060e-4c2e-b23a-bd226c51c984

📥 Commits

Reviewing files that changed from the base of the PR and between 47b8e61 and e8db301.

📒 Files selected for processing (1)
  • src/automation/suggestions.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.rs: Use anyhow for command dispatch and top-level context in src/lib.rs; prefer typed thiserror errors in Gmail, workflow, and store layers in Rust
Keep error enums local to the layer that owns the failure semantics; do not introduce a repo-wide catch-all error enum unless it clearly reduces total code and cognitive load
For new CLI JSON contracts, normalize success and failure to one top-level shape: { "success": true, "data": ... } for success and { "success": false, "error": { "code": ..., "message": ..., "kind": ..., "operation": ..., "causes": [...] } } for failure
Keep error.code stable and operator-oriented, keep error.kind for deeper subsystem detail, and keep error.causes to an ordered message chain only in JSON error contracts
Do not include debug or backtrace payloads in the JSON error contract; use stderr and Rust backtrace env vars for deep diagnostics instead
Preserve existing human-facing error text unless it is misleading, ambiguous, or missing required operator action
When adding or changing CLI failures, keep exit codes in a small stable bucket set rather than creating one-off codes per variant
Keep blocking SQLite and filesystem work behind tokio::task::spawn_blocking; do not treat running spawn_blocking work as abortable
Add focused error-path tests for every new failure class; if CLI JSON or exit-code behavior changes, add contract tests for the new output and exit mapping in the same pass

Files:

  • src/automation/suggestions.rs
🔍 Remote MCP Firecrawl

Additional context gathered (concise, review-focused)

  • PR page (full conversation & bot reviews) confirms the feature: adds read-only CLI automation rules suggest that generates disabled starter rules from recurring local mailbox evidence; includes docs, models, service, output, CLI wiring, and tests. Safety constraints: suggestions limited to older INBOX list/bulk patterns; generated rules are disabled and archive-only; no Gmail mutations nor writes to .mailroom/automation.toml.

  • Verification steps listed by the author (formatting/lint/test checks and some runtime smoke commands) are present on the PR description.

  • Automated review bots (Copilot / CodeRabbit / coderabbitai / qodo-code-review) ran and produced:

    • Positive automated coverage notes and file-by-file review highlights (models, service, suggestions pipeline, output rendering, CLI wiring all noted as additive and plausible).
    • CodeRabbit flagged a doc inconsistency: recommendation to add cargo run -- automation rules suggest --json into the "Commands recap" in docs/operations/verification-and-hardening.md.
    • coderabbitai requested changes: specifically, add three missing CLI contract tests covering validation errors for --min-thread-count 0, --older-than-days 0, and --sample-limit 0 (only --limit 0 currently has a contract test). The bot included precise test naming and assertion guidance mirroring the existing zero-limit test in src/cli_output/tests.rs.
  • Files and areas the automated reviewers exercised/flagged for reviewers:

    • src/automation/suggestions.rs — deterministic grouping, slug rules, sampling, ranking; tests present and flagged as thorough.
    • src/automation/service.rs — validate_suggest_request enforces four validation errors; reviewers note only one of those currently has an end-to-end CLI contract test.
    • docs/operations/verification-and-hardening.md and docs/operations/automation-rules-and-bulk-actions.md — updated to include the suggest flow; reviewer recommends minor consistency patch to the commands recap.

Sources used

  • PR page and all review bot comments (scraped): #22 — Firecrawl scrape
🔇 Additional comments (1)
src/automation/suggestions.rs (1)

295-314: Rule-id collision handling looks solid.

The suffixing logic plus TOML re-render on mutated IDs is correct and keeps serialized output consistent with suggestion.rule.id.

Comment thread src/automation/suggestions.rs Outdated
Comment thread src/automation/suggestions.rs Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bf7ab2c7b8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/automation/suggestions.rs
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8fd8f650ed

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/automation/suggestions.rs Outdated
@BjornMelin BjornMelin self-assigned this May 7, 2026
@BjornMelin BjornMelin merged commit 3a3b0a0 into main May 7, 2026
3 checks passed
@BjornMelin BjornMelin deleted the feat/automation-rule-suggestions branch May 7, 2026 18:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants