Skip to content

feat(#6339): replace jira-poll HarnessRouter with CEL trigger evaluation - #6340

Merged
ralphbean merged 2 commits into
mainfrom
agent/6339-cel-trigger-jira-poll
Aug 19, 2026
Merged

feat(#6339): replace jira-poll HarnessRouter with CEL trigger evaluation#6340
ralphbean merged 2 commits into
mainfrom
agent/6339-cel-trigger-jira-poll

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Summary

Replace the hardcoded HarnessRouter in the Jira poll input driver with CEL trigger evaluation from the harness dispatch system (ADR 0061). The hardcoded router pre-determined stages and required per-stage workflow files, which broke repos using the default workflow_call shim mode from fullsend github setup. This change routes Jira poll events through harness CEL trigger: expressions — the same evaluation path as webhook-driven dispatch.

Changes

  • Define EventMatcher interface in the jirapoll package replacing dispatch.EventRouter
  • Convert JiraEventnormevent.Event (typed enums) for CEL evaluation instead of dispatch.NormalizedEvent (plain strings)
  • Add celMatcher in the CLI layer that loads harness CEL triggers once per cycle and evaluates each event via harnessdispatch.MatchHarnesses
  • Output DispatchRecord (execution refs) instead of poll.Dispatch (stage-based), compatible with the workflow_call shim
  • Update behaviour tests with a routerMatcher adapter for backward compat
  • HarnessRouter and buildRouter remain for the GitLab poll path

Testing

  • All jirapoll unit tests pass (87.4% coverage)
  • Poll-related CLI tests pass
  • Harness dispatch tests pass
  • go vet clean on all affected packages
  • Secret scan clean

Note: pre-commit could not run in the sandbox due to network restrictions (git fetch blocked). The post-script runs it authoritatively.


Closes #6339

Post-script verification

  • Branch is not main/master (agent/6339-cel-trigger-jira-poll)
  • Secret scan passed (gitleaks — 7917eb6306949a94da6ecc5dc7105d70a2f70206..HEAD)
  • PR body secret scan passed (gitleaks — no-git)

Replace the hardcoded HarnessRouter in the Jira poll input driver with
CEL trigger evaluation from the harness dispatch system (ADR 0061).

The HarnessRouter pre-determined stages (triage, code, review, etc.)
and the downstream shell glue required per-stage workflow files with
`# fullsend-stage: <stage>` markers. This broke repos using the
workflow_call shim mode (the default scaffold from `fullsend github
setup`), which has a single fullsend.yaml → reusable-dispatch.yml
with no per-stage files.

Changes:
- Define EventMatcher interface in jirapoll package with
  Match(ctx, *normevent.Event) → []DispatchRecord signature
- Replace dispatch.EventRouter dependency with EventMatcher in Poller
- Convert JiraEvent → normevent.Event (typed enums) instead of
  dispatch.NormalizedEvent (plain strings) for CEL evaluation
- Add celMatcher in CLI layer that loads harness CEL triggers once
  per cycle and evaluates events via harnessdispatch.MatchHarnesses
- Output DispatchRecord (execution refs) instead of poll.Dispatch
  (stage-based), compatible with the workflow_call shim
- Add routerMatcher adapter in behaviour tests for backward compat

The HarnessRouter and buildRouter remain for the GitLab poll path.

Closes #6339
@fullsend-ai-coder
fullsend-ai-coder Bot requested a review from a team as a code owner August 18, 2026 19:47
@fullsend-ai-coder fullsend-ai-coder Bot added the ready-for-review Triggers review agent dispatch label Aug 18, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 18, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:50 PM UTC · Completed 8:19 PM UTC

Commit: 80a366f · View workflow run →

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 18, 2026

Copy link
Copy Markdown

Review

Findings

Medium

  • [test-integrity] pkg/behaviourtest/steps/jirapoll.go:73 — The routerMatcher adapter marshals a dispatch.NormalizedEvent as the EventPayload (containing entity.key), but the production celMatcher calls harnessdispatch.ProjectExecutionRefbuildEventPayload, which produces {"issue":{"number":N,"html_url":"..."}} with no top-level entity key. dispatchMatchesIssue (line 285) parses entity.key from EventPayload — this works against the test adapter's format but would silently return false for production-format payloads. The behaviour tests exercise per-issue assertions against a payload schema that production never produces.

Low

  • [architectural-divergence] internal/cli/poll.go:654 — GitLab poll path still uses hardcoded HarnessRouter while Jira poll now uses CEL triggers. The code comment at line 653 acknowledges this as intentional interim state. This is documented tech debt, not a defect introduced by this PR.
  • [test-integrity] internal/jirapoll/poller_test.go:359TestRunHappyPath_CommentWithSlashCommand had payload-integrity assertions removed. The stubMatcher does not produce an EventPayload, so the test no longer verifies the event-to-payload pipeline at the poller level.
  • [test-integrity] pkg/behaviourtest/steps/jirapoll.go:192thenDispatchActorRole no longer filters dispatches by issueKey before checking actor role. In a multi-issue test scenario, this could match a dispatch from the wrong issue.

Labels: PR modifies dispatch routing code (internal/jirapoll, internal/cli/poll.go) and user-facing Jira integration documentation

Previous run

Review

Findings

High

  • [stale-doc] docs/guides/user/jira-integration.md:127 — Multiple sections reference removed/renamed dispatch record fields that no longer exist in the new DispatchRecord schema. The shell script dispatch loop (lines 127–130) extracts .stage, .resource_key, and .iid fields; the example jq command (line 245) references stage and resource_key; the format tables (lines 252–277) document stage, resource_key, iid, and event_payload_b64 — all of which were removed or renamed. New field names: stageagent, resource_key removed, iid removed (status_number added), event_payload_b64event_payload (now plain JSON, not base64).
    Remediation: Update all references in jira-integration.md to use the new DispatchRecord field names (agent, event_type, event_payload, trigger_source, source_repo, status_repo, status_number).

  • [logic-error] pkg/behaviourtest/steps/jirapoll.go:289thenDispatchNotContains no longer filters by issue key — it rejects whenever ANY dispatch exists in the output file. The old code checked d.ResourceKey == "issue-" + issueKey per record, correctly distinguishing dispatches belonging to different issues. The new code checks len(dispatches) > 0, so asserting "no dispatch for PROJ-999" will fail if dispatches for an unrelated issue (e.g., PROJ-202) are present. Verified against the existing behaviour test scenario jira-poll-dispatch.feature which calls thenDispatchNotContains(w, "PROJ-999") after dispatching for PROJ-202.
    Remediation: Restore per-record issue filtering using DispatchRecord.StatusNumber (entity ID as string) or by parsing EventPayload for the entity key.

Medium

  • [architectural-divergence] internal/cli/poll.go:233 — GitLab poll path still uses hardcoded HarnessRouter while Jira poll now uses CEL triggers, creating architectural divergence. ADR 0063 describes both as poll input drivers that should route through the same dispatch core with CEL evaluation. The PR acknowledges this with the comment "Used by the GitLab poll path which still uses hardcoded routing."
    Remediation: File a follow-up issue to migrate the GitLab poll path to CEL trigger evaluation.

Low

  • [authorization-bypass-in-test-adapter] pkg/behaviourtest/steps/jirapoll.go:46routerMatcher.Match() does not call harnessdispatch.IsAuthorized(event) before routing, unlike the production celMatcher.Match(). Practical impact is limited since HarnessRouter.Route() already gates slash commands and labels on write role, matching what IsAuthorized enforces for Jira events.

  • [test-integrity] internal/jirapoll/poller_test.go:359TestRunHappyPath_CommentWithSlashCommand had payload-integrity assertions removed. The old test verified EventPayloadB64 contained a valid NormalizedEvent with correct entity key and slash command instruction. The new test only checks d.Agent == "triage". Payload serialization is now handled by harnessdispatch.ProjectExecutionRef (presumably tested there), but the end-to-end round-trip is no longer exercised in the jirapoll package.

  • [test-integrity] pkg/behaviourtest/steps/jirapoll.go:228thenDispatchContains dropped per-issue filtering: new code matches only on d.Agent == stage, ignoring the issueKey parameter (used only in the error message). See also: [logic-error] finding at thenDispatchNotContains.

  • [error-handling-idiom] internal/cli/poll.go:304 — Error message "projecting %s: %w" uses an unconventional verb. The function name ProjectExecutionRef makes it traceable, but established pattern uses standard verbs like load, build, read.

  • [naming-convention] internal/jirapoll/convert.go:29 — Function name toNormEvent is abbreviated; the old name toNormalizedEvent was more descriptive and consistent with codebase conventions.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR

fullsend-ai-review[bot]

This comment was marked as outdated.

@ralphbean

Copy link
Copy Markdown
Member

/fs-fix address unresolved findings

@ralphbean

Copy link
Copy Markdown
Member

/fs-fix address unresolved findings and fix CI

@fullsend-ai-coder

fullsend-ai-coder Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 8:30 PM UTC · Completed 8:44 PM UTC

Commit: 80a366f · View workflow run →

@ralphbean

Copy link
Copy Markdown
Member

Filed #6343 to track the [architectural-divergence] finding (GitLab poll → CEL trigger migration).

- [stale-doc] Update jira-integration.md: replace removed field names
  (stage, resource_key, iid, event_payload_b64) with new DispatchRecord
  fields (agent, event_type, event_payload, source_repo, status_number,
  status_repo, role, trigger_source)
- [logic-error] Restore per-issue filtering in thenDispatchNotContains
  and thenDispatchContains by parsing entity.key from EventPayload
- [authorization-bypass-in-test-adapter] Add IsAuthorized gate to
  routerMatcher.Match() to match production celMatcher behaviour
- [error-handling-idiom] Change unconventional "projecting" verb to
  "build execution ref for" in celMatcher error message
- [naming-convention] Rename abbreviated toNormEvent back to
  toNormalizedEvent for consistency with codebase conventions

Addresses review feedback on #6340
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 1 (human-triggered)

Addressed 6 of 8 review findings with code fixes. Fixed the CI-failing logic error in thenDispatchNotContains (restored per-issue key filtering), updated stale documentation with new DispatchRecord field names, added IsAuthorized gate to test adapter, fixed error message idiom, and renamed abbreviated function. Disagreed with 2 findings: architectural-divergence (follow-up issue filing is out of scope) and poller_test payload assertions (removed assertions tested a field that no longer exists).

Fixed (6):

  1. stale-doc: jira-integration.md references removed dispatch fields (docs/guides/user/jira-integration.md): Updated shell script dispatch loop, jq inspection examples, and dispatch record format tables to use new DispatchRecord field names (agent, event_type, event_payload, source_repo, status_number, status_repo, role, trigger_source), replacing removed fields (stage, resource_key, iid, event_payload_b64, is_fork)
  2. logic-error: thenDispatchNotContains rejects any dispatch instead of filtering by issue key (pkg/behaviourtest/steps/jirapoll.go): Restored per-issue filtering in thenDispatchNotContains by parsing entity.key from EventPayload JSON. Added dispatchMatchesIssue helper that unmarshals EventPayload to check the entity key. Fixes the failing behaviour test scenario where PROJ-999 assertion failed due to PROJ-202 dispatches being present
  3. test-integrity: thenDispatchContains dropped per-issue filtering (pkg/behaviourtest/steps/jirapoll.go): Added per-issue filtering to thenDispatchContains using the same dispatchMatchesIssue helper, so dispatch assertions match both agent name and issue key
  4. authorization-bypass-in-test-adapter: routerMatcher.Match() missing IsAuthorized check (pkg/behaviourtest/steps/jirapoll.go): Added harnessdispatch.IsAuthorized(event) gate to routerMatcher.Match() to match production celMatcher behaviour. Existing tests pass because mock actors have write role via the Developers role
  5. error-handling-idiom: unconventional verb 'projecting' in error message (internal/cli/poll.go): Changed error message from 'projecting %s' to 'build execution ref for %s' to use a standard verb consistent with codebase conventions
  6. naming-convention: abbreviated function name toNormEvent (internal/jirapoll/convert.go): Renamed toNormEvent back to toNormalizedEvent for consistency with the original name and codebase conventions. Updated all call sites in convert_test.go and poller.go

Disagreed (2):

  1. architectural-divergence: GitLab poll path still uses hardcoded HarnessRouter: Filing a follow-up issue is out of scope for the fix agent. The PR already acknowledges this divergence in the buildRouter comment. A follow-up issue should be filed by a human to track migrating the GitLab poll path to CEL triggers
  2. test-integrity: TestRunHappyPath_CommentWithSlashCommand removed payload-integrity assertions: The removed assertions verified base64-encoded NormalizedEvent round-tripping through EventPayloadB64 — a field that no longer exists. The test now uses stubMatcher which returns synthetic DispatchRecords without real payloads. Restoring payload assertions would test the stub implementation, not production code. Payload serialization is handled by harnessdispatch.ProjectExecutionRef which has its own test coverage

Tests: passed

Next steps:

  • /fs-review — request a re-review of the changes
  • /fs-fix <your instruction> — run another fix pass with specific guidance
  • Push commits directly — review re-runs automatically on push
    Updated by fullsend fix agent

@github-actions

Copy link
Copy Markdown

Site preview

Preview: https://3863f55d-site.fullsend-ai.workers.dev

Commit: c1c02086a7d8fc944b7553aac6eebcfad3c7ae1c

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 18, 2026

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 8:46 PM UTC · Ended 8:58 PM UTC

Commit: c1c0208 · View workflow run →

@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 33.92857% with 37 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/cli/poll.go 0.00% 35 Missing ⚠️
internal/jirapoll/poller.go 71.42% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@ralphbean

Copy link
Copy Markdown
Member

/fs-review

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 18, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:59 PM UTC · Completed 9:15 PM UTC

Commit: c1c0208 · View workflow run →

@fullsend-ai-review
fullsend-ai-review Bot dismissed their stale review August 18, 2026 21:14

Superseded by updated review

@fullsend-ai-review fullsend-ai-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See the review comment for full details.


Note: The following inline comments could not be posted on the diff (GitHub returned 422) and are included here instead:

  • pkg/behaviourtest/steps/jirapoll.go:73: [medium] test-integrity

routerMatcher adapter marshals a dispatch.NormalizedEvent as the EventPayload (containing entity.key), but the production celMatcher calls harnessdispatch.ProjectExecutionRef → buildEventPayload, which produces {"issue":{"number":N,"html_url":"..."}} with no top-level entity key. dispatchMatchesIssue (line 285) parses entity.key from EventPayload — this works against the test adapter format but would silently return false for production-format payloads. The behaviour tests exercise per-issue assertions against a payload schema that production never produces.

Suggested fix: Make the routerMatcher produce EventPayload in the same format as the production celMatcher (call harnessdispatch.ProjectExecutionRef or buildEventPayload), and adjust dispatchMatchesIssue to use a field that exists in the production payload format (e.g., status_number).

  • internal/cli/poll.go (file-level): Line 654 · [low] architectural-divergence

GitLab poll path still uses hardcoded HarnessRouter while Jira poll now uses CEL triggers, creating architectural divergence. The code comment acknowledges this as intentional interim state. This is documented tech debt, not a defect introduced by this PR.

  • internal/jirapoll/poller_test.go:359: [low] test-integrity

TestRunHappyPath_CommentWithSlashCommand had payload-integrity assertions removed. The stubMatcher does not produce an EventPayload, so the test no longer verifies the event-to-payload pipeline at the poller level.

  • pkg/behaviourtest/steps/jirapoll.go (file-level): Line 192 · [low] test-integrity

thenDispatchActorRole no longer filters dispatches by issueKey before checking actor role. In a multi-issue test scenario, this could match a dispatch from the wrong issue and verify the wrong role.

@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment component/dispatch Workflow dispatch and triggers go Pull requests that update go code documentation labels Aug 18, 2026
@ralphbean
ralphbean added this pull request to the merge queue Aug 19, 2026
Merged via the queue into main with commit c3cdf4e Aug 19, 2026
35 of 36 checks passed
@ralphbean
ralphbean deleted the agent/6339-cel-trigger-jira-poll branch August 19, 2026 12:09
@fullsend-ai-retro

fullsend-ai-retro Bot commented Aug 19, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ❌ Failure · Started 12:12 PM UTC · Completed 12:12 PM UTC

Commit: c1c0208 · View workflow run →

fullsend-ai-coder Bot added a commit that referenced this pull request Aug 19, 2026
Merge origin/main to incorporate recent changes including:
- PER_REPO_WIF_REPOS=* for GCP public mint mode (PR #6377)
- Base dispatch e2e tests (PR #6367)
- CEL trigger jira-poll (PR #6340)
- Other main-branch updates

Resolved conflict in docs/cli/mint.md: adopted main's updated
--public flag description (PER_REPO_WIF_REPOS=* replaces
ALLOWED_ORGS=* for GCP) while preserving the new --status-auth,
--status-github-group, and --status-github-client-id flags from
this PR.

Addresses review feedback on #6365
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/dispatch Workflow dispatch and triggers documentation go Pull requests that update go code ready-for-review Triggers review agent dispatch requires-manual-review Review requires human judgment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(dispatch): replace jira-poll hardcoded HarnessRouter with CEL trigger evaluation

1 participant