- 4.1 Define success criteria and evaluation signals — expected outcomes, constraints, qualitative and quantitative signals, alignment with intent, automated scanning
- 4.2 Analyze agent failures and identify root causes — logs, plans, traces, outputs, workflow artifacts; reasoning, tool, context and environment causes
- 4.3 Tune agent behaviour based on evaluation results — instructions, workflows and constraints; memory usage; tool usage and access
4.1 Define success criteria and evaluation signals
Specify expected outcomes and operational constraints
Evaluation starts before the agent runs. Write the criteria into the issue, reference them from the pull request, and validate against them. Alongside what must be true, state the operational constraints the work must respect:
| Constraint | Example |
|---|---|
| Scope | Only src/ and lockfiles; no workflow or infrastructure changes |
| Non-goals | No refactoring beyond the fix; no new dependencies |
| Quality | No test deleted or skipped; coverage does not drop |
| Security | No new code scanning alerts; no secrets; no High/Critical dependency vulnerabilities |
| Time | Fits one cloud agent session — the hard limit is 59 minutes |
| Cost | A cap on autopilot continuations or, in Agentic Workflows, max-ai-credits per run |
Identify qualitative and quantitative signals
| Quantitative — measurable, automatable | Qualitative — needs judgement |
|---|---|
| Required checks pass or fail · test pass rate and coverage delta · lint and type errors · new code scanning alerts · secret scanning hits · dependency review findings · number of iterations before green · session duration and AI credits consumed · PRs created versus merged and median time to merge (Copilot usage metrics) | Does the change solve the actual problem? · review comments and requested changes · plan quality and adherence · fit with architecture and conventions · readability and maintainability · clarity of the PR description and evidence · whether the scope was respected |
Quantitative signals scale and can be enforced as gates; qualitative ones catch what no check was written to detect. A healthy evaluation design uses both, and lets the quantitative gates remove the obvious failures so human reviewers spend their attention on the qualitative questions.
flowchart LR ISSUE["Issue<br/>task · acceptance criteria"] --> PR["Agent opens PR<br/>references the criteria"] PR --> RUNS["Workflows run<br/>build · test · lint · scans"] RUNS --> GATE["Required checks<br/>enforced by ruleset"] GATE --> REVIEW["Reviewer approves<br/>qualitative judgement"] REVIEW --> MERGE["Merge"]
Align evaluation criteria with development intent
An evaluation that measures the wrong thing rewards the wrong behaviour. Criteria must describe the intent of the task, and each should map to a signal that can actually observe it.
| Weak criterion | Aligned criterion | Signal |
|---|---|---|
| "Tests pass" | "The vulnerable version is no longer resolvable, including transitively" | Dependency alert closed; dependency review clean |
| "Add tests" | "Branch coverage of auth/ rises above 80%, with no test removed" | Coverage report artifact; diff check on test files |
| "Fix the bug" | "Issue #312 reproduction case passes and is added as a regression test" | New test in the PR; CI result |
| "Improve performance" | "p95 of /search under 200 ms in the benchmark job" | Benchmark artifact compared with the base branch |
Guard the gates themselves
An agent that is judged by tests can make them pass by weakening them. Treat edits to tests, CI workflows and required-check names as high-risk changes — route them to code owners and flag them in review. Renaming or removing a required check silently disables a gate: if checks drift, governance drifts.
Generate evaluation signals with automated scanning
GitHub's security tools produce objective signals that fit straight into a required-check model.
| Tool | Signal | Where it appears |
|---|---|---|
| Code scanning (CodeQL, or third-party SARIF uploads) | Vulnerabilities and coding errors in changed code | PR checks and annotations; Security tab; a ruleset can require code scanning results |
| Secret scanning and push protection | Credentials in code; push protection blocks supported secrets before they land | Alerts; blocked pushes |
| Dependency review | New dependencies with known vulnerabilities or disallowed licences | PR check (fail on a severity threshold) |
| Dependabot alerts | Vulnerable dependencies already in the repository | Security tab; good agent task sources |
| Copilot code review | An AI second opinion on the diff | Review comments on the PR |
name: Security gates
on:
pull_request:
branches: [main]
permissions:
contents: read
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/dependency-review-action@v5
with:
fail-on-severity: high
codeql:
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- uses: actions/checkout@v7
- uses: github/codeql-action/init@v4
with:
languages: javascript-typescript
- uses: github/codeql-action/analyze@v4 Cloud agent's built-in validation
Copilot cloud agent evaluates its own work before finishing, and tries to fix what it finds:
- CodeQL checks the code it generated for security issues.
- Newly added dependencies are checked against the GitHub Advisory Database for malware and for High or Critical CVSS vulnerabilities.
- Secret scanning looks for API keys, tokens and other secrets.
- Copilot code review gives it a second opinion.
This does not require a GitHub Secret Protection, Code Security or Advanced Security licence. Details appear in the session log, and a repository administrator can switch individual tools off under Settings → Copilot → Cloud agent → Validation tools — for example to avoid duplicating another security product. It is a first line of defence, not a replacement for your own required checks.
The agent's self-check is not the gate
Built-in validation makes the agent's output better; required status checks and reviews decide whether it merges. If a question asks how to guarantee that no PR with a new high-severity alert can merge, the answer is a ruleset requiring code scanning results or the CodeQL check — not the agent's built-in CodeQL pass.
4.2 Analyze agent failures and identify root causes
Identify failures with logs, plans, traces, outputs and artifacts
The first step is always to gather the evidence the platform already produced, then compare intent with results: intent lives in the issue and the plan; results live in the commits, outputs and logs.
| Evidence | What it tells you | Where to find it |
|---|---|---|
| Cloud agent session log | The agent's reasoning, each tool call and its result, setup and MCP start-up | Agents page; View session on the PR; the link in each commit |
| The plan | What the agent intended — compare with what it did | PR description, planning session, plan.md |
| PR timeline and diff | What changed, when, and in response to which comment | The pull request |
| Checks tab and workflow logs | Which gate failed, and how | Checks tab; Actions tab |
| Uploaded artifacts | Durable reports that outlive scrolled-away logs | Artifacts section of the run |
| Firewall warnings | Blocked outbound requests and the command that made them | PR body or a comment |
| Hook logs | Tool calls allowed, denied or failed, with arguments | Wherever your hooks write — sent over HTTP for cloud agent, whose filesystem is ephemeral |
| Traces | Spans for each model call and tool call | OpenTelemetry from the Copilot SDK and CLI |
| Audit log | Agent actions across the organization | actor:Copilot filter, with agent_session_id |
Classify root causes
flowchart TB
START["Agent output is wrong or incomplete"] --> Q1{"Did it understand<br/>the task correctly?"}
Q1 -->|"no — wrong goal, ignored criteria"| REASON["Reasoning error<br/>fix: instructions · criteria · plan-first"]
Q1 -->|"yes"| Q2{"Did its tool calls<br/>succeed as intended?"}
Q2 -->|"no — wrong command, misused workflow"| TOOL["Tool misuse<br/>fix: tool allowlist · skills · hooks"]
Q2 -->|"yes"| Q3{"Was its information<br/>current and consistent?"}
Q3 -->|"no — stale PR, conflicting docs"| CONTEXT["Context issue<br/>fix: single source of truth · update branch"]
Q3 -->|"yes"| ENV["Environment issue<br/>fix: setup steps · firewall · secrets · runner"] | Category | Typical symptoms | Usual fix |
|---|---|---|
| Reasoning error | Misread requirements; wrong logic; acceptance criteria ignored; plausible but wrong solution | Sharper acceptance criteria; plan-first review; clearer or more specific instructions; a narrower custom agent |
| Tool misuse | Wrong commands or scripts; misconfigured or untriggered workflows; the wrong MCP tool; destructive commands attempted | Restrict or re-scope tools; add a skill with the right procedure; a preToolUse hook to block misuse; fix workflow triggers |
| Context issue | Outdated PR state; missing earlier decision; conflicting information across artifacts; stale memory | One source of truth; update the branch; update the issue or PR; delete wrong memories |
| Environment issue | Missing dependency or tool; blocked network request; missing secret; wrong runner OS; session timeout; ruleset blocking the push | copilot-setup-steps.yml; firewall allowlist; Agents secrets; runner label; smaller tasks; bypass actor |
Symptom first, not model first
"The model is bad" is almost never the root cause you can act on. A dependency the agent could not install is an environment issue; a test it deleted to make CI pass is a reasoning issue and a missing guardrail. Name the category, then fix the layer that owns it.
4.3 Tune agent behaviour based on evaluation results
flowchart LR RUN["Run the agent"] --> OBSERVE["Observe<br/>logs · traces · artifacts"] OBSERVE --> CLASSIFY["Classify<br/>the root cause"] CLASSIFY --> FIX["Fix one thing<br/>instructions · memory · tools"] FIX --> RERUN["Re-run and compare"] RERUN --> OBSERVE
Revise instructions, workflows or constraints
- Put build, test and validation commands in
.github/copilot-instructions.md— an agent that can validate its own changes produces PRs that merge faster. - Add path-specific instructions where one area keeps going wrong, and say what to do when requirements are ambiguous.
- Tighten a custom agent's prompt and description; split an over-broad agent into narrower ones.
- Make tasks smaller, with explicit acceptance criteria and file directions; move risky work to plan-first.
- Fix the workflow — triggers, required checks, a missing evidence upload.
# .github/copilot-instructions.md
This is a TypeScript monorepo using pnpm workspaces.
## Build and validate before every commit
- Install: pnpm install --frozen-lockfile
- Build: pnpm -r build
- Test: pnpm -r test (all tests must pass; do not skip or delete tests)
- Lint: pnpm lint
## Boundaries
- Never edit files under .github/workflows/ or infra/.
- Database migrations live in packages/db/migrations and are append-only.
- If a requirement is ambiguous, stop and ask in the PR instead of guessing. Refine memory usage
- Delete wrong or misleading Copilot Memory facts (repository owners, Settings → Copilot → Memory).
- Move repeated corrections into instructions so they are authoritative and reviewed.
- Remove conflicting instruction files and keep one source of truth per topic.
- Update the issue or PR description when decisions change, so resumed sessions re-anchor correctly.
Refine tool usage and tool access
| Evaluation finding | Tool-level change |
|---|---|
| Agent kept guessing how to install dependencies | Deterministic copilot-setup-steps.yml |
| Request to an internal registry was blocked | Add the host to the firewall custom allowlist |
| Agent used a write-capable MCP tool it did not need | Allowlist only the read-only tools in the server's tools field |
| Reviewer agent edited code | Remove edit and execute from its profile's tools |
Agent tried rm -rf or git push --force | preToolUse hook denying the pattern; --deny-tool in CLI runs |
| Agent could not read Actions logs to debug CI | Enable the GitHub MCP actions toolset, or add a debugging skill |
| Planning agent was slow and expensive | Fewer tools means a smaller context; give it only read and search |
- name: Summarize the agent run for reviewers
if: always()
run: |
{
echo "## Agent evaluation"
echo "- Commit: ${{ github.sha }}"
echo "- Tests: $(jq -r '.numPassedTests' results.json) passed, $(jq -r '.numFailedTests' results.json) failed"
echo "- Coverage delta: $(cat coverage-delta.txt)"
} >> "$GITHUB_STEP_SUMMARY"
- uses: actions/upload-artifact@v7
if: always()
with:
name: evaluation-${{ github.run_id }}
path: |
results.json
coverage/ Test agent changes before releasing them
Treat a profile change like a code change. Organizations can test a custom agent privately in the
.github/agents folder of their .github-private repository, then release it by moving the
profile to the root agents folder. Every tuning change should land through a pull request, so the
history of why the agent behaves as it does stays reviewable. The lifecycle side of this is on the
multi-agent page.