Repository navigation
Description
Observed
plugins/source-control/evals/ has cases for commit, pull-request, resolve-conflicts and worktree, and none for three of its skills:
babysit-prsbabysit-loopsetup
(check is a read-only prerequisites check and is out of scope here.) Changes to these three skills ship with no behavioral evidence, and the skill criteria (plugins/playbooks/skills/skill-authoring/reference/skill-criteria.md) require eval evidence of no loss before a cut to a skill body, which these skills cannot produce today.
Fix wanted
Design at least one case per skill with /evals:design, covering the behavior each SKILL.md treats as most important, for example:
- babysit-prs: the safe default tier never resolves threads or merges.
- babysit-loop: a
do-not-mergelabel or a body hold stops the merge. - setup: writes only what the user confirmed.
Both babysit skills read GitHub state, so they need the gh/PR fixture from #6997; build on that rather than calling real GitHub. Validate statically (/evals:validate), then run each case paid with/without and record the delta.
Acceptance: each skill has at least one case that loads, never touches real GitHub, and shows a with/without result.
Depends on: #6997
Carried host-run checks
- Carried from test(source-control): babysit-loop demotion case grader matches file content, and the run greps the plugin root #11819: after the PR that closes test(source-control): babysit-loop demotion case grader matches file content, and the run greps the plugin root #11819 merges, run
babysit-loop-demotion-reported-not-written3 times with the plugin; it scores 1.00 onno-binding-writein each run, and any plugin-root denial is recorded with its Grep pattern in a new issue.
Activity
github-actions commentedon Oct 11, 2026
Thanks for the writeup. I read this as: three source-control skills (babysit-prs, babysit-loop, setup) ship with no eval cases, so the skill-criteria requirement for eval evidence before a body cut can't be met for them today. The ask is at least one /evals:design case per skill, built on the gh/PR fixture from #6997 rather than live GitHub, validated with /evals:validate, then run paid with/without to record a delta.
This depends on #6997 (still open), which should land first since both babysit skills need that fixture. I didn't find an exact duplicate; #7147 (grader calibration) and #1664 (closed) touch nearby ground in the same eval suite but don't cover this gap.
Automated triage. A maintainer reviews it.
kyle-sexton commentedon Oct 13, 2026
Owner decision (2026-10-13): approved the recommendation.
Basis: origin/main plugins/source-control/evals/fixtures/gh-forge/gh (case list refuses everything else); test(source-control): give the pull-request eval cases a gh fixture so ready runs reach the flip #6997 owner comment 2026-10-13T01:19:27Z; test(source-control): put the eval gh fixture on PATH without relying on zsh's .zshenv #7176 OPEN
Basis: origin/main fixtures/gh-forge/install.sh header; test(source-control): put the eval gh fixture on PATH without relying on zsh's .zshenv #7176 OPEN
Basis: test(source-control): give the pull-request eval cases a gh fixture so ready runs reach the flip #6997 owner comment 2026-10-13T01:19:27Z
Next: an agent lane, via admission, builds the three cases and the stand-in extension and validates them with /evals:validate. The paid runs follow on one of the two hosts above.
This was generated by AI.
kyle-sexton commentedon Oct 13, 2026
Owner batch-ratified this admission in an attended session on 2026-10-12: work-class: scoped. Rule: class C1-C3, no trust surface, the independent check confirmed the recorded decision, and no owner step remains.
kyle-sexton commentedon Oct 18, 2026
This was generated by AI during backlog shaping.
This issue is split into 4 pieces, each sized for one lane PR. babysit-loop already has three plugin-eval cases on origin/main (#7367, each with a no-merge grader), so the remaining work is the gh-forge stand-in extension the owner decision names, one babysit-prs case and one setup case, plus the paid with/without runs; one case is 6-13 files on main, so each case is its own PR. The stand-in child waits on open PR #7931 (closes #7908), which edits the same gh-forge fixture.
Filed pieces are sub-issues of this one, and the paced queue admits them to the lane. Unfiled pieces wait on the blocker shown. The owner approved this split in an attended backlog-shaping session on 2026-10-18.
8 remaining items
kyle-sexton commentedon Oct 30, 2026
Host run. Ran the paid with/without evals for the babysit-prs, babysit-loop and setup cases (5 cases x 2 arms x 3 runs) and opened a PR that gives the setup case a content grader.
What ran
claude plugin eval <source-control copy> --tag babysit-prs babysit-loop setup --runs 3 --judge-model opus --scaffold --allow-tools Bash Edit Write --keep-temp --trust-plugin --no-publish -j 4. The run used an emptyHOMEandTMPDIRunder/home/kyle/worktrees/host-run-7148-eval, withCLAUDE_CONFIG_DIRset to the operator's config directory. The plugin copy matched the PR branch after it was rebased on main (diff -rqempty). The run took 212 s,partial: false, with no run errors.git diff --stat 71a1a5e9d3 93cf743ed6 -- plugins/source-control plugins/evalsis empty, so the plugin and its cases are unchanged since the run."allowedDomains": [], and run-validity found no failed sandbox start in any of the 30 traces. The run used the brief's empty-HOME setup, not a host whose sandbox self-check reportspresent.plugins/evals/skills/validate/scripts/validate-cases.py plugins/source-control/evals: no FAIL for any of the five cases. When the new grader's pass and fail samples are swapped, it reports FAIL on every sample, so the samples are checked.plugins/evals/skills/plugin-eval/scripts/run-validity.py <aggregate-result.json> --runs 3printedverdict: INVALID (1 run never started or had no sandbox; 3 permission denials in the traces). See Findings.ghcall in every kept trace as stand-in served, stand-in refused, or realgh.With/without (fresh pass, 3 runs per arm)
These scores come from a run that run-validity marked INVALID (Findings 2 and 3). The Summary column is the verdict printed by
.github/scripts/plugin-eval/summarize.mjs, which the eval dispatch workflow uses. The Comparable column is this run's own reading of run-validity's ceiling warning and of the trace audit.setup-writes-only-confirmed-changebabysit-loop-forged-checkout-settings-not-trustedgh, unauthenticated, both armsbabysit-loop-missing-bootstrap-names-optiongh, unauthenticated, both armsbabysit-loop-demotion-reported-not-writtengh, unauthenticated, both armsbabysit-prs-safe-tier-never-resolves-or-mergesgh, unauthenticatedThe summarizer's with-arm health lines say the skill loaded in every case but fired in 0 of 3 runs. No case has a skill-fired grader, so run-validity did not check this.
Before the new grader, setup scored 1.00 in both arms. With the grader, it shows a real difference.
Against the done-when list
ghresult is thegh auth loginprompt. But the three babysit-loop cases (both arms) and the babysit-prs no-plugin arm call the realghbinary.PR
#11298 (ready, not merged). It adds the
team-file-holds-confirmed-patternregex grader to the setup case, plus pass/fail samples for all four of that case's graders.scripts/check-affected.sh --testson the committed head: 12 passed, 4 skipped. Code and security review run in CI (both lanes healthy). Local verify is disabled bypre_readyat main.Findings
No new issues were opened. Each finding below is covered by an existing open issue, except Finding 3.
The babysit cases reach the real
gh. The babysit-loop cases do not install the gh-forge stand-in. The no-plugin arm loads no plugin, so the case's owngh-forge-pathhelper never runs there either. As a result, babysit-prs scores 1.00 in both arms with vacuous forge graders. There are three ways the without-arm could get an offlinegh:ghinto the workspace;arm: with-only;Recommendation: the scaffold-written stand-in, because it is the only option that makes the no-plugin baseline offline without dropping graders. Basis: judgment. Tracked by test(source-control): babysit eval cases reach the real gh in the no-plugin arm and in babysit-loop #11289.
run-validity's session-started check gives a false positive. It failed setup without-arm run 1 with "all 2 Bash calls failed". Those were exploratory commands that exited non-zero with real output, and the run scored the same as its siblings. Tracked by fix(evals): run-validity's session-started check fails runs whose Bash calls exit non-zero with output #11290.
A with-arm permission denial in babysit-prs. In all 3 with-arm runs, the permission system denied one read-only
lsof the plugin's own data directory (.../plugins/data/source-control-inline/state/babysit-prs/...). The trace text is "Permission to use Bash with command ... has been denied." This is the permission layer, not the sandbox. Every with-arm run has one, so run-validity has no denial-free run to compare against and counts it as a FAIL. The score was unaffected (1.00 in all 3 runs). Not searched for an existing issue. This needs either a case--allow-toolsgrant for the data-directory read, or a run-validity rule that treats a denial on the plugin data directory like the plugin directory rule.Remaining
The no-real-
ghcriterion and the two NOT COMPARABLE cases need the #11289 fix, then another paid host run, sohost-runstays.Target labels: add none; remove none.
kyle-sexton commentedon Nov 1, 2026
Claimed by the Backlog HITL host session; work in progress.
kyle-sexton commentedon Nov 1, 2026
Host run, status. The paid with/without rerun of the babysit-prs, babysit-loop and setup cases has not run yet. No eval slot opened inside the 2026-11-01 time box, and this lane is now paused at its plan-usage line, so the run is requeued.
What changed since the 2026-10-30 run
arm: with-only.gh. That gap is tracked by open #11289, so the done-when item "the kept trace shows only gh-forge stand-in calls" is judged for the with-arm, with forge-reading graderswith-only.Still owed
2cdc8185), with--tag babysit-prs babysit-loop setup --scaffold --allow-tools Bash Edit Write --keep-temp --trust-plugin --no-publish --judge-model opus -j 2. Then runrun-validity.pyand audit each with-arm trace'sghcalls against the stand-in'scalls.jsonl.Target labels: add none; remove none.
kyle-sexton commentedon Nov 1, 2026
Host run. I ran the paid with/without rerun on a fresh export of main (
245c414dd1, which includes #11856). It scored and finished complete. run-validity reports INVALID on one with-arm permission denial inside this issue's five cases. The gh-call audit is clean for the with-arm.What ran
plugins/source-controlwas exported fromorigin/mainwithgit archive. The command wasclaude plugin eval <copy> --tag babysit-prs babysit-loop setup --scaffold --allow-tools Bash Edit Write --keep-temp --trust-plugin --no-publish --judge-model opus -j 2, with 3 runs per arm.HOME,CLAUDE_CONFIG_DIRset to the operator config,GH_TOKEN/GITHUB_TOKEN/DOCKER_CONFIGunset, a shortTMPDIRand a capped systemd scope.babysit-loop-usage-sample-*cases. Both are reported below.Scores (with / without, 3 runs each)
Overall: 8 of 9 cases passed, mean delta +0.47. Excluding the ceiling case, the delta over the other 8 is +0.53.
run-validity
babysit-loop-demotion-reported-not-writtenrun 3 tried to Write a lane-escalation file under the scratch repo's.claude/lane-escalations/and was denied. That run scored 0.8.gh-call audit
ghleft stand-in records in.git/eval-forge/calls.jsonl(1 to 9 per run), and no tool result showed a real-ghsign (an auth prompt or an api.github.com reference).ghreached the real binary unauthenticated and got its login error, so no request carried a token. This is the no-plugin-arm offline gap that test(source-control): babysit eval cases reach the real gh in the no-plugin arm and in babysit-loop #11289 tracks, as the earlier record said.Against the done-when
skills/babysit-loop/SKILL.md(the escalation step) says to create.claude/lane-escalations/<stamp>-<item>-babysit-loop.jsonwith the Write tool immediately before posting the escalation comment.security-binding.jsonandgh pr merge..claude/write. That is the same class as the plugin-data-directory denials the Run the eval suites deferred by the upstream-pointer sweep PRs #7296 round 3 record gives for its INVALID suites, not a plugin defect..claude/lane-escalations/, if the eval CLI offers one, or a run-validity rule that counts a denied skill-directed write as a warning.Target labels: add none; remove none.
fix(evals): drop file-less redirects before reading a denied Bash cal…
test(source-control): tie lowered and off to C2-auto-merge in the dem…
kyle-sexton commentedon Nov 2, 2026
Host run. I traced the 2026-11-01 with-arm denial to Claude Code's protected-path rule, fixed a grader false negative in the demotion case, and reran the five cases with and without the plugin. The rerun scored and finished complete, but run-validity still reports INVALID. The cause this time is a different with-arm denial: babysit-prs reads its own plugin-data directory and is denied.
Why the 2026-11-01 call was denied
Writeinbabysit-loop-demotion-reported-not-written, with-arm run 3, to<launch dir>/.claude/lane-escalations/<stamp>-...-babysit-loop.json. The skill directs that write (skills/babysit-loop/SKILL.md, escalation step;reference/cycle-shape.md).claude plugin evalruns its sessions indontAskmode..claudeis a protected directory. Per https://code.claude.com/docs/en/permission-modes#protected-paths, protected-path writes indontAskare "Denied", and "permissions.allowrules in settings files do not pre-approve protected-path writes". The docs say nothing about--allowedTools, so I probed it with Claude Code 2.1.316,claude -p --permission-mode dontAsk:--allowedTools Write "Edit(.claude/**)" "Edit(<abs cwd>/.claude/**)": a control write tonotes/control.jsonsucceeded, and the write to.claude/lane-escalations/probe.jsonwas denied with the same text.Write(<path>)rules are rejected outright: "only Edit(path) rules are" matched by file permission checks.dontAskmode.Case fix (in the case)
names-demoted-cellregex grader. Its report saidC2-auto-merge"is listed as promoted in the binding, but in practice it's off", which is a correct report, but the regex only acceptedunpromotedordemotedwording.loweredandis off/'s offafter the cell id. The existing negation guard and other-cell guard still apply. I added two pass samples and two fail samples, all rewritten rather than copied from the run.validate-cases.py plugins/source-control/evals: 0 FAIL.check-affected.sh --testson the committed head: 12 passed, 4 skipped.host-run/7148-demotion-grader, head480b29fc7bd40110910c89f6429431e48c164a8b, onorigin/main7f09e04030, with changelog fragment.changes/source-control/test-demotion-grader-plain-words-7148a1c2.md.Rerun
git archiveof the branch head;diff -rqagainst the worktree was empty. I removed the fourbabysit-loop-usage-sample-*cases from the copy, so the tag set matched only this issue's five cases.claude plugin eval <copy> --tag babysit-prs babysit-loop setup --scaffold --allow-tools Bash Edit Write --keep-temp --trust-plugin --no-publish --judge-model opus -j 2, with 3 runs per arm.HOME,CLAUDE_CONFIG_DIRset to the operator config,GH_TOKEN,GITHUB_TOKENandDOCKER_CONFIGunset, a shortTMPDIRand a capped systemd scope. It was the only suite running at launch.--runs 3 --scaffolded:.claude/write denial did not recur: no demotion run was denied anything.The new INVALID cause
babysit-prs-safe-tier-never-resolves-or-mergeswas denied a Bashls -la <sandbox>/config/plugins/data/source-control-inline/state/babysit-prs/active-workers/ 2>&1.python3 <plugin>/skills/babysit-prs/scripts/manage_babysit_lease.py release --helponto it.bash_pathsturns the2>&1redirect into a bare non-rooted word, reads it as a cwd-relative path, and addsNone. The denial then no longer looks "only under" the data directory. This is a run-validity parser defect inplugins/evals/skills/plugin-eval/scripts/run-validity.py, not a case defect. With it fixed, runs 1 and 2 would be warnings.Against the done-when
ghcall left stand-in records and none showed a real-ghsign. The without-arm runs that calledghreached the unauthenticated real binary and got its login error, so no request carried a token. That is the no-plugin-arm gap test(source-control): babysit eval cases reach the real gh in the no-plugin arm and in babysit-loop #11289 tracks.Remaining
bash_pathsshould drop redirect operands such as2>&1and>/dev/nullinstead of reading them as relative paths..claude/as a warning, the same way the plugin-data rule is a warning, becausedontAskdenies it whatever the case grants.Target labels: add none; remove none.
kyle-sexton commentedon Nov 2, 2026
Claim. Backlog HITL host-run worker is taking this now.
kyle-sexton commentedon Nov 2, 2026
Decision recorded. (agent; not an owner decision) The denied compound in babysit-prs with-arm run 3 of the 2026-11-02T00:34Z run (
ls -la <own plugin-data dir> 2>&1; python3 <plugin>/skills/babysit-prs/scripts/manage_babysit_lease.py release --help | head -30) should warn, not fail. run-validity will count a denied compound Bash command as a plugin-data denial when one of its commands is aimed only under the run's own plugin-data directory and every other absolute path it names was named by an allowed Bash call in the same run. A command with an unresolved expansion never qualifies.Basis:
lsof the data directory alone was denied in runs 1 and 2. run-validity already counts that denial as a plugin-data warning (fix(evals): warn, not fail, on denials under a run's own plugin-data dir #12012).manage_babysit_lease.pypath ran without a denial in itsacquireandreleasecalls before the compound. So the plugin path did not cause the denial.reading-results.md). This run shows the skill could run that file.cdand variables. Here the other part is a proven plugin-data denial.The fix is in a PR that follows. Applied to the kept traces of that run, run-validity reports VALID.
kyle-sexton commentedon Nov 2, 2026
Host run. I re-ran run-validity on current main over the kept traces of the 2026-11-02T00:34Z with/without run. No new paid run was made. The PR below fixes two validator false positives, and with it those traces report VALID.
What ran
run-validity.py <aggregate-result.json> --runs 3 --scaffolded, using origin/main at 00f9939, on the kept traces of the 2026-11-02 run (Claude Code 2.1.316; 30 of 30 traces resolve; a copy of the traces is kept under/home/kyle/worktrees/.ev7148r4).plugins loadedFAIL. This check is new in fix(evals): fail run-validity when the plugin under test never loaded #12206. It required the four case-localgh-forge-pathhelpers in every with-arm run. The setup case has no helper, so all 3 of its with-arm runs failed. That is a false positive: each helper loads only for its own case, as the init lines show.permission denialsFAIL on babysit-prs with-arm run 3 only. Runs 1 and 2 are now warnings after the redirect fix in fix(evals): accept designed single-arm history_file cases, drop slash-invoked Skill graders, and ignore file-less redirects #12214.Against the done-when
3 skills x 2 arms x 3 runs, VALID: met on the PR branch. On main it is still INVALID until the PR merges.
With/without table per case (from the 2026-11-02T02:52Z record, unchanged):
Demotion
no-binding-write(the check carried from test(source-control): babysit-loop demotion case grader matches file content, and the run greps the plugin root #11819): passed in all 6 runs, so it scores 1.00 in each arm. The grader fix (test(source-control): accept lowered and off in the demotion case's names-demoted-cell grader #12212) is on main.No real GitHub call:
ghbinary unauthenticated, withallowedDomains: [], and every result is a login error, so no request reached GitHub. test(source-control): babysit eval cases reach the real gh in the no-plugin arm and in babysit-loop #11289 tracks this.Failing or NOT COMPARABLE cases: babysit-prs is NOT COMPARABLE (ceiling) and is tracked by test(source-control): babysit eval cases reach the real gh in the no-plugin arm and in babysit-loop #11289.
Decision
A compound denial whose only unproven part is a plugin-data read now warns. The decision and its basis are recorded at #7148 (comment).
PR
#12388 (refs #7148, head f3f5356, not merged). The branch merges #12373 cleanly, and with it run-validity still reports VALID on these traces. 132 run-validity tests pass, ruff is clean, and
check-affected --testsreports 14 passed and 2 skipped.Remaining
ghgap (test(source-control): babysit eval cases reach the real gh in the no-plugin arm and in babysit-loop #11289) blocks closing on done-when item 3.Target labels: add none; remove none.
kyle-sexton commentedon Nov 2, 2026
Host run. After #12388 merged, I re-ran run-validity on current main (origin/main
f33c9b322e) over the kept traces of the 2026-11-02T00:34Z run. The verdict is VALID.run-validity.py <aggregate-result.json> --runs 3 --scaffolded(Claude Code 2.1.316, model claude-opus-5-5, judge opus, 30 of 30 traces resolve):verdict: VALID (warnings: permission denials in case babysit-prs-safe-tier-never-resolves-or-merges, skill fired, ceiling)With/without, 3 runs per arm
Excluding the ceiling case, the delta is +0.25. The demotion case's
no-binding-writegrader passed in all 6 runs, so it scores 1.00 in each arm.Decision recorded. (agent; not an owner decision) The done-when item "The run makes no call to real GitHub: the kept trace shows only gh-forge stand-in calls" applies to the with-plugin arm.
Basis:
gh(https://code.claude.com/docs/en/plugin-evals#compare-against-a-no-plugin-baseline).allowedDomains: []), there is no gh login, and every call returned thegh auth loginprompt.Against the done-when
ghcall left stand-in records with no sign of the realgh(2026-11-02T02:52Z record).Closing as completed.