Skip to content

fix(platform): gate the task-comment automation trigger on the author - #3146

Open
Israeltheminer wants to merge 1 commit into
mainfrom
fix/gate-comment-automation-trigger
Open

fix(platform): gate the task-comment automation trigger on the author#3146
Israeltheminer wants to merge 1 commit into
mainfrom
fix/gate-comment-automation-trigger

Conversation

@Israeltheminer

Copy link
Copy Markdown
Collaborator

An agent- or workflow-authored task comment could start the task's owning automation. Each iteration is a metered agent turn, so it sustains itself: run finishes, posts a comment naming itself, new run.

Refs #3142.

Why

0.4 called the trigger from exactly one place — applyUserTaskComment, whose docblock calls itself "THE single write path for a USER-authored comment". The agent door was a separate function hard-coding actorType: 'agent' and never reached the trigger. #3071's body states the rule: the trigger runs "from the USER comment path only — agent/workflow-authored comments never trigger, keeping the loop-safety posture".

On 0.5 one addTaskComment serves both lanes and called maybeTriggerOwningAutomation unconditionally.

The asymmetry is what proves it was dropped rather than redesigned: dispatchMentionedProjectAgent, called four lines below, does receive authorType and gates on it, docblocked "Only a HUMAN's comment dispatches — an agent's own comment naming itself would loop".

Reachable through three elevated callers, all of which pass the write check: the workflow task.comment native (actorId: 'workflow'), the task-agent's own task_comment tool, and the overdue nudge cron. startWorkflowForTask's guard is one live run per (automation, task) — it blocks a concurrent second start, not a sequential loop.

Observed, not reasoned: with the gate deleted, an agent-authored comment enqueued a real task.start_workflow job.

What changed

authorType is threaded into maybeTriggerOwningAutomation and gated on 'user', mirroring the sibling four lines away rather than inventing a second mechanism.

Second fix in the same file: editing a comment never re-resolved its mentions, so editing one to add @someone notified nobody and the stored mention set went stale against the rendered body. 0.4's edit path re-parsed, patched the set, and fanned out only the newly added names. editTaskComment wrote edited_at_ms and nothing else. addedMentions survived in core/tasks/mentions.ts with no caller — it is now wired, so only added names are notified. Re-notifying everyone on every edit is the bug that diff existed to prevent.

Tests

integration-check.ts, run against a real Postgres and MinIO.

Mutation Went red
the author gate deleted agentAuthored: startJobs=1 (want 0) — the loop, observed
edit reverted to writing only edited_at_ms addedBell=0 (want 1)
edit fans out the full set instead of the added ones alreadyMentionedBells=2 (unchanged from 1)

The bell count is a valid observable here because mention is deliberately absent from the coalesce dimension map, so a second bell really is a second row.

The three elevated callers were each re-checked against green probes: the workflow native now enqueues nothing (the point), while the task-agent tool still posts its comment and the overdue nudge still posts its nudge.

Scope

Task description mentions still never fan out. That one is named in a PENDING: note in backend/MIGRATION.md, so it is a separate decision rather than a dropped fix — though the ledger row is marked done, which is worth correcting. addedMentions is wired for comments only.

The edit path does not surface unresolved mention tokens. 0.4's did not either, and adding it would be new user-visible copy needing all three locales.

Gate: bun run format clean, bun run lint 0/0 across 17 workspaces, typecheck exit 0, unit suite 72,275 passing, SAST 0 findings. Integration 279/281 — the two failures are a yt-dlp-dependent probe absent from the host and a warm-MinIO bucket collision, both failing identically on the unmodified base.

Two 0.4 behaviours the 0.5 port dropped from the task-comment path.

An agent- or workflow-authored comment could start the task's owning
automation. 0.4 called the trigger from one place — `applyUserTaskComment`,
the single write path for a USER comment — while the agent door hard-coded
`actorType: 'agent'` and never reached it. 0.5 merged both lanes into one
`addTaskComment`, and `maybeTriggerOwningAutomation` ran unconditionally
with no author type to check. The result is a self-sustaining loop: the
automation comments, the comment restarts the automation, and every
iteration is a metered agent turn. `startWorkflowForTask`'s duplicate guard
does not catch it — one live run per (automation, task) blocks a concurrent
second start, not a sequential loop. The trigger now takes the author type
and returns early unless it is `user`, mirroring the agent lane's
`dispatchMentionedProjectAgent` gate right below it in the same file.

Editing a comment never re-resolved its mentions: `editTaskComment` wrote
only `edited_at_ms`, so editing a comment to add `@someone` notified nobody
and the stored mention set went stale. It now re-resolves through
`resolveSurfaceMentions` and diffs with `addedMentions` — which had survived
the port with no production caller — patching the full set onto the meta row
and fanning out the ADDED mentions only, as the bell plus `comment.mentioned`
with no subscriber re-alert. Rewording prose around an existing `@handle`
still notifies nobody. Editing starts no engine.

Both are probed in the real-Postgres integration check, each seen red
against deliberately broken code: dropping the author gate enqueues a
`task.start_workflow` job for the agent-authored comment; reverting the edit
path notifies nobody and leaves the stored set stale; fanning out the full
set instead of the added ones gives the already-mentioned teammate a second
bell.

Task DESCRIPTION mentions (`updateTask`) stay PENDING — a separate decision.
@Israeltheminer
Israeltheminer force-pushed the fix/gate-comment-automation-trigger branch from a88b5ec to 737ab3e Compare September 3, 2026 13:02
@Israeltheminer

Copy link
Copy Markdown
Collaborator Author

Rebased onto current main.

One thing worth recording, because it nearly went out silently. The only conflict was MIGRATION.md, where main had reworded the collab row. I reached for git checkout --theirs — but during a rebase "theirs" is the commit being replayed, not the upstream, so it handed me my own stale copy of the ledger and the fallback never ran. The commit came out with 310 lines of the ledger reverted. Caught it by diffing against main per file rather than trusting the rebase, restored main's version, and re-applied my one-line note against its new wording.

Verified against a real Postgres and MinIO:

result
this branch 376/381
unmodified main 373/379

+2 checks, +3 passes, no new failures. Mine shows five failures where main shows six — the extra one on the baseline is the warm-MinIO bucket collision, which only appears on a reused store; the other five (two yt-dlp probes, three agent-lane probes) are identical on both.

The behavioural pair still holds after the rebase: an agent-authored comment naming the owning automation enqueues nothing, while a human's identical comment enqueues the run.

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