test(uipath-maestro-case): add outcome-based e2e graded on third-party effects - #2524
test(uipath-maestro-case): add outcome-based e2e graded on third-party effects#2524song-zhao-25 wants to merge 4 commits into
Conversation
|
Claude finished @song-zhao-25's task in 2m 36s —— View job PR Review: test(uipath-maestro-case): add outcome-based e2e graded on third-party effects
SummaryAdds Change-by-Change Review1.
|
| Area | Status | Notes |
|---|---|---|
| Frontmatter | N/A | Test task, not a skill — no SKILL.md frontmatter |
| E2E Tests | OK | Proper tags, task_id convention, weighted criteria, passing run documented |
| Skill Body | N/A | No skill changes |
| References & Assets | OK | sdd-template.md is well-structured, all paths resolve |
| Repo Hygiene | OK | CODEOWNERS present, no secrets, no personal paths, scoped changes |
Issues for Manual Review
- Sandbox connection health: The task depends on two live Integration Service connections (
dd657127…for Outlook,f5273a4d…for Jira) and Jira projectSJPwith issue type10506. These are documented as tenant preconditions (YAML header, lines 1–21). Verify these fixtures are stable and monitored — the graders correctly abort as ENVIRONMENT gaps, but frequent connection revocations would make this test flaky. - Re-run claim: The PR body notes that the exact committed state has not been re-run (three non-grading changes —
max_iterations,task_timeoutbump, README section — were added after the passing run). The author offers to add replicates. Given these changes are demonstrably non-grading, this is low risk, but a clean re-run before merge would eliminate doubt. - Jira issue accumulation: One issue per run accumulates permanently in
SJP. The PR acknowledges this and notes it's harmless to correctness. Worth tracking whether the sandbox owner can grant delete permission.
Conclusion
Clean, well-designed PR. The outcome-based grading approach is thoughtfully implemented — the per-run token, order-independent graders with exclusive locking, generous polling, anti-gaming guards, and >= 1 assertion semantics all address real failure modes documented with evidence. The README addition extracts these lessons into a reusable pattern. The code is clear, the YAML follows conventions, and no issues require changes.
Approve — no blocking issues found.
|
Claude finished @song-zhao-25's task in 2m 0s —— View job Coder-eval task lint (advisory)1 task YAML changed; verdict counts: 0 Critical, 0 High, 0 Medium, 0 Low, 1 OK. Rubric: .claude/commands/lint-task.md. This check is advisory and never blocks merge. Evidence of passing run
Per-task lint
|
5e54542 to
f943194
Compare
f943194 to
310f893
Compare
…y effects Grades whether the case ACHIEVED ITS BUSINESS OBJECTIVE in the systems it touches, rather than whether it was built a particular way: an Outlook 365 email must reach the approver's mailbox and a Jira issue must exist as the audit record. Topology is deliberately not graded, `case validate` passing is advisory, and reaching finalStatus=Completed carries less weight than the effects themselves — a case can complete while notifying nobody. The graders read the target systems back through the same Integration Service connections the case uses, so no third-party credentials are needed in CI. Design notes worth keeping: - Per-run token. `uip maestro case debug` has no --input flag, so pre_run renders sdd-template.md into the sandbox with a fresh token baked in and records it in seed.json. Both target sandboxes are shared and noisy, so the probes match this run's token instead of "the newest record", which also keeps parallel replicates independent. - Order-independent execution. coder_eval does not guarantee criterion order, so ensure_debug_ran() runs the case once per task behind an exclusive lock and republishes the payload to the other graders. - Timeout tolerance. `case debug` abandons polling at 600s while the case keeps running; a run was measured landing its effects ~7 min later. The checker follows the instance to a terminal state instead of failing on the timeout, and the probes poll ~10 min because Graph's subject filter lags delivery. - Anti-gaming. Both effects are reachable straight from the shell, so direct mutating connector calls and self-run `case debug` are disqualifying. - Effects are asserted as >= 1, not exactly once: a stray extra case run otherwise fails a case that met the objective. Verified: coder-eval run 2026-08-07_16-42-17 -> 7/7 criteria, score 1.000, SUCCESS in 816s, with the email and Jira issue confirmed in the live systems. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
310f893 to
2ba5148
Compare
An isolated review by a second agent (codex, gpt-5.6-sol) found five issues in 2ba5148. All five verified and fixed. 1. Attribution (critical). The anti-gaming criteria only match Bash commands starting with `uip`/`$UIP`, so an agent could wrap the connector calls in a helper script, run `python3 helper.py`, build a trivially-completing case, and pass every criterion. The probes now discard any record that predates the harness-owned debug start, which is published to debug_result.json — anything the agent produced necessarily predates it, since debug runs only after the agent finishes. `_to_epoch` normalizes Graph's `…Z` and Jira's `…+0300` so both compare against that floor; a 120 s negative skew allowance covers foreign clocks, and an unknown floor fails open. 2. Jira cleanup never ran. `JIRA_CONN` was used but not imported — a NameError swallowed by the broad handler, so every successful run left its issue open. Latent because the branch only executes when there IS an issue to close, which dry runs never had; the earlier manual verification used an ad-hoc script rather than this one. Imported, and the handler now prints a traceback so the next such bug is visible. 3. Stranded debug lock. `_do_debug()` raising (its `fail()` exits) left the lock in place with no published result, so every peer grader waited out PEER_WAIT_SECONDS and reported a timeout instead of the real error. Lock is now released in `finally` and a failure result is published before re-raising. 4. Environment gaps were still scored as skill failures. coder_eval has no skip semantics: `precondition_failed`'s message is only a triage aid, since any non-zero exit fails the criterion. Added a connection health check to `pre_run` so a revoked connection errors the run before an agent run is spent, and corrected the docstring's claim. 5. `max_iterations: 1` did nothing. `coder-eval plan` reports it as an unknown top-level field that is ignored, so it documented a guarantee it did not provide. Removed, with a comment recording why and what to do if a supported retry control appears. (~10 sibling case tasks carry the same no-op field.) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Replicate run 1 failed for a timing bug, not a test-logic bug: check_case_ran's worst case (600 s CLI debug poll + INSTANCE_WAIT_SECONDS 1200 s) equalled the criterion timeout exactly, so coder_eval killed the grader at 1800 s. A killed grader publishes no result and its `finally` never runs, so the lock stranded and the next criterion launched a SECOND concurrent debug run. - INSTANCE_WAIT_SECONDS 1200 -> 600 and the criterion timeout 1800 -> 2400, so the slow path (1200 s) finishes well inside the wall. - Stale-lock takeover: a lock older than STALE_LOCK_SECONDS (1500 s) had its owner killed, so a later grader claims it instead of waiting out PEER_WAIT_SECONDS and reporting a timeout that hides the real failure. - The lock file now carries the ISO instant its owner started, and a takeover REUSES that instant as the attribution floor. Re-stamping "now" would slide the floor forward and discard effects the first debug run legitimately produced. Also corrects the SDD's saveAsDraft rationale. It claimed an unbound input "leaves this task incomplete so the case never reaches Stage 2"; a run today stalled at exactly that point with saveAsDraft bound to false, so the stall is platform behavior, not a consequence of drafting. The binding requirement stands on the vendor's documented default, which is what the note now says. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ency The tenant is shared, so "newest Studio Web Debug instance in this window" is not us. Measured in the 19:09 run: two instances started 87 s apart — ours (2c6c7726, trigger_1, stalled) and a stranger's (cd683370, trigger_Wz1hgs) which reached Completed. Following the newest would have scored their success as ours. Candidates are now fingerprinted against the stage ids of the caseplan the agent built (Stage_aR4mK9 …). Those are randomly minted per build, unlike trigger ids — the skill often emits the literal `trigger_1`, which any case can carry — and the runtime names per-stage elements after them (stageSlaEventSubprocess_<stageId>). Known limit, recorded rather than papered over: an instance that stalls BEFORE entering its first stage runs only generic elements and its PackageId is minted server-side, appearing nowhere on disk. Such an instance cannot be told apart from a stranger's, so the follow-up refuses. That is the safe direction — this criterion is the execution vehicle at weight 2.0, while the outcome probes are token-scoped and unambiguous, so they still report whether the case did its job. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
What
Adds
tests/tasks/uipath-maestro-case/e2e_outcome_external/— an e2e task that grades whether a case achieved its business objective in the systems it touches, not whether it was built a particular way.The agent gets an approved SDD for a two-stage expense-approval case. Two things must be true afterwards:
both carrying a per-run reference token. The harness executes the case, then queries Microsoft Graph and Atlassian to confirm both effects landed.
Why
Acceptance for this work was defined as: "it achieved the business objectives as measured in the third party systems. Not if it validated, not if it debugged, but that it accomplished what the user was after — the coding agents can do this a number of different ways."
So the weighting is deliberate:
case debugitselfuip maestro case validatepassespass_threshold: 0)Caseplan topology and stage/task shape are not graded — any design that achieves the objective passes. Completing is worth less than the effects because a case can complete while notifying nobody.
This is additive: it does not touch
e2e_expense_runnableor any existing task.No new CI secrets
The graders read each target system back through the same Integration Service connection the case uses (
uip is resources run list …). The HTTP call hits the vendor API for real; auth is the tenant's existing connection.Verification
Local
coder-evalruns against alpha/codereval:2026-08-07_16-05-282026-08-07_16-42-172026-08-07_19-09-092026-08-07_20-14-15Grader evidence from the passing run:
Platform caveat, stated plainly. Case execution on the eval tenant was intermittently
unreliable while developing this: 3 of 7 runs stalled after starting (instance runs its
scaffolding elements, never enters Stage 1, raises no incident), then the identical build
completed in 56 s minutes later. The stalls are not caused by the build, the skill, or this
test — the same caseplan both stalls and passes. When it happens the test reports it
accurately, which is the intended behavior, but expect occasional red runs that are
platform-caused until that is addressed.
The first run's failure was informative rather than incidental — see below.
Design notes (each one earned by a failure)
uip maestro case debughas no--inputflag, sopre_runrenderssdd-template.mdinto the sandbox with a fresh token baked in and records it inseed.json. Both target sandboxes are shared and noisy (the mailbox took unrelated mail mid-run), so probes match this run's token rather than "the newest record" — which also keeps parallel replicates independent.case debugabandons polling at a fixed 600 s while the case keeps running. In run 1 the effects landed ~7 min after the CLI gave up, which is exactly why it scored 0.333. The checker now follows the instance to a terminal state instead of failing on the timeout, and probes poll ~10 min because Graph's subject filter lags delivery.ensure_debug_ran()executes it once behind an exclusive lock and republishes the payload. With-j > 1it refuses to guess when several debug instances fall in the same window, rather than risk attaching to a sibling task's success.>= 1, not exactly once. A stray extra execution otherwise fails a case that met the objective; duplicates are reported instead.command_not_executedguards. Read-onlyis resources run liststays allowed because the skill uses it to resolve reference fields.max_iterations: 1. A retry re-executes the case and duplicates external records; it does not recover the run.Reviewer notes / known trade-offs
dd657127…, Jiraf5273a4d…) plus Jira projectSJPwith issue type10506. If one disappears the graders abort withtest precondition failed: … ENVIRONMENT gaprather than looking like a skill regression.saveToSentItems: true, so each run leaves two copies). Jira issues cannot be deleted — the sandbox account gets 403 inSJP— sopost_runtransitions them to Done instead. Rows persist one per run, but the project's open-issue list stays clean. Worth requesting delete permission from the sandbox owner if the row count ever matters.smoke-skills.ymlruns--tags smoke; this ise2e, so it runs undermake e2e(sequential by default).🤖 Generated with Claude Code