Skip to content

fix(labeler): dispatch the assessment Workflow on operator rerun#2103

Merged
ascorbic merged 1 commit into
feat/plugin-registry-labelling-servicefrom
fix/labeler-rerun-dispatch
Jul 17, 2026
Merged

fix(labeler): dispatch the assessment Workflow on operator rerun#2103
ascorbic merged 1 commit into
feat/plugin-registry-labelling-servicefrom
fix/labeler-rerun-dispatch

Conversation

@ascorbic

@ascorbic ascorbic commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

Fixes a functional bug in the operator Re-run an assessment action: it minted a fresh run and gated the release with assessment-pending, but never dispatched the Cloudflare Workflow that executes it — so the release was stranded in pending indefinitely (until it surfaced as a reconciliation stuck-run alert). Targets the feat/plugin-registry-labelling-service integration branch (RFC #694, umbrella #1909).

Root cause: ConsoleMutationDeps never carried the ASSESSMENT_WORKFLOW binding at all, so runRerun's deferred tail (deferRerunTail) advanced the run to pending and published the pending label but structurally could not dispatch. The discovery path got the binding and dispatches (discovery-consumer.ts); the console path never did — almost certainly because rerun was built while the analysis stages were stubbed and PROD-gated, and the dispatch wasn't reconnected when the real stages landed (#2090/#2091). The bug became live-visible only once the pipeline started producing real assessments.

Fix: thread AssessmentWorkflowBinding into ConsoleMutationDeps (wired from env.ASSESSMENT_WORKFLOW at the console-mutation construction site), and dispatch the run in deferRerunTail after advancing to pending, keyed on the run's persisted runKey:

await dispatchAssessmentWorkflow(deps.assessmentWorkflow, { runKey: run.runKey, assessmentId: runId });

Why it's safe (the dedup rests on #2072's instance-id lock):

  • A rerun mints a fresh runKey (from the operator:<actionId> trigger, distinct from discovery's initialTriggerId(cid) and any prior rerun), so it dispatches its own new Workflow instance — no collision with the original run's.
  • The Workflow instance id is the runKey, so a re-driven tail (a defer retry, or the guardMutation replay branch — which also runs this tail) re-dispatching the same runKey dedups to "exists": no duplicate run, no duplicate labels.
  • No new current-pointer/double-issue hazard: the rerun run traverses the identical Workflow → orchestrator → finalization path as a discovery run, with runKey-derived idempotency keys; the fix only lets a previously-stranded run actually execute.

Dispatch-failure posture (documented): the dispatch stays inside the existing guarded deferRerunTail try/catch — a failure is logged and dropped. Unlike discovery (which retries the queue message), this deferred tail has no retry lever, so a dropped dispatch strands the run pending until the reconciliation stuck-run sweep surfaces it for a manual re-trigger. A comment states this. Not escalated to an operational_event for a rare infra failure that the stuck-run alert already backstops — flagging in case you'd prefer immediate operator visibility.

Type of change

  • Bug fix
  • Feature (requires a Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes (all three labeler projects)
  • pnpm lint passes (oxlint type-aware, clean on all changed files)
  • pnpm test passes (rerun + discovery + dispatch suites 53; full labeler suite 833) — including TDD: the dispatch-assertion tests fail against pre-fix code (expected [] to have length 1, proving dispatch was never invoked) and pass post-fix.
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable) — n/a: server-side; the console "Re-run" button already calls the endpoint, no UI change.
  • I have added a changeset (if this PR changes a published package) — n/a: @emdash-cms/labeler is private: true.
  • New features link to an approved Discussion — n/a: bug fix; the umbrella Plugin registry labelling service #1909 tracks RFC RFC: Decentralized Plugin Registry #694.

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: Claude Opus 4.8 (implementation + threat-model check), reviewed by Claude Fable 5

Screenshots / test output

console-assessment-mutations: 24 passed  (incl. the rerun-dispatch + idempotent-replay + dispatch-failure cases)
discovery-consumer + assessment-dispatch: verified unaffected
full labeler suite: 833 passed

New tests: a rerun dispatches its fresh run to its own Workflow instance (id === runKey, params.assessmentId === runId); proceed + replay yields exactly one created instance (idempotent); a tail dispatch failure keeps the mutation a 200 and doesn't crash the tail.


Try this PR

Open a fresh playground →

A full working EmDash site, deployed from this branch. Each visit gets its own session-scoped sandbox: no login needed and no shared state. Try the admin, edit content, hit the public site.

Tracks fix/labeler-rerun-dispatch. Updated automatically when the playground redeploys.

runRerun minted a fresh assessment run, gated the release with assessment-pending, and advanced the run to pending in its deferred tail, but never dispatched the Cloudflare Workflow that executes it — stranding the release pending forever until the reconciliation stuck-run alert. ConsoleMutationDeps lacked the workflow binding entirely.

Thread AssessmentWorkflowBinding into ConsoleMutationDeps (wired from env.ASSESSMENT_WORKFLOW), and dispatch the run in deferRerunTail after advancing to pending, keyed on the run's runKey. The operator trigger yields a fresh runKey, so the rerun gets its own instance; a re-driven tail or replay dedups onto it via the instance-id lock.
@github-actions github-actions Bot added review/needs-review No maintainer or bot review yet size/M cla: signed labels Jul 17, 2026
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-demo-do 57b442c Jul 17 2026, 09:32 PM

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-demo-cache 57b442c Jul 17 2026, 09:33 PM

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-playground 57b442c Jul 17 2026, 09:33 PM

@emdashbot emdashbot 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.

This is a focused, correct bug fix.

Approach judgment: The PR targets the right problem — operator reruns were minting fresh runs and gating releases with assessment-pending, but never dispatching the AssessmentWorkflow, leaving runs stranded. The fix threads the existing ASSESSMENT_WORKFLOW binding into ConsoleMutationDeps and dispatches the run in deferRerunTail after advancing it to pending. This mirrors the discovery-consumer path, reuses the runKey-as-instance-id idempotency invariant from #2072, and keeps the dispatch off the response path consistent with other post-commit side effects (afterCommit, notifications).

I verified that:

  • ASSESSMENT_WORKFLOW is already declared in wrangler.jsonc and worker-configuration.d.ts; the PR only wires the binding into console mutations.
  • dispatchAssessmentWorkflow is called with runKey: run.runKey and assessmentId: runId, matching the discovery path.
  • Both the proceed and replay branches recompute the same trigger/runKey so the Workflow instance id is stable across replays and dedups correctly.
  • The dispatch failure is intentionally caught and logged (documented reliance on the stuck-run sweep) without surfacing to the client.
  • All ConsoleMutationDeps construction sites (production and tests) were updated with a workflow binding.
  • The package is private: true, so the "no changeset" checkbox is justified.

The test additions cover the happy path, replay idempotency, and dispatch-failure posture, and the default mutationDeps mock won’t break non-rerun tests. No logic bugs, AGENTS.md convention violations, or missing wiring found.

Approve with no blocking comments.

@github-actions github-actions Bot added review/approved Approved; no new commits since and removed review/needs-review No maintainer or bot review yet labels Jul 17, 2026
@ascorbic
ascorbic merged commit 7c4b847 into feat/plugin-registry-labelling-service Jul 17, 2026
11 checks passed
@ascorbic
ascorbic deleted the fix/labeler-rerun-dispatch branch July 17, 2026 21:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla: signed review/approved Approved; no new commits since size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant