Skip to content

fix(plugins): isolate cmdRun results to requested id; gate gmail cursor on dryRun - #2435

Open
Tanmay9223 wants to merge 1 commit into
santifer:mainfrom
Tanmay9223:fix-2354-plugins-run-id-filter-and-gmail-dryrun
Open

fix(plugins): isolate cmdRun results to requested id; gate gmail cursor on dryRun#2435
Tanmay9223 wants to merge 1 commit into
santifer:mainfrom
Tanmay9223:fix-2354-plugins-run-id-filter-and-gmail-dryrun

Conversation

@Tanmay9223

@Tanmay9223 Tanmay9223 commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes two bugs in the plugin engine reported in #2354:

  1. Misattribution (cmdRun): runHook() runs every enabled plugin of the requested hook kind and returns [{id, ok, result}] for all of them. cmdRun never filtered by the requested plugin id before flattening results, so running node plugins.mjs run linkedin-alerts --dry-run with gmail also enabled silently merged gmail's results in and reported them all under the linkedin-alerts name.

  2. Dry-run not dry (gmail): plugins/gmail/index.mjs called saveProcessedIds() unconditionally — ignoring ctx.dryRun. A --dry-run permanently marked Gmail messages as processed even though nothing was written to the pipeline. Any message seen during a dry-run was hidden from all future real runs. plugins/notion/index.mjs already handles this correctly with if (ctx?.dryRun) { ... continue; }.

Related issue

Fixes #2354

Type of change

  • Bug fix

Checklist

  • I have read CONTRIBUTING.md
  • My PR does not include personal data (CV, email, real names, scan results, or pipeline data)
  • I ran node test-all.mjs and all tests pass (2783 passed, 1 pre-existing failure on main unchanged)
  • My changes respect the Data Contract (no modifications to user-layer files)
  • My changes align with the project roadmap

Summary by CodeRabbit

  • Bug Fixes

    • Plugin searches and ingestion now return only results for the requested plugin.
    • Gmail dry runs no longer save processed message IDs, preventing test or preview runs from changing ingestion state.
  • Tests

    • Added regression coverage for plugin result isolation and Gmail dry-run behavior.

…or on dryRun (closes santifer#2354)

runHook() runs every enabled plugin of the requested hook kind and
returns [{id, ok, result}] for all of them. cmdRun never filtered by
plugin id, so running `node plugins.mjs run linkedin-alerts` with
gmail also enabled silently merged gmail's results in and reported
them under the linkedin-alerts name.

Separately, plugins/gmail/index.mjs called saveProcessedIds()
unconditionally, so `--dry-run` permanently marked Gmail messages as
processed even though nothing was written to the pipeline. This is
the same pattern as ctx.dryRun already used by plugins/notion.

- plugins.mjs: filter results to r.id === id before flatMap
- plugins/gmail/index.mjs: gate saveProcessedIds behind !ctx.dryRun
- test-all.mjs: two regression tests for both invariants (santifer#2354)
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The plugin engine now limits collected hook results to the requested plugin. Gmail now avoids saving processed message IDs during dry runs. Regression tests cover both behaviors.

Changes

Plugin execution safeguards

Layer / File(s) Summary
Requested-plugin result isolation
plugins.mjs, test-all.mjs
cmdRun accepts successful array results only when their id matches the requested plugin. A regression test verifies that results from other plugins are excluded.
Gmail dry-run state protection
plugins/gmail/index.mjs, test-all.mjs
Gmail calls saveProcessedIds only when ctx.dryRun is false. A source-level test verifies the guard.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested labels: 🔴 core-architecture

Suggested reviewers: santifer, rubicon

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes both fixes: plugin result isolation and Gmail cursor protection during dry runs.
Linked Issues check ✅ Passed The changes implement both requirements in issue #2354 and add regression tests for result isolation and dry-run cursor persistence.
Out of Scope Changes check ✅ Passed All changes directly support issue #2354 and its regression coverage; no unrelated code changes are described.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
plugins.mjs (1)

154-158: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Apply the plugin-ID filter to every hook path.

Line 158 filters only ingest and search results. The export and notify branches still iterate all runHook results, so node plugins.mjs run <id> export or notify can report results from other enabled plugins. Filter results before each branch consumes them, or centralize the filter after runHook.

🤖 Prompt for 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.

In `@plugins.mjs` around lines 154 - 158, Apply the requested plugin-ID filtering
consistently across all hook branches in the run command: ensure results
returned by runHook are restricted to entries whose id matches the requested id,
including export and notify, before those branches consume them. Reuse the
existing filtering logic near the ingest/search handling rather than leaving
those paths to iterate every enabled plugin’s results.
🤖 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 `@test-all.mjs`:
- Around line 10989-11005: Update test-all.mjs lines 10989-11005 so the
regression test invokes production cmdRun or a shared production helper with two
plugin results and verifies only the requested plugin’s result is returned; do
not recreate the filtering logic in the test. Update test-all.mjs lines
11008-11020 to invoke Gmail ingest with dryRun: true and assert that
data/gmail-state.json remains unchanged, exercising the production path rather
than inspecting or reimplementing source behavior.

---

Outside diff comments:
In `@plugins.mjs`:
- Around line 154-158: Apply the requested plugin-ID filtering consistently
across all hook branches in the run command: ensure results returned by runHook
are restricted to entries whose id matches the requested id, including export
and notify, before those branches consume them. Reuse the existing filtering
logic near the ingest/search handling rather than leaving those paths to iterate
every enabled plugin’s results.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 81ce953d-c259-4231-918a-7c4894bacdd6

📥 Commits

Reviewing files that changed from the base of the PR and between fe4561b and 21b2130.

📒 Files selected for processing (3)
  • plugins.mjs
  • plugins/gmail/index.mjs
  • test-all.mjs

Comment thread test-all.mjs
Comment on lines +10989 to +11005
// #2354 — cmdRun must only collect results from the requested plugin id.
// If a second plugin of the same hook kind is enabled it must NOT bleed in.
{
const { runHook: rh } = await import(pathToFileURL(join(ROOT, 'plugins/_engine.mjs')).href);
// Simulate runHook returning two plugins' results (as the real function would when two are enabled).
const fakeResults = [
{ id: 'linkedin-alerts', ok: true, result: [{ title: 'Correct', url: 'https://a.test/1' }] },
{ id: 'gmail', ok: true, result: [{ title: 'Wrong', url: 'https://b.test/2' }] },
];
// The fix: filter(r => r.id === id && ...) — only linkedin-alerts' result must survive.
const id = 'linkedin-alerts';
const found = fakeResults.filter(r => r.id === id && r.ok && Array.isArray(r.result)).flatMap(r => r.result);
if (found.length === 1 && found[0].url === 'https://a.test/1') {
pass('cmdRun result isolation: only the requested plugin id\'s results are collected (#2354)');
} else {
fail(`cmdRun result isolation broken: got ${JSON.stringify(found)}`);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make both regression tests execute the production behavior.

Both tests reimplement or inspect source text instead of exercising the affected code paths.

  • test-all.mjs#L10989-L11005: call cmdRun or a shared production helper with two plugin results.
  • test-all.mjs#L11008-L11020: invoke Gmail ingest with dryRun: true and assert that data/gmail-state.json is unchanged.
📍 Affects 1 file
  • test-all.mjs#L10989-L11005 (this comment)
  • test-all.mjs#L11008-L11020
🤖 Prompt for 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.

In `@test-all.mjs` around lines 10989 - 11005, Update test-all.mjs lines
10989-11005 so the regression test invokes production cmdRun or a shared
production helper with two plugin results and verifies only the requested
plugin’s result is returned; do not recreate the filtering logic in the test.
Update test-all.mjs lines 11008-11020 to invoke Gmail ingest with dryRun: true
and assert that data/gmail-state.json remains unchanged, exercising the
production path rather than inspecting or reimplementing source behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

plugins.mjs run <id> aggregates every enabled ingest plugin under the named one; gmail plugin persists its cursor even under --dry-run

1 participant