Skip to content

feat(triage): experimental Flue-based issue triage#1090

Open
ascorbic wants to merge 7 commits into
mainfrom
feat/flue-triage
Open

feat(triage): experimental Flue-based issue triage#1090
ascorbic wants to merge 7 commits into
mainfrom
feat/flue-triage

Conversation

@ascorbic
Copy link
Copy Markdown
Collaborator

@ascorbic ascorbic commented May 18, 2026

What does this PR do?

Adds an experimental Flue-based issue triage system. Two complementary paths, both opt-in, neither deployed. CI tooling only -- nothing published, no behaviour change in any package.

Phase 1: Worker-deployed auto-labeller (.flue/agents/triage-label.ts)
Receives issues.opened webhooks, verifies HMAC against raw bytes via Web Crypto, classifies the issue with kimi-k2.6, applies labels, and posts a structured summary comment. Filter-then-apply guard against hallucinated label names. DRY_RUN env var skips writes for first deploys. Schema enforces: kind (bug/enhancement/documentation/question), severity (low/medium/high/critical), 0-3 area/* labels, reproducible bool, dataLossRisk bool, one-sentence summary.

Phase 2: GH-Actions-driven reproduction (.flue/agents/repro-issue.ts + .github/workflows/auto-repro.yml)
Fires only when a maintainer applies the triage:reproduce label, never on every new issue. Uses Flue's local() sandbox so the agent's bash tool gets real pnpm/git/gh/node access on the runner. Tries to reproduce as a vitest test or repro script, posts a single summary comment, applies reproduced / not-reproduced / repro-skipped. Strict guardrails: no branch pushes, no commits, no fix attempts.

Local prototype runner (.flue/scripts/run-local.ts)
Wraps flue run triage-issue with real issue payloads. 5 fixture issues from the current queue (#1021, #1042, #1046, #1049, #1080) so the prompt can be iterated without deploying anything.

Model traffic uses the existing CF_AI_GATEWAY_* secrets shared with bonk.yml and review.yml for unified cost tracking. No new auth surface, no new provider keys.

Why two phases: Phase 1 is cheap (~$0/issue on Workers AI), fast (~5s), and conservative (label + comment only) -- safe to run on every new issue. Phase 2 is expensive (Opus on a 30-min runner) and powerful (real shell, can write tests) -- runs only on explicit maintainer opt-in.

Not deployed. This PR adds the scaffolding only. No alchemy deploy workflow, no GH webhook registered, no GITHUB_WEBHOOK_SECRET set. Follow-up PRs will wire up the deploy plumbing and a first synthetic-issue test once we agree on the shape here.

Threat model

The repro agent (Phase 2) feeds attacker-controlled issue bodies into a prompt context with a bash-equipped sandbox. Anyone can file an issue. The agent's "do not push, do not commit, do not curl" guardrails in skills/reproduce/SKILL.md are prompt-level only.

What blocks real abuse (in order of strength):

  1. Two-token split, mirroring withastro/astro's issue-triage setup:
    • AGENT_GH_TOKEN (workflow's default GITHUB_TOKEN, scoped to contents: read, issues: read) is the only token passed into the local() sandbox env. A jailbroken agent's bash can gh issue view, git clone, read the repo -- and nothing else.
    • ORCHESTRATOR_GH_TOKEN (a GitHub App installation token minted by actions/create-github-app-token) holds issues: write. Stays in the TS orchestrator's process.env; never crosses into the sandbox. All comment and label writes go through this. A jailbroken agent's bash cannot impersonate the bot.
  2. Maintainer label gate. The workflow fires on issues.labeled with label.name == 'triage:reproduce'. A maintainer has to apply that label before the agent ever sees the body. First boundary -- don't apply triage:reproduce to an issue you wouldn't drop a fresh Opus into.
  3. contents: read permission on the job. No branch pushes, even from a jailbroken agent.

Capabilities a jailbroken sandbox retains: read-only operations across the repo, arbitrary runner network egress. Structurally impossible: comment / label / close any issue or PR, push to any branch, exfiltrate the orchestrator token or AI Gateway credentials (local() filters host env by default).

Follow-up worth considering: step-security/harden-runner for an egress allowlist (defense in depth against the "agent curls attacker.com from the runner" residual).

Closes #

Type of change

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

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes (0 diagnostics in the new files)
  • pnpm test passes (or targeted tests for my change) -- no test surface changed
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable) -- N/A, prompt-driven agent code
  • User-visible strings in the admin UI are wrapped for translation (if applicable). Do not include messages.po changes except in translation PRs -- a workflow extracts catalogs on merge to main. (N/A -- no admin UI changes.)
  • I have added a changeset (if this PR changes a published package) -- N/A, tooling-only, no published package affected
  • New features link to an approved Discussion -- N/A, this is CI tooling

AI-generated code disclosure

  • This PR includes AI-generated code -- model/tool: Claude Opus 4.7 (opencode)

Review history

  • efd0efc1 -- initial scaffold (Phase 1 + 2 + local runner, 5 issue fixtures)
  • b453ae95 -- review feedback: HIGH webhook-secret fail-open fix, MEDIUM fence-escape in repro comment, MEDIUM threat model docs, LOW label precreation, LOW SKIP_HMAC removal, LOW newline strip on summary, LOW arg validation on local runner
  • fd81927e -- two-token split for prompt-injection mitigation (sandbox gets read-only GITHUB_TOKEN; orchestrator uses an app token for writes); narrowed workflow permissions; removed three inaccurate "all model traffic goes through the gateway" code comments
  • 916b32b4 -- merge main (53 commits, no conflicts)

Remaining before ready-for-review

  • First real run through pnpm prototype against a fixture issue (blocked on local CF gateway env)
  • Stale Validate PR badge -- compliance check actually passed on b453ae95; needs a re-trigger against the current head

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 feat/flue-triage. Updated automatically when the playground redeploys.

Adds two complementary triage paths under .flue/:

Phase 1: Worker-deployed auto-labeller (agents/triage-label.ts).
Receives GitHub issues.opened webhooks, verifies HMAC against raw
bytes via Web Crypto, classifies the issue with kimi-k2.6 through
our existing Cloudflare AI Gateway (same path /bonk uses), and
applies bug/question/documentation + area/* labels with a short
summary comment. Filter-then-apply guards against hallucinated
label names. DRY_RUN env var skips writes for first deploys.

Phase 2: GH-Actions-driven reproduction (agents/repro-issue.ts +
.github/workflows/auto-repro.yml). Fires only when a maintainer
applies the triage:reproduce label, never on every new issue.
Uses local() sandbox so the agent's bash tool gets real pnpm/git/
gh/node access on the runner. Tries to reproduce as a vitest test
or repro script and posts a single summary comment with one of
reproduced/not-reproduced/repro-skipped labels. Never pushes
branches, commits, or attempts fixes.

Local prototype runner (scripts/run-local.ts) wraps flue run
triage-issue with real issue payloads (5 fixtures from current
queue) so the prompt can be iterated without deploying anything.

All model traffic routes through CF_AI_GATEWAY_* secrets shared
with bonk/review for unified cost tracking. Not deployed yet --
needs a Discussion before any actual rollout.
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 18, 2026

⚠️ No Changeset found

Latest commit: 048fe2e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 18, 2026

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 61ba8dc May 26 2026, 04:19 PM

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 18, 2026

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
docs 61ba8dc May 26 2026, 04:19 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-perf-coordinator efd0efc May 18 2026, 11:23 AM

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 18, 2026

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 61ba8dc May 26 2026, 04:19 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-i18n efd0efc May 18 2026, 11:23 AM

@github-actions
Copy link
Copy Markdown
Contributor

Scope check

This PR changes 6,530 lines across 20 files. Large PRs are harder to review and more likely to be closed without review.

If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs.

See CONTRIBUTING.md for contribution guidelines.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 18, 2026

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@1090

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@1090

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@1090

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@1090

emdash

npm i https://pkg.pr.new/emdash@1090

create-emdash

npm i https://pkg.pr.new/create-emdash@1090

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@1090

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@1090

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@1090

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@1090

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@1090

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@1090

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@1090

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@1090

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@1090

commit: 61ba8dc

@ascorbic
Copy link
Copy Markdown
Collaborator Author

/review

Comment thread .flue/alchemy.run.ts Outdated
className: "TriageLabel",
sqlite: true,
}),
GITHUB_WEBHOOK_SECRET: process.env.GITHUB_WEBHOOK_SECRET ?? "dev-secret-rotate-me",
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.

HIGH — fail-open default for webhook secret.

When process.env.GITHUB_WEBHOOK_SECRET is unset, the binding is initialised to the literal string "dev-secret-rotate-me". Combined with triage-label.ts:226 which only refuses the request when secret is falsy, this means a deploy that forgets to set the secret will happily verify HMACs against a value now public in this PR. Anyone reading the source can forge issues.opened webhooks against the deployed Worker.

This is the kind of footgun that bites the first time someone runs alchemy deploy against a real stage without the secret bound. The comment two lines above is the only thing stopping it.

Fix: default to "" (or omit the binding) so the request handler's if (!secret) branch refuses the request, instead of a plausible-looking sentinel that passes the existence check.

Comment thread .flue/agents/repro-issue.ts Outdated
lines.push("");
lines.push("```");
lines.push(repro.notes);
lines.push("```");
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 — fenced-code-block escape via model output.

repro.notes is the agent's transcript of bash commands and their captured output. Whenever the agent runs pnpm test or grep against a markdown file (highly likely on this monorepo — docs/, README.md, CONTRIBUTING.md, every plugin's docs), the captured output frequently contains a literal triple-backtick. Wrapping it in ``` ... ``` then breaks out of the fence and the rest of the comment renders as raw markdown / executes any nested formatting.

This isn't theoretical — the very reproduce skill encourages running grep/test commands whose stdout includes the repo's markdown. First time the model captures a doc snippet, the comment is mangled.

Fix: use a four-backtick (or longer) fence, or replace \u0060\u0060\u0060 runs inside repro.notes before interpolation. The simplest: find the longest backtick run in notes, fence with one more.

// surface. Override with FLUE_REPRO_MODEL for experiments.
model: process.env.FLUE_REPRO_MODEL ?? "cloudflare-ai-gateway/claude-opus-4-7",
});
const reproSession = await reproHarness.session();
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 — prompt injection from issue body into shell-equipped agent.

The repro session uses local() so its bash tool has real pnpm / gh / git / node against the runner's filesystem, and GH_TOKEN is explicitly injected on line 132. Then reproSession.skill("reproduce", { args: { ..., issueTitle, issueBody, triage } }) feeds the entirely-attacker-controlled issue body into the prompt context.

The "strict guardrails" called out in the PR description and in SKILL.md ("no commits, no fix attempts, no curl") are prompt-level — they are advisory text the model can be argued out of by a sufficiently clever issue body. With write-scoped GH_TOKEN in env, a successful jailbreak can call gh to comment on any issue/PR in the repo, apply or remove arbitrary labels, close issues, etc. (The token can't push to branches because the workflow grants contents: read, which is the one real guardrail.)

The trigger is gated on triage:reproduce being applied by a maintainer, which is the main mitigation. But the threat model is: open a malicious issue, wait for a triager to mistakenly request reproduction. Worth being explicit about this in the README / Discussion before enabling, and considering whether the runner should run with a token that only has issues:write on the target issue (e.g. a finer-grained PAT or App installation scoped via headers).

Comment thread .flue/agents/repro-issue.ts Outdated
? "reproduced"
: "not-reproduced";
try {
await addLabels(githubToken, owner, repo, issueNumber, [resultLabel]);
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.

LOW — comment claims the workflow creates labels lazily; nothing does.

Lines 165-169 say "these labels don't exist yet; the workflow creates them lazily" but neither auto-repro.yml nor this file calls POST /repos/{owner}/{repo}/labels to create them. addLabels calls POST /repos/{owner}/{repo}/issues/{n}/labels, which returns 422 if the label doesn't exist on the repo. The try/catch on line 175 swallows this so it's not fatal, but the first N runs will silently fail to apply any result label until someone manually creates reproduced, not-reproduced, repro-skipped in the repo's Labels settings.

Fix options: (a) precreate the three labels as part of the workflow's setup step using gh label create --force, or (b) update the comment to say "these labels must be precreated" so the next person reading the code knows.

Comment thread .flue/agents/triage-label.ts Outdated
// Set to "true" to skip HMAC verification. DEV ONLY — never enable in
// production. Used for local `flue run` prototyping where we can't
// easily sign the synthetic payload.
SKIP_HMAC?: string;
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.

LOW — SKIP_HMAC is read directly from env with no compile-time gate.

SKIP_HMAC === "true" bypasses HMAC verification entirely. The comment says "DEV ONLY — never enable in production" and alchemy.run.ts doesn't currently expose SKIP_HMAC as a binding, so it can't be turned on via the standard deploy path. But a future contributor copy-pasting a binding row or a wrangler secret put SKIP_HMAC true would silently disable auth on the live Worker, with no log telemetry beyond a single warn line that gets buried under request volume.

Fix: gate this on a build-time signal that can't be flipped after deploy — e.g. only consult the flag when globalThis.process?.env?.NODE_ENV === "development" (or equivalent for the Workers runtime), or just delete it. The local prototype path (scripts/run-local.ts) doesn't hit this code path anyway — it calls triage-issue.ts directly without the webhook agent.

}
lines.push("");
lines.push(`> ${triage.summary}`);
return lines.join("\n");
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.

LOW — triage.summary interpolated into a blockquote line with no newline-stripping.

The schema allows up to 200 chars and \n is a valid character in a v.string(). If the model returns a multi-line summary, only the first line is in the blockquote — the rest renders as bare body text, breaking the formatted comment.

Not a security issue (GitHub comments don't render raw HTML), but the auto-comment loses its structured look the moment the model decides to return two sentences. Strip \r?\n from triage.summary before rendering, or replace with a space.

async function loadFixture(arg: string, live: boolean): Promise<Fixture> {
if (live) {
const raw = execSync(
`gh issue view ${arg} --repo emdash-cms/emdash --json number,title,body,labels`,
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.

LOW — unvalidated arg interpolated into gh command.

gh issue view ${arg} where arg is args.filter((a) => !a.startsWith("--"))[i]. Nothing enforces that arg is a number — pnpm prototype '1 && rm -rf /' (or anything with shell metachars) is passed through execSync as-is. This is a local dev tool with no security boundary, but the same arg is then used to construct issue-${arg}.json and would happily traverse with ../.

Fix: assert /^\d+$/.test(arg) before either call, fail with a clear message otherwise.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk Bot commented May 18, 2026

Review posted successfully with 7 line-anchored comments.

Summary of findings:

  • HIGHalchemy.run.ts:40: Fail-open default "dev-secret-rotate-me" for GITHUB_WEBHOOK_SECRET. Combined with the truthy check in the handler, a deploy without overriding the secret accepts forged webhooks signed with this now-public value.
  • MEDIUMrepro-issue.ts:198: Triple-backtick model output (very likely from grep/test against the repo's markdown) breaks out of the code fence in the rendered comment.
  • MEDIUMrepro-issue.ts:142: Issue body is attacker-controlled and feeds a bash-equipped agent with GH_TOKEN in env. The "strict guardrails" are prompt-level only; jailbreak → comment/label/close any issue in repo. Maintainer label-gating is the real mitigation.
  • LOWrepro-issue.ts:176: Comment claims workflow creates result labels lazily; nothing does. Try/catch hides the silent failure.
  • LOWtriage-label.ts:32: SKIP_HMAC env flag has no compile-time gate.
  • LOWtriage-label.ts:206: triage.summary interpolated into blockquote without newline stripping.
  • LOWrun-local.ts:41: Unvalidated arg interpolated into gh issue view.

github run

ascorbic added 3 commits May 23, 2026 08:11
HIGH:
- alchemy.run.ts: default GITHUB_WEBHOOK_SECRET to "" (was a sentinel
  string that would have fail-open authenticated forged webhooks). The
  empty default makes the agent's `if (!secret)` guard fail closed.

MEDIUM:
- repro-issue.ts: dynamic-length backtick fence for `repro.notes` in
  the rendered comment. Captured bash output frequently contains triple
  backticks (markdown docs, README snippets, etc.) which would otherwise
  break out of the code fence.
- README + SKILL.md: document the threat model for the Phase 2 repro
  agent. The maintainer label gate is the real security boundary, not
  the prompt-level guardrails. Add explicit hard prohibitions to the
  skill (no git push, no PR writes, no label management, no curl, etc.)
  and call out the worst-case capabilities of a successful jailbreak.

LOW:
- auto-repro.yml: precreate the three result labels (reproduced /
  not-reproduced / repro-skipped) idempotently with gh label create
  --force. Update the misleading comment in repro-issue.ts that
  implied the workflow did this already.
- triage-label.ts: remove SKIP_HMAC entirely. The local prototype path
  uses triage-issue.ts (no webhook) so the bypass was never reachable
  in dev, and a wrangler secret put SKIP_HMAC true on a live deploy
  would silently disable verification.
- triage-label.ts: collapse newlines in triage.summary before rendering
  into the blockquote, so a multi-line model summary doesn't leak text
  outside the > prefix.
- run-local.ts: validate issueNumber matches /^\d+$/ before
  interpolating into the gh command and the fixture path.
Mirrors the .flue/agents/issue-triage.ts pattern in withastro/astro:
two distinct tokens, separating the agent's bash sandbox capability
from the orchestrator's write capability.

- AGENT_GH_TOKEN (workflow's default GITHUB_TOKEN, scoped to
  contents: read + issues: read) is the only token passed into
  local({ env: { GH_TOKEN: ... } }). The agent's bash can clone the
  repo and read issues, but cannot comment, label, or close anything.
- ORCHESTRATOR_GH_TOKEN (a GitHub App installation token minted by
  actions/create-github-app-token) is read by the TS orchestrator
  from process.env and used for postIssueComment and addLabels. It
  never enters the sandbox env, so a complete jailbreak of the
  agent's bash cannot escalate to comment or label writes.

Workflow changes:
- Job permissions narrowed: contents: read, issues: read (was
  contents: read, issues: write).
- App token minted via actions/create-github-app-token@v3.2.0 with
  the existing APP_ID / APP_PRIVATE_KEY secrets (same pattern
  release.yml and auto-format.yml use).
- Label precreation step now uses the app token; agent step gets
  both tokens but only forwards AGENT_GH_TOKEN to the sandbox.

README threat model rewritten to reflect the new split: lists the
exact capabilities a jailbroken sandbox retains (read-only operations
across the repo + arbitrary runner network egress) versus what is
now structurally impossible (comment, label, branch push, secret
exfiltration of the orchestrator token or AI Gateway credentials).

Also drops three inaccurate 'all model traffic goes through the AI
Gateway' code comments. The gateway is a binding option on
env.AI.run(), not a routing layer.
@ascorbic ascorbic marked this pull request as ready for review May 26, 2026 12:15
Copilot AI review requested due to automatic review settings May 26, 2026 12:15
@ascorbic ascorbic changed the title feat(triage): experimental Flue-based issue triage (phase 1 + 2) feat(triage): experimental Flue-based issue triage May 26, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Comment thread .github/workflows/auto-repro.yml Fixed
zizmor flagged the create-github-app-token step as minting an
unscoped token. By default the action issues a token with every
permission the App's installation has been granted, which is
broader than the orchestrator needs.

Add explicit `repositories: emdash` and `permission-issues: write`
inputs so the minted token can only post comments and apply labels
on this one repo. Read of issue bodies is implied by the
installation; we don't need permission-contents (the workflow's
default GITHUB_TOKEN handles the checkout).
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 19 out of 20 changed files in this pull request and generated 10 comments.

Comment on lines +124 to +128
# title/body cleanly (they may contain quotes, backticks, etc.).
PAYLOAD=$(jq -nc \
--argjson n "$ISSUE_NUMBER" \
--arg t "$ISSUE_TITLE" \
--arg b "$ISSUE_BODY" \
Comment on lines +74 to +82
const result = spawnSync(
"npx",
["flue", "run", "triage-issue", "--target", "node", "--id", id, "--payload", payload],
{
cwd: FLUE_DIR,
env: process.env,
encoding: "utf8",
},
);
Comment thread .flue/scripts/run-local.ts Outdated
Comment on lines +102 to +105
if (apply && !process.env.GITHUB_TOKEN && !process.env.GH_TOKEN) {
console.error("--apply requires GITHUB_TOKEN");
process.exit(2);
}
Comment thread .flue/agents/triage-issue.ts Outdated
Comment on lines +5 to +8
// - GitHub Actions in Phase 2, when a maintainer applies a `triage:run`
// label to an existing issue and we want a fresh classification.
// - The webhook agent (agents/triage-label.ts), which calls into the
// same `classifyIssue` core function after verifying the signature.
Comment thread .flue/agents/triage-label.ts Outdated
Comment on lines +290 to +292
// The deployed Worker uses the Workers AI binding directly (no
// gateway hop) since we're already running on Cloudflare. The
// AI Gateway is for off-CF callers like the GH Actions runner.
Comment thread .flue/app.ts Outdated
Comment on lines +3 to +5
// Registers the Cloudflare Workers AI binding with our AI Gateway so all
// model calls (kimi, etc.) flow through gateway logging + cost tracking,
// same surface bonk.yml and review.yml use. Without this, the binding
Comment thread .flue/lib/github.ts Outdated
Comment on lines +67 to +68
};
if (token) headers.Authorization = `token ${token}`;
Comment thread .flue/README.md Outdated
@@ -0,0 +1,86 @@
# Flue triage experiment

**Status:** prototype, not deployed. See the EmDash Discussion for design context (TBD link).
Comment thread .flue/agents/repro-issue.ts Outdated
Comment on lines +233 to +236
if (repro.suggestedNextStep.trim().length > 0) {
lines.push("");
lines.push(`> ${repro.suggestedNextStep}`);
}
Comment thread .flue/tsconfig.json Outdated
"verbatimModuleSyntax": true,
"noEmit": true,
"allowImportingTsExtensions": false,
"types": ["@cloudflare/workers-types"]
Real bugs:

- workflow: ran 'cd .flue' before 'flue run', which made the local()
  sandbox start in .flue/ -- skills resolved against the wrong cwd
  and 'pnpm test' would have hit the wrong package root. Run from
  the repo root via 'pnpm --dir .flue exec flue run --root .flue',
  and pin agent + sandbox cwd to GITHUB_WORKSPACE explicitly.
- run-local.ts: 'npx flue' could pull a different Flue version than
  the lockfile-pinned one. Swap to 'pnpm exec flue'.
- run-local.ts: --apply accepted either GITHUB_TOKEN or GH_TOKEN but
  the spawned agent only read GITHUB_TOKEN. Normalise to
  GITHUB_TOKEN before spawn so a user-set GH_TOKEN works end-to-end.
- repro-issue.ts: suggestedNextStep was rendered into a blockquote
  without newline-stripping, same bug class as the summary fix in
  triage-label.ts. Add module-scope NEWLINE_RUN_RE and apply it.
- tsconfig.json: explicit 'types' list excluded @types/node, which
  agents and scripts need (process.env, node:child_process, etc.).
  Add 'node' to the types array and declare @types/node in deps.

Doc / comment accuracy:

- triage-issue.ts: comment referenced 'triage:run' label and a
  Phase 2 classification flow that doesn't exist. Replace with the
  accurate description -- Phase 2's repro-issue.ts calls
  classifyIssue() directly.
- triage-label.ts: comment claimed 'no gateway hop' for the deployed
  Worker, but app.ts wires the gateway through env.AI.run when
  CLOUDFLARE_GATEWAY_ID is set. Reword to describe the actual logic.
- app.ts: top-comment over-claimed 'all model calls flow through
  the gateway'; it's conditional on the binding being set. Qualify.
- README: replace 'TBD link' placeholder with the actual PR link.

Style:

- lib/github.ts: switch 'Authorization: token' to 'Authorization:
  Bearer' and add 'X-GitHub-Api-Version: 2022-11-28' to match
  GitHub's current preferred header style and how other workflows
  in this repo call the API.

Build hygiene:

- Add .flue/pnpm-workspace.yaml so pnpm v11's strict build-script
  gate (verifyDepsBeforeRun + strictDepBuilds at the repo root) can
  be satisfied without --ignore-workspace. Drops that flag from the
  README install instructions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants