refactor(action)!: eight inputs, none of them a CLI flag, plus an unlisted evalboard source for ad-hoc GH runs - #147
Draft
bai-uipath wants to merge 7 commits into
Draft
refactor(action)!: eight inputs, none of them a CLI flag, plus an unlisted evalboard source for ad-hoc GH runs#147bai-uipath wants to merge 7 commits into
bai-uipath wants to merge 7 commits into
Conversation
…se and args inputs The composite action had no way to run from a subdirectory, install an agent extra, or put a plugin in the environment it invokes, which is what kept every real consumer on a hand-rolled `uv pip install` + `coder-eval run` instead. `working-directory` applies to both of the action's steps. It is the only way in: GitHub rejects `working-directory:` on a `uses:` step, and a job-level `defaults.run` does not reach inside a composite action. `extras` composes into the requirement string rather than installing afterwards, and `extra-packages` maps to `uv tool install --with`. Both exist because that install builds an isolated environment whose shims shadow every other coder-eval on PATH, so neither an extra nor a plugin added beside it is ever imported by the CLI the action runs. `prerelease` passes `--prerelease=allow` for when either needs a prerelease to resolve. `args` takes one argument per line and appends each verbatim. `extra-args` is deliberately word-split, which also means pathname-expanded, so a `-D` override whose value is a bracketed list (`key=[A,B,C]`, a bash character class) was intact only while no file in the working directory happened to match it. A single file named `key=A` silently rewrote a three-name list to one name and the run measured something other than what the workflow asked for. tests/test_action_inputs.py executes both step scripts pulled straight out of action.yml, with uv and coder-eval stubbed to record their argv, so the assertions are about the text that ships rather than a copy of it. The action-dogfood job then covers the two things a unit test cannot reach: `working-directory` on a composite step, and a plugin installed via `extra-packages` actually being discovered at runtime. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…runs UiPath/skills' `run-coder-eval` workflow_dispatch produces debug runs that today survive only as a downloadable artifact zip. This gives them a dashboard link, without putting them anywhere they can be mistaken for nightly history. Registered but deliberately unlisted: no tab, no listing page, no aggregate view. `NAV` in app/layout.tsx is a hardcoded array and does not iterate `SOURCES`, so registration and surfacing are independent, and app/runs/[id] reads by id on demand — a fresh link resolves with no listing in existence. Registration in `SOURCES` is still mandatory, because `sourceById` is the only path by which a container becomes reachable, and it coerces an unknown id to the default source rather than throwing, so without an entry `?src=gha` would quietly read the skills nightly's container and 404. Its own container rather than an `adhoc-` prefix inside `runs`, for two reasons. `getAdhocRunListing` loads per-run metadata for every non-date-shaped id in a container before truncating to the front page's limit, so a stream of dispatches would bury the intentional ad-hoc runs and cost a blob load each. And the storage account carries a lifecycle rule that deletes these after 14 days; sharing a container would put months of nightly history behind it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…l-spec tests `test_local_installs_the_action_checkout` failed on the Windows smoke job: Git Bash converts an argument that looks like an absolute POSIX path into Windows form on the way to a native binary, so `CE_ACTION_PATH=/action-checkout` reached the recording stub as `C:/Program Files/Git/action-checkout`. Nothing to do with the action — the same mangling would hit any test that asserts on argv through `shell: bash` on a Windows runner. MSYS2_ARG_CONV_EXCL and MSYS_NO_PATHCONV turn the conversion off, and are ignored on POSIX. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The argv-recording stub had a `#!/usr/bin/env python3` shebang, which makes every stub invocation cross the MSYS-to-native boundary on a Windows runner. Git Bash rewrites arguments that look like absolute POSIX paths on the way across it, so `/action-checkout` reached the stub as `C:/Program Files/Git/action-checkout`. Switching the conversion off with `MSYS2_ARG_CONV_EXCL` only moved the failure: the shebang launcher then could not hand python its own script path either, and all 21 tests in the file failed instead of one. A bash stub never crosses that boundary, so argv arrives byte-for-byte on every platform and no environment switches are needed. argv is now recorded NUL-delimited rather than as JSON, so a value carrying a quote, a backslash or a space needs no escaping on the way out of bash. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`coder-eval run` has 21 flags. The action promoted five of them to named inputs with no principle behind the choice: `--tags` got one, its sibling `--exclude-tags` did not, and `extra-args`'s own description admitted it covered "--tags exclusions". A forwarding input buys nothing and costs a lot, because GitHub silently IGNORES an input the referenced tag does not define, so one that is mistyped or newer than the consumer's pin yields a run that measured something else and still exits 0. A wrong CLI flag is a hard error instead. So the surface is now eight inputs, none of which is a CLI flag. An input exists only where the action does something with the value besides pass it along: `version`/`extras`/`extra-packages`/`install-flags` compose the install spec, `working-directory` is applied to the action's own steps, `env` is exported into its shell, and `run-dir` is read back for the outputs. Removed: `tasks`, `tags`, `model` and `extra-args` all fold into `args`. `prerelease` generalises to `install-flags`, which also covers a private index. `junit-path` is derived as `<run-dir>/junit.xml`, which is where both existing consumers already put it. `step-summary` and its write are replaced by a `run-md-path` output, because a consumer that has to redact the report first cannot undo a write that already happened. `minimum-task-score` and its embedded Python are gone; both call sites set it to `0.0`, a no-op, and a score floor is policy over `run.json` that belongs in the CLI where it is unit-testable and reachable from ADO. Folding task globs into `args` removes a documented hazard rather than adding one. They now reach the CLI unexpanded and `expand_task_files` handles them, so `**` works without `globstar`, and a glob matching nothing exits 1 instead of arriving as a literal path. Three "sharp edges" in the docs describing the old shell-expansion behaviour are deleted. The four line-list parsers collapse to one `clean_lines`, copied into both step scripts because they are separate bash processes, with a test asserting the copies stay byte-identical. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comments only, no behavior change. action.yml goes 291 -> 245 lines (71 -> 41 comment lines): the `author:` justification and the boxed INPUT DESIGN banner go, the forwarding-input rule keeps six lines, and every input description and inline comment is cut to its contract. The dogfood `with:` block in pr-checks.yml had 30 comment lines around 20 lines of YAML, now 12. GHA_SOURCE's preamble drops to the 4-6 line shape the two sources above it already use. Also reflows the ragged comment left in verify-published-action.yml by the previous commit's edit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three surfaces still described the deleted inputs, found while trimming comments: - The `ci` skill told the agent to pass an experiment via `extra-args:`, so an emitted workflow would have had its experiment silently ignored — GitHub drops an input the tag does not declare. Now `-e` and the path as two `args` lines. The "a path containing a space is unsafe there" caveat goes with it: `args` entries are verbatim. - docs/CI_GATE.md carried "Extras and plugins" twice, the second copy predating the rewrite and still naming `prerelease`. Its frontmatter and intro also still promised a per-task score floor and a job-summary write. - test_ci_skill_does_not_recommend_a_recursive_task_glob enforced the opposite of the new behavior: it banned `**` in a `tasks:` value and required the skill to explain `globstar`. It passed only because the rewritten skill contains the phrase "no `globstar` caveat". Removed; CE026's unknown-input check already covers a stale `tasks:` in a snippet. test_ci_skill_covers_experiments_and_pins asserted `"extra-args" in text`; retargeted to a standalone `-e` token, since the substring also matches inside "coder-eval". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Upstream half of giving
UiPath/skills'run-coder-evaldispatch a real evalboard link while moving it onto the published composite action. Along the way the action's input surface got rewritten rather than extended. The skills-side PR consumes it and is blocked on this one being merged and released, sinceuses: UiPath/coder_eval@v0reads its inputs from the action at that tag.Why the input surface changed instead of growing
The action had no real consumers, only this repo's own
action-dogfoodjob and the synthetic e2e inverify-published-action.yml, so a bad release surfaced in a self-test rather than in someone's workflow. Makingrun-coder-evala real dependent meant adding inputs for a suite in a subdirectory, an agent extra, and a plugin inside the environment the CLI runs from. Five new inputs took the count to fifteen, and at that point the shape of the thing was the problem, not the gaps.coder-eval runhas 21 flags. The action promoted five of them to named inputs with no principle behind the choice:--tagsgot one, its sibling--exclude-tagsdid not, andextra-args's own description admitted it covered "--tagsexclusions". A forwarding input buys nothing and costs a lot, because GitHub silently ignores an input the referenced tag does not define. One that is mistyped, or newer than the consumer's pin, yields a run that measured something else and still exits 0. A wrong CLI flag is a hard error instead.The surface: 15 inputs to 8, none of them a CLI flag
An input now exists only where the action does something with the value besides pass it along.
version,extras,extra-packages,install-flagsworking-directoryuses:step, and job-leveldefaults.rundoes not reach into a compositeenv$GITHUB_ENVargsrun-dirtasks,tags,model,extra-argsargsprereleaseinstall-flags, which also covers--extra-index-urlfor a private indexjunit-path<run-dir>/junit.xml, which is where both existing consumers already put itstep-summaryrun-md-pathoutput. A consumer that has to redact the report first cannot undo a write that already happened, so the write is the consumer's call.cat "$RUN_MD" >> "$GITHUB_STEP_SUMMARY"is the whole of the default this replacesminimum-task-scoreFour different encodings (whitespace-split, line-delimited, comma-separated,
NAME=VALUE) collapse to oneclean_linesparser, copied into both step scripts because they are separate bash processes, with a test asserting the copies stay byte-identical.Folding task globs into
argsremoves a hazardThey now reach the CLI unexpanded and
expand_task_fileshandles them. Verified directly:So three documented "sharp edges" are deleted rather than reworded:
**silently degrading withglobstaroff (which was silently dropping every top-level task), a non-matching glob reaching the CLI as a literal path, and the need for an explicit per-depth ladder.The other correctness win is the one that motivated
argsin the first place, reproduced with onetouch:[...]is a bash character class. Silent, and it changes what the run measures. That path no longer exists.The one capability this removes
minimum-task-scoreand its ~50 lines of embedded Python are gone with no replacement. Both call sites set it to"0.0", a no-op, so it had no real user. It is also in the wrong place: a score floor is policy overrun.json, so it belongs in the CLI as--min-task-score, where it is unit-testable and reachable from the ADO pipelines and the nightly VM, which cannot use a GitHub action at all and reimplement their own gating today. Small follow-up, deliberately not in this PR.Evalboard side
A third source,
gha-> containerruns-gha, registered and deliberately unlisted: no tab, no listing page, no aggregate view. Registration and surfacing turn out to be independent (NAVis a hardcoded array that does not iterateSOURCES), andapp/runs/[id]reads by id on demand, so a direct link resolves with no listing in existence. That is what lets this skip the one piece of evalboard work with a documented footgun.Its own container rather than a prefix inside
runs, becausegetAdhocRunListingloads per-run metadata for every non-date-shaped id before truncating to the display limit, and because the storage account now carries a 14-day expiry rule scoped toruns-gha/that must never be able to reach nightly history.Three stale surfaces found while trimming the comments
Each one described an input that no longer exists, and each was invisible to the pinning lint:
ciskill still told the agent to pass an experiment viaextra-args:, so an emitted workflow would have had its experiment silently ignored — exactly the forwarding-input failure this PR is about, aimed at the workflows the plugin writes. Now-eand the path as twoargslines.docs/CI_GATE.mdcarried "Extras and plugins" twice; the second copy predated the rewrite and still namedprerelease. Its frontmatter and intro also still promised a per-task score floor and a job-summary write.test_ci_skill_does_not_recommend_a_recursive_task_globenforced the opposite of the new behaviour: it banned**and required the skill to explainglobstar. It was passing only because the rewritten skill contains the phrase "noglobstarcaveat". Removed — CE026's unknown-input check already covers a staletasks:in a snippet.After merging
Two dispatches, in this order, before the skills PR can do anything:
release.yml), which bumps the version, re-pinsaction.yml, and movesv0. Untilv0moves, a consumer passing these inputs gets them silently ignored. Note the reverse window too: between this merge and that release,verify-published-action.ymlonmainpasses the newwith:block to the old@v0, so its nightly runs the same single task on the default model instead of haiku. Cheap, not broken, but a reason to release promptly.deploy-evalboard.ymlincoder_eval_uipath(manual dispatch, no post-deploy health check). Theghasource only exists on the deployed site once that runs, so sequence it before the first upload or the emitted link 404s on arrival.Verification
tests/test_action_inputs.py(37 cases) executes both step scripts pulled out of action.yml withuv/coder-evalstubbed to record argv, so it asserts the text that ships rather than a copy. Mutation-checked: flattening the junit derivation fails 5 of them./action-checkouton the Windows runner.action.ymldoes not declare, caught a surface I had missed (plugins/coder-eval/skills/ci/SKILL.md). Worth noting the rule earned its keep.action-dogfoodcovers what a unit test cannot:working-directoryon a composite step, a plugin installed throughextra-packagesbeing discovered at runtime, and now therun-md-pathsummary-append recipe the docs tell consumers to use. The discovery probe is acoder-eval planon a task naming the BYOA fixture's agent kind, which exits 1 withNo agent registered for type 'byoa-demo'if the entry point was not found.tsc --noEmit,next build, ruff and pyright clean. actionlint findings unchanged against baseline on every workflow touched.🤖 Generated with Claude Code