fix(run): hide framework-staged files from the codebase's own tooling (#296) - #302
Merged
Conversation
…#296) Staged skills live inside the task repository, under the harness's skills dir. A codebase whose lint or format step globs the whole tree therefore reported eval-magic's own artifacts as project failures — and only in the arm that stages a skill, because the control arm has nothing to find. That is asymmetric by construction. Any `command_check` running the project's checks was biased against `with_skill`, and worse, the agent under test runs those same checks: it saw a permanently red step listing files it did not create and must not touch. A run meant to measure honest self-verification was measuring the framework instead. So `run` now writes a delimited block into the project's own ignore files, naming the paths the runner placed: # >>> eval-magic framework files >>> /.eval-magic-outputs/ /.claude/skills/ /.claude/settings.local.json # <<< eval-magic framework files <<< The paths come from the selected harness descriptor — `skills_dir` and the file its guard stages (`hooks_file`, or `plugin_file` for a plugin engine) — plus the framework outputs dir, so a BYOH harness contributes its own without configuring anything. The block is written into every environment: both arms, every repetition, `--no-stage` and `--dry-run` alike. An entry present in one arm only would trade one asymmetry for another. It lands before the baseline commit, so it never appears in a run's diff-scope. Which ignore files receive it is detected from the codebase's tooling through a new packaged `ignore-profiles/` family, embedded by `build.rs` the way `guard-profiles/` already is: prettier, eslint, stylelint, markdownlint, and docker, each declaring its markers and whether its file may be created when the project has none. ESLint's may not — ESLint 9's flat config no longer reads `.eslintignore`, so creating one would be inert. Both profile families now detect through one walk, `src/core/tree_profiles.rs`. `codebase.ignore_files` names the files outright when detection cannot find them; `[]` opts out. Paths may not escape the environment. `.gitignore` is deliberately never a target. The baseline force-adds harness config dirs, so an entry there would hide nothing from Git — but it would hide the staged skills from every `.gitignore`-aware tool the agent uses, such as `rg`, damaging the treatment arm instead of protecting it. Nothing records the effective list in an artifact either: the written file is committed into each environment's baseline, so `git show refs/eval-magic/baseline:.prettierignore` is the evidence, per arm and per run. Before / after, the shipped Weeknight fixture (`.prettierrc.json`, no `.prettierignore`), `npm run lint` in `env-g1-with_skill`: [warn] .claude/skills/slow-powers-eval-1-with_skill__demo/SKILL.md [warn] Code style issues found in the above file. exit 1 All matched files use Prettier code style! exit 0 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018H2oD4ry5huonXVyrpDGFj
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.
Closes #296.
The problem
Staged skills live inside the task repository, under the harness's skills dir. A codebase whose lint or format step globs the whole tree therefore reported eval-magic's own artifacts as project failures — and only in the arm that stages a skill, because the control arm has nothing to find.
That is asymmetric by construction: any
command_checkrunning the project's checks was biased againstwith_skill. Worse, the agent under test runs those same checks, so it saw a permanently red step listing files it did not create and must not touch. A run meant to measure honest self-verification was measuring the framework instead.The change
runwrites a delimited block into the project's own ignore files, naming the paths the runner placed:skills_dirplus the file the guard stages (hooks_file, orplugin_filefor a plugin engine), plus the framework outputs dir. A BYOH harness contributes its own without configuring anything.--no-stageand--dry-runalike. An entry present in one arm only would trade one asymmetry for another. It lands before the baseline commit, so it never appears in a run's diff-scope.ignore-profiles/family, embedded bybuild.rsthe wayguard-profiles/already is. Each profile declares its markers and whether its file may be created when the project has none — ESLint's may not, since ESLint 9's flat config no longer reads.eslintignore.src/core/tree_profiles.rs.codebase.ignore_filesnames the files outright when detection cannot find them;[]opts out. Paths may not escape the environment..gitignoreis deliberately never a target: the baseline force-adds harness config dirs, so an entry there would hide nothing from Git — but it would hide the staged skills from every.gitignore-aware tool the agent uses, such asrg, damaging the treatment arm instead of protecting it.No artifact records the effective list. The written file is committed into each environment's baseline, so
git show refs/eval-magic/baseline:.prettierignoreis the evidence, per arm and per run.runalso prints the files it wrote.Before / after
The shipped Weeknight fixture (
.prettierrc.json, no.prettierignoreof its own),npm run lintinenv-g1-with_skill:Both arms carry a byte-identical
.prettierignore, and the control arm still passes.Schema / CLI / docs
schema/evals.schema.json—ignore_fileson both codebase shapes.eval-magic run --help— a paragraph beside theexclude_skill_sourcesnote.eval-magic docs codebase— new "Framework files stay out of the project's own tooling" section: the detected set, the override, the.gitignorerationale, and where the evidence lives.eval-magic docs byohandharnesses/template.toml— one line telling descriptor authors thatskills_dirand the guard's staged file are what the entries derive from.docs/developer_overview.md— repository-map entry forignore-profiles/.Verification
Plus the end-to-end run above against the real fixture, driven through
runonclaude-code.New coverage: unit tests for the shared detection walk and for the writer (creation, idempotent rewrite,
create_if_missing = false, declared-list override, opt-out, a directory in the way, symlinks inside and escaping the environment, packaged-profile integrity); validation tests forignore_filescontainment;tests/run/ignore_files.rsfor arm symmetry, baseline membership, the declared list, the opt-out, and a codebase with no matching tooling; and a docs contract test.🤖 Generated with Claude Code
https://claude.ai/code/session_018H2oD4ry5huonXVyrpDGFj