Repository navigation
Turn the implement-ticket agent into a spec, implement, check and review pipeline - #5575
Merged
Marc-André Moreau (mamoreau-devolutions) merged 23 commits intoOct 23, 2026
Conversation
The first agent run only screenshotted that a new control existed, never used it, captured a page still loading, lost a turn to a blocked Stop-Process pipeline, and spent most of its time waiting on builds and formatters. - Add Stop-UniGetUIDev.ps1, which stops UniGetUI by process id, since Copilot CLI refuses `Get-Process | Stop-Process`. - Add Wait-UniGetUIIdle.ps1, which waits until the page has finished loading packages (LoadingProgressBar is out of the UI Automation tree) and the window has stopped changing, and can save a screenshot then. - Keep the shared compiler server for the agent's dev builds and skip restore while project files are unchanged; locally, a rebuild after an edit drops from 33-39s to 15s. - Have the prompt ask for acceptance criteria and assumptions up front, a screenshot of each criterion being exercised through the UI, one formatter pass at the end, and fix its claim that no packages are installed on the runner. - Warn from the export step when code changed without any after-*.png. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Marc-André Moreau (mamoreau-devolutions)
requested review from
a team and
Gabriel Dufresne (GabrielDuf)
as code owners
October 22, 2026 14:49
IronRDP's harness installs the module with Install-Module when it is missing. Installing it first with ModuleFast, pinned to v0.6.1 and to AwakeCoding.PSRemoting 2026.10.21, into the same CurrentUser folder lets the harness skip that install; locally ModuleFast takes about 5s where Save-Module takes 12s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The agent's RDP extension (MSI feature F.Session, Session.Enabled in agent.json) starts DevolutionsSession in each RDP session, which serves the NOW protocol over a virtual channel for remote execution. - Install the Devolutions.Agent winget package, 2026.3.1.0 by default, with ADDLOCAL=F.Agent,F.Session, before the desktop session connects: the agent only starts DevolutionsSession on RDP connect and logon. winget is not on the runner image, so the MSI comes from the package's winget manifest and is checked against the manifest's SHA256. - Check that DevolutionsSession runs in the desktop session, and copy the agent's config and logs into the session artifacts. - An empty devolutions-agent-version input skips both steps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first run with Devolutions Agent showed DevolutionsSession starting in the RDP session and exiting 5 seconds later. It opens the Devolutions::Now::Agent channel and gives up unless NOW negotiation starts within 5 seconds, while ironrdp-agent only negotiates on its first NOW request, so nothing negotiated and the agent never restarted it. - Start-DesktopSession.ps1 -NowChannel runs `ironrdp-agent now capabilities` right after connecting; it waits for the channel and negotiates in time, and the daemon keeps the channel alive afterwards. - The check step now runs a PowerShell command over NOW and checks that it ran in the desktop session, and also collects DevolutionsSession's logs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The second run negotiated in time, but the RDP session dropped on the first NOW message: ironrdp-dvc-pipe-proxy encodes its messages for the channel yet hands them to the client as channel 0, and the client's DvcMessageBatch::try_new rejects the mismatch and ends the session. Devolutions Agent is still installed; the handshake and the remote execution check only run with the new now-channel input set to true. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The agent now gets the avalonia-docs MCP server (https://docs-mcp.avaloniaui.net/mcp) through --additional-mcp-config, and the prompt has it load get_avalonia_expert_rules before writing UI code and look up Avalonia 12 APIs in the docs rather than from memory. It is a public, read-only documentation service and gets no credential. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…publish In run 39806855126 the agent ran a NativeAOT publish twice (about 6 minutes). Its Release restore rewrote project.assets.json without the Debug-only diagnostics package, and Start-UniGetUIDev.ps1 then skipped restore because no project file had changed, so the next Debug build failed. - The restore stamp also records each project's project.assets.json timestamp, so any other restore forces a fresh one. - The prompt tells the agent not to run dotnet publish; the PR's CI checks the NativeAOT build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Swatinem/rust-cache saved about 400 MB per branch (77s at job end), and with the repository's Actions cache at its 10 GB limit that evicted the cached ironrdp-agent and NuGet packages, so run 39862220431 rebuilt ironrdp-agent and restored NuGet from scratch. The built agent is already cached by IronRDP commit, so the Rust cache only shortened the rare full rebuild (about 9 minutes without it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… agent workflow Turns the implement-ticket workflow into a pipeline around the agent: - Spec stage: mode spec (or the agent:spec label, or "/agent spec" on an issue) has an agent write a spec with acceptance criteria and a UI sketch, posted on the issue; "/agent implement" (or spec_run_id) implements the approved spec. - Checks after the agent (Test-AgentChange.ps1): paths, build, the tests of projects that reference the change, formatting of the changed files, and new translation keys, reported in the PR. - Review by a second agent on a different model (REVIEW_PROMPT.md), with its verdict and findings in the PR. - Screenshot evidence (New-ScreenshotEvidence.ps1): taskbar cropped, each before/after step pair outlined in red, nearly identical pairs flagged. - Triggers: agent:spec / agent:implement labels, "/agent ..." comments on issues, "/agent <feedback>" on agent PRs to iterate on them, all limited to users with write access (Resolve-AgentRequest.ps1); results are posted back on the issue. - Invoke-CopilotAgent.ps1 runs every agent with JSON output and records the model, Copilot usage and what the agent ran; the report adds the phase timings. agent-eval.yml replays merged PRs and scores them. - NuGet packages are restored from main's cache without saving one per agent branch; Invoke-DesktopNowCommand runs commands over NOW once the desktop session's now-channel input is on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new workflow can only be dispatched once it is on the default branch. To be removed before merging. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A called workflow sees its caller's event, so a push-triggered eval skipped every job. A run with an artifact prefix is a workflow call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oxy fix The desktop-session action now applies ironrdp-patches/*.patch on top of ironrdp-ref before building ironrdp-agent, and keys the agent's cache on them. 0001 is IronRDP 2cc70912, not merged upstream yet: the DVC pipe proxy reported channel 0 for the NOW messages it sent, and the client dropped the RDP session. With it, the agent workflow turns now-channel on, so DevolutionsSession's NOW channel is negotiated and checked, and the agent can use Invoke-DesktopNowCommand. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In eval run 39893339409 the agent wrote its description next to the ticket instead of to $env:AGENT_PR_BODY, so the report and the review had none. The prompt now names that file and the screenshots folder. The report is also uploaded and printed, and the review tries Sonnet before Opus (34 AI credits for one review against 4.6 for the change). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Eval run 39893339409 (case 5561) passed through it; from the default branch the eval is started by hand. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In run 39899544626 UniGetUI.Tui.Tests crashed in its test host after 104 of 225 tests (Avalonia CreateWindow), where run 39893563369 had passed them. They take about five minutes; the pull request's CI runs them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The review moves into the desktop job, after the checks and the screenshot evidence, so its findings can be fixed where the app runs: - REVIEW_PROMPT.md asks for structured findings (id, severity, file, line, details, fix) with an implementable flag: local, one clear resolution, no design or spec decision. - Invoke-AgentReview.ps1 runs the reviewer on a different model against the working tree and puts the implementer's change back if the review touched it. - Select-AgentFixes.ps1 picks, without an agent, the failed checks and the implementable blocker, major and minor findings. - One fix round (FIX_PROMPT.md) fixes only those, on the same desktop, updates the description and reports each item fixed or not. The checks and the evidence then run again; the first round is kept in before-fix/. There is no second review or second fix round. - The report lists each problem with its outcome, what was left for the maintainer, the checks before the fix, and the fixer's metrics; the eval counts cases with no blocker or major finding left unfixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In run 39958994732 the NOW handshake succeeded but the check's command failed with "NOW operation stream closed before a terminal event", and DevolutionsSession logged nothing after the handshake; the same check passed in run 39893563369. The command is now tried three times; if it still fails, the job continues with DESKTOP_SESSION_NOW=0 and a warning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
IronRDP#2209 (10199db3) merged the fix that ironrdp-patches/0001 carried: the pipe proxy now sends NOW messages on their own channel. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The report job ran after cancelled run 39969885850 and failed to download the change it never got. It now runs only after an implement job that succeeded or failed, tolerates a missing change, and the report script no longer trips over an empty fix request in strict mode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… runs Following https://awakecoding.com/posts/speeding-up-dotnet-builds-on-windows-nuget-cache/: on Windows runners NuGet's package and HTTP caches default to C:, the slow OS disk. NUGET_PACKAGES and NUGET_HTTP_CACHE_PATH now point under RUNNER_TEMP on D: for the whole job (base build, the agents' builds and tests, the checks). The NuGet cache restored from main is dropped: every run restores from scratch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
build-release, dotnet-test, cli-headless-e2e and the unigetui-build action already keep NUGET_PACKAGES in the workspace, on D: on Windows runners. NUGET_HTTP_CACHE_PATH now goes beside it in .nuget/http-cache instead of defaulting to C:. Their NuGet caches are unchanged and still cover only .nuget/packages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NUGET_PACKAGES and NUGET_HTTP_CACHE_PATH move to the workflow-level env of build-release, dotnet-test and cli-headless-e2e, and desktop-smoke gets them too; the unigetui-build action no longer sets its own and uses its caller's. dotnet-test keeps its exact path, so its NuGet cache still matches; build-release's Windows build job now uses the forward slashes of its other jobs, so its cache misses once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Marc-André Moreau (mamoreau-devolutions)
merged commit Oct 23, 2026
1f139e5
into
main
6 checks passed
Marc-André Moreau (mamoreau-devolutions)
deleted the
claude/ai-factory-workflow-analysis-f8a3d5
branch
October 23, 2026 19:10
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The first run of the implement-ticket agent (run 39783250637) produced a plausible change. But:
This turns the workflow into a pipeline around the agent.
What changes
The agent's own run
Stop-UniGetUIDev.ps1replaces a stop command that Copilot CLI refuses to run.Wait-UniGetUIIdle.ps1waits for the page to finish loading (through UI Automation) before a screenshot.PROMPT.mdasks for acceptance criteria, before/after screenshot pairs that look visibly different, and one formatter pass. It also bansdotnet publish.mcp-config.json).The pipeline (
agent-implement-ticket.yml)resolve (no agent): turns the trigger into a request and acknowledges it on the issue or PR (
Resolve-AgentRequest.ps1). It handles:agent:specoragent:implementlabel,/agent specor/agent implement [guidance]on an issue,/agent <feedback>on an agent PR.Labels and comments only start a run for users with write access.
spec (Linux): an agent writes a spec with behavior, an ASCII UI sketch, acceptance criteria and the alternatives it considered (
SPEC_PROMPT.md). spec-publish posts it on the issue;/agent implement(orspec_run_id) implements the approved spec.implement (Windows desktop): the agent, then:
Test-AgentChange.ps1): no.github/changes, the build, the tests of projects that reference the change, formatting of the changed files, and new strings missing fromlang_en.json;New-ScreenshotEvidence.ps1): crops the taskbar, outlines in red what changed betweenafter-Na/after-Nb, and flags pairs that are nearly identical.review and fix round (on the same Windows desktop, inside the implement job):
claude-sonnet-5.5, thenclaude-opus-5.5,gpt-6-luna) reviews the change in place (Invoke-AgentReview.ps1,REVIEW_PROMPT.md).implementablewhen the fix is local, has one clear resolution and needs no design or spec decision.Select-AgentFixes.ps1picks, without an agent, the failed checks and the implementable blocker, major and minor findings.FIX_PROMPT.md) fixes only those, reports each one fixed or not, and updates the description. The checks and evidence then run again; the first round is kept inbefore-fix/.report (
New-AgentReport.ps1):Metrics:
Invoke-CopilotAgent.ps1runs every agent with JSON output. It records the modelautopicked, the AI credits, the tool calls, the builds, tests, waits, clicks and screenshots, and the MCP calls. The report adds the time spent in each phase.Older starting commits: a run from an older commit (an eval case, a PR iteration) uses this workflow's own
.github/agentand.github/actions, committed locally so the agent's patch leaves them out.Eval (
agent-eval.yml).github/agent/eval/cases.json: Let the package details action button grow with its translated label #5561, Count failed operations in the operations panel header #5567 and Ask for confirmation before ignoring the updates of several packages #5556) from the commit before each one, with only the problem as the ticket.Desktop session
AwakeCoding.PSRemotingis installed with ModuleFast.Devolutions.Agent2026.3.1.0) is installed with its RDP extension (F.Session).now-channelinput, the action negotiates NOW right after connecting and checks that a PowerShell command runs in the desktop session over NOW.Invoke-DesktopNowCommandexposes that to the agent.ironrdp-refpoints at the merge of Devolutions/IronRDP#2209 (10199db3). That fixes the DVC pipe proxy, which handed its NOW messages to the client as channel 0 and dropped the RDP session.ironrdp-patches/can carry upstream fixes ahead of a merge; it's empty now.DESKTOP_SESSION_NOW=0) and warns. One run failed with "NOW operation stream closed before a terminal event" right after the handshake.mainwithout saving its own copy.Tested in CI
gpt-6-luna, 93s, 1.33 credits) wrote a spec with an ASCII sketch and 8 acceptance criteria.Agent: covered all 8 criteria with 41 waits, 39 clicks and 0 sleeps.
Checks: caught 4 strings missing from
lang_en.json.Review: found 4 real major bugs (updates hidden for undiscovered sources, an error state that can't appear, "Select all" leaving a manager indeterminate, the missing strings).
Evidence: flagged a nearly identical pair.
The Sonnet review approved with minor findings.
The report artifact has the phase timings (setup 1m13s, base build 1m42s, agent 9m20s, checks 7m20s).
The Sonnet review approved with 5 minor findings, 3 marked implementable.
The fix round fixed all 3 in 7m18s (including 6 new unit tests: 873 → 879).
R1 (a design choice) and R5 (missing evidence for the loading, empty and error states) were left for the maintainer.
The checks passed before and after; the reviewer guard didn't trigger.
10199db3without the patchironrdp-agentbuilt from the merge commit.NOW negotiated, and the check command ran on the first attempt.
desktop-smoke launched UniGetUI and took clean screenshots.
The checks passed (870 and 225 tests).
The review requested changes (a real split-button height bug).
The agent changed the same single file as the human PR.
The scorer wrote the report table.
Locally:
--additional-mcp-config.Not tested yet
issuesandissue_commentworkflows from the default branch, so they start working after merge. The resolver logic is tested locally, and its dispatch equivalents (spec_run_id,continue_run_id) ran in CI.agent-eval.ymlcan only be started by hand from the default branch. Its CI run used a temporary push trigger that has since been removed.create_proff.agent:specandagent:implementlabels don't exist in the repository yet.UniGetUI.Tui.Testsunless the TUI changed (its test host crashed once on the runner). That rule was tested locally, not in CI.🤖 Generated with Claude Code