Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions personas/e2e-mode2-hello/agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { handler } from '@agentworkforce/runtime';

/**
* Minimal Mode 2 handler. One trigger -> one action.
*
* Listens for GitHub `issues.opened` / `issues.labeled` events on
* AgentWorkforce/cloud. If the issue is open and carries the `hello`
* label, posts a single confirmation comment via ctx.github.comment.
* Anything else is ignored. No clone, no shell, no workflow.
*/

const REPO_OWNER = 'AgentWorkforce';
const REPO_NAME = 'cloud';
const REPO_FULL_NAME = `${REPO_OWNER}/${REPO_NAME}`;
const LABEL = 'hello';
const COMMENT_BODY =
'hello from e2e-mode2-hello persona — Mode 2 E2E reached this handler.';

export default handler(async (ctx, event) => {
if (event.source !== 'github') {
ctx.log('info', 'ignoring unsupported event source', { source: event.source });
return;
}
if (event.type !== 'issues.opened' && event.type !== 'issues.labeled') {
ctx.log('info', 'ignoring non-issue event', { type: event.type });
return;
}
Comment on lines +24 to +27
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

The current label gate can post duplicate comments for unrelated label events.

Once an issue has hello, every future issues.labeled event still satisfies Line 45 and can post another confirmation comment, which breaks the intended single-comment probe behavior.

Proposed fix
   const labels = readLabels(issue.labels ?? resource.labels);
   if (!labels.includes(LABEL)) {
     ctx.log('info', 'skipping issue without hello label', { labels, eventId: event.id });
     return;
   }
+  if (event.type === 'issues.labeled') {
+    const addedLabel = stringValue(asRecord(resource.label).name)?.toLowerCase();
+    if (addedLabel !== LABEL) {
+      ctx.log('info', 'skipping labeled event for non-hello label', { addedLabel, eventId: event.id });
+      return;
+    }
+  }

Also applies to: 44-47, 67-71

🤖 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 `@personas/e2e-mode2-hello/agent.ts` around lines 24 - 27, The current gate
allows any issues.labeled webhook to trigger the probe, causing duplicate
comments; change the condition to only proceed for issues.opened OR for
issues.labeled events where the action is 'labeled' and the added label name ===
'hello' (inspect event.action and event.label.name on the payload), and before
posting, query existing issue comments to skip posting if a confirmation comment
from this bot already exists (or if the issue already had the hello label prior
to this event). Update the conditional that uses event and ctx.log (and the
label-handling logic referenced around the blocks you noted) so it only handles
the specific label-addition case and performs an idempotency check to avoid
duplicate comments.


const resource = asRecord(event.payload);
const issue = maybeRecord(resource.issue) ?? resource;
const repo = asRecord(resource.repository);
const fullName = stringValue(repo?.full_name) ?? REPO_FULL_NAME;
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.

P1: Fail closed when repository.full_name is missing instead of defaulting it to the target repo, so malformed payloads cannot bypass the repository guard.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At personas/e2e-mode2-hello/agent.ts, line 32:

<comment>Fail closed when `repository.full_name` is missing instead of defaulting it to the target repo, so malformed payloads cannot bypass the repository guard.</comment>

<file context>
@@ -0,0 +1,99 @@
+  const resource = asRecord(event.payload);
+  const issue = maybeRecord(resource.issue) ?? resource;
+  const repo = asRecord(resource.repository);
+  const fullName = stringValue(repo?.full_name) ?? REPO_FULL_NAME;
+  if (fullName !== REPO_FULL_NAME) {
+    ctx.log('info', 'ignoring event for different repo', { fullName });
</file context>

if (fullName !== REPO_FULL_NAME) {
ctx.log('info', 'ignoring event for different repo', { fullName });
return;
Comment on lines +32 to +35
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail closed when repository.full_name is missing.

Line 32 currently defaults missing repo metadata to AgentWorkforce/cloud, which can let malformed payloads pass the repo gate and write a comment anyway.

Proposed fix
-  const fullName = stringValue(repo?.full_name) ?? REPO_FULL_NAME;
-  if (fullName !== REPO_FULL_NAME) {
+  const fullName = stringValue(repo?.full_name);
+  if (!fullName) {
+    ctx.log('warn', 'missing repository.full_name', { eventId: event.id });
+    return;
+  }
+  if (fullName !== REPO_FULL_NAME) {
     ctx.log('info', 'ignoring event for different repo', { fullName });
     return;
   }
🤖 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 `@personas/e2e-mode2-hello/agent.ts` around lines 32 - 35, The code defaults a
missing repo.full_name to REPO_FULL_NAME allowing malformed payloads through;
change the logic in agent.ts around fullName/REPO_FULL_NAME so missing
repository.full_name fails closed: get fullName with
stringValue(repo?.full_name) without the fallback, log and return if fullName is
falsy (use ctx.log with a clear message), then only continue if fullName ===
REPO_FULL_NAME (and otherwise log and return as it currently does).

}

const issueState = stringValue(issue.state ?? resource.state)?.toLowerCase();
if (issueState !== 'open') {
ctx.log('info', 'skipping non-open issue', { issueState, eventId: event.id });
return;
}

const labels = readLabels(issue.labels ?? resource.labels);
if (!labels.includes(LABEL)) {
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.

P2: issues.labeled events are not constrained to the newly added hello label, so adding unrelated labels can still post the hello comment.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At personas/e2e-mode2-hello/agent.ts, line 45:

<comment>`issues.labeled` events are not constrained to the newly added `hello` label, so adding unrelated labels can still post the hello comment.</comment>

<file context>
@@ -0,0 +1,99 @@
+  }
+
+  const labels = readLabels(issue.labels ?? resource.labels);
+  if (!labels.includes(LABEL)) {
+    ctx.log('info', 'skipping issue without hello label', { labels, eventId: event.id });
+    return;
</file context>

ctx.log('info', 'skipping issue without hello label', { labels, eventId: event.id });
return;
}

const issueNumber = numberValue(issue.number ?? resource.number);
if (!issueNumber) {
ctx.log('warn', 'missing issue number', { eventId: event.id });
return;
}

if (!ctx.github?.comment) {
// Fail loud: a missing github.comment binding silently hides every effect
// this persona produces. Surface it in cloud-web tail rather than appear
// green-but-no-op.
ctx.log('warn', 'ctx.github.comment unavailable; comment dropped', {
issueNumber,
bodyPreview: COMMENT_BODY.slice(0, 120)
});
return;
}

await ctx.github.comment(
{ owner: REPO_OWNER, repo: REPO_NAME, number: issueNumber },
COMMENT_BODY
);
ctx.log('info', 'posted hello comment', { issueNumber });
});

function asRecord(value: unknown): Record<string, any> {
return value && typeof value === 'object' && !Array.isArray(value)
? (value as Record<string, any>)
: {};
}

function maybeRecord(value: unknown): Record<string, any> | null {
return value && typeof value === 'object' && !Array.isArray(value)
? (value as Record<string, any>)
: null;
}

function stringValue(value: unknown): string | null {
return typeof value === 'string' && value.trim() ? value.trim() : null;
}

function numberValue(value: unknown): number | null {
const number = typeof value === 'number' ? value : Number(value);
return Number.isFinite(number) && number > 0 ? number : null;
}

function readLabels(value: unknown): string[] {
return Array.isArray(value)
? value.map((entry) => String(asRecord(entry).name ?? entry).toLowerCase())
: [];
}
9 changes: 9 additions & 0 deletions personas/e2e-mode2-hello/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "e2e-mode2-hello",
"version": "0.1.0",
"private": true,
"type": "module",
"dependencies": {
"@agentworkforce/runtime": "^3.0.30"
}
}
32 changes: 32 additions & 0 deletions personas/e2e-mode2-hello/persona.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"id": "e2e-mode2-hello",
"intent": "review",
"tags": [
"review"
],
"description": "Minimal Mode 2 E2E probe: replies to AgentWorkforce/cloud issues labeled `hello` with a single confirmation comment, to prove the single-agent handler path runs end-to-end.",
"cloud": true,
"onEvent": "./agent.ts",
"harness": "codex",
"model": "gpt-5",
"systemPrompt": "Handle the proactive event.",
"harnessSettings": {
"reasoning": "low",
"timeoutSeconds": 60
},
"integrations": {
"github": {
"source": {
"kind": "workspace"
},
"triggers": [
{
"on": "issues.opened"
},
{
"on": "issues.labeled"
}
]
}
}
}
35 changes: 35 additions & 0 deletions personas/e2e-mode2-hello/persona.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { definePersona } from '@agentworkforce/persona-kit';

/**
* Minimal Mode 2 persona (single-agent handler).
*
* Trigger: a GitHub issue is opened or labeled on AgentWorkforce/cloud.
* Action: if the issue carries the `hello` label, the handler posts a
* single comment back acknowledging the Mode 2 handler ran.
*
* Exists to prove the Mode 2 path (proactive trigger -> single agent.ts
* handler -> back-channel write) end-to-end with the smallest possible
* persona — no clone, no shell, no workflow, no runtime work.
*/
export default definePersona({
id: 'e2e-mode2-hello',
intent: 'review',
tags: ['review'],
description:
'Minimal Mode 2 E2E probe: replies to AgentWorkforce/cloud issues labeled `hello` with a single confirmation comment, to prove the single-agent handler path runs end-to-end.',
cloud: true,
onEvent: './agent.ts',
harness: 'codex',
model: 'gpt-5',
systemPrompt: 'Handle the proactive event.',
harnessSettings: { reasoning: 'low', timeoutSeconds: 60 },
integrations: {
github: {
source: { kind: 'workspace' },
triggers: [
{ on: 'issues.opened' },
{ on: 'issues.labeled' }
]
}
}
});
Loading