Skip to content

feat: Add Task Filtering and Tagging Capabilities#242

Closed
thalesraymond wants to merge 1 commit intomainfrom
add-task-filtering-15239283241663882048
Closed

feat: Add Task Filtering and Tagging Capabilities#242
thalesraymond wants to merge 1 commit intomainfrom
add-task-filtering-15239283241663882048

Conversation

@thalesraymond
Copy link
Copy Markdown
Owner

Implement add-task-filtering specification from OpenSpec, allowing users to selectively execute tasks based on tags or names with dependency resolution capabilities.


PR created automatically by Jules for task 15239283241663882048 started by @thalesraymond

- Added `tags?: string[]` property to `TaskStep`.
- Introduced `TaskFilterConfig` interface to configure filters and dependencies.
- Created `src/utils/TaskFilter.ts` containing the `filterTasks` logic.
- Updated `TaskRunnerExecutionConfig` with the new optional `filter` property.
- Integrated filtering in `TaskRunner.execute` to process steps prior to graph execution.
- Added comprehensive unit tests and E2E integration tests to verify functionality.
- Marked all spec tasks as complete and archived OpenSpec change `add-task-filtering`.

Co-authored-by: thalesraymond <32554150+thalesraymond@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 3, 2026

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 3, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request implements task filtering within the TaskRunner, allowing users to selectively execute tasks based on names or tags. Key changes include the addition of a TaskFilterConfig interface, the inclusion of optional tags in TaskStep, and the implementation of a filterTasks utility that supports recursive dependency resolution. Review feedback suggests removing redundant JSDoc blocks in TaskRunnerExecutionConfig.ts and improving type safety in TaskFilter.ts by replacing non-null assertions with optional chaining and explicit null checks.

Comment on lines +1 to +4
/**
* Configuration options for TaskRunner execution.
*/
import { TaskFilterConfig } from "./contracts/TaskFilterConfig.js";
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.

medium

The JSDoc block at the top of the file is redundant as it is repeated immediately before the interface declaration. Removing it keeps the code concise and avoids duplication.

import { TaskFilterConfig } from "./contracts/TaskFilterConfig.js";

Comment on lines +17 to +44
const filteredSteps = steps.filter((step) => {
// 1. Check exclusions first (highest priority)
if (
config.excludeNames?.includes(step.name) ||
(step.tags && config.excludeTags?.some((tag) => step.tags!.includes(tag)))
) {
return false;
}

// 2. Check inclusions (if both are provided, satisfying either is enough or requires both? Usually OR semantics for inclusions)
// Actually, usually if include is present, it MUST match one of the inclusions.
// Let's implement OR logic: if included by name OR included by tag.
const hasIncludeNames =
config.includeNames && config.includeNames.length > 0;
const hasIncludeTags = config.includeTags && config.includeTags.length > 0;

if (!hasIncludeNames && !hasIncludeTags) {
return true; // No inclusion filters, so keep it if it passed exclusion
}

const includedByName = hasIncludeNames && config.includeNames!.includes(step.name);
const includedByTag =
hasIncludeTags &&
step.tags &&
config.includeTags!.some((tag) => step.tags!.includes(tag));

return includedByName || includedByTag;
});
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.

medium

The filtering logic uses several non-null assertion operators (!), which violates the project's general rules. Additionally, the logic can be simplified and made more robust by using optional chaining and nullish coalescing to handle potentially undefined properties like tags or includeNames.

  const filteredSteps = steps.filter((step) => {
    // 1. Check exclusions first (highest priority)
    const isExcludedByName = config.excludeNames?.includes(step.name) ?? false;
    const isExcludedByTag = step.tags?.some((tag) => config.excludeTags?.includes(tag)) ?? false;

    if (isExcludedByName || isExcludedByTag) {
      return false;
    }

    // 2. Check inclusions
    const hasIncludeNames = (config.includeNames?.length ?? 0) > 0;
    const hasIncludeTags = (config.includeTags?.length ?? 0) > 0;

    if (!hasIncludeNames && !hasIncludeTags) {
      return true;
    }

    const includedByName = config.includeNames?.includes(step.name) ?? false;
    const includedByTag = step.tags?.some((tag) => config.includeTags?.includes(tag)) ?? false;

    return includedByName || includedByTag;
  });
References
  1. In TypeScript, avoid using the non-null assertion operator (!). Instead, use explicit checks or safe fallbacks like the nullish coalescing operator (??) to handle potentially null or undefined values.

const queue = [...filteredSteps];

while (queue.length > 0) {
const current = queue.shift()!;
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.

medium

Avoid using the non-null assertion operator (!). Although the queue length is checked, it is better practice to handle the potential undefined return from shift() explicitly to adhere to the project's safety rules.

    const current = queue.shift();
    if (!current) continue;
References
  1. In TypeScript, avoid using the non-null assertion operator (!). Instead, use explicit checks or safe fallbacks like the nullish coalescing operator (??) to handle potentially null or undefined values.

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.

1 participant