feat: TDD pipeline — move adversarial_tester before builder in FeatureBench - #1319
Open
colehurwitz wants to merge 33 commits into
Open
feat: TDD pipeline — move adversarial_tester before builder in FeatureBench#1319colehurwitz wants to merge 33 commits into
colehurwitz wants to merge 33 commits into
Conversation
… adapter Upgrades the featurebench workflow from a 4-node pipeline (study → builder → gate_verify → auto_merge) to a 10-node two-loop QA pipeline with code review and adversarial testing before running the full test suite. Adds a FeatureBench agent adapter for integration with the FeatureBench evaluation harness. Workflow changes (10 nodes, 12 edges): - QA loop: builder → code_reviewer → gate_review → adversarial_tester → gate_qa - Test loop: health_checker → gate_tests → builder (RELOOP) - Both loops max 3 iterations via shared builder counter - Fully autonomous — no user gates New files: - factory/featurebench/agent.py — BaseAgent subclass for FeatureBench containers - factory/featurebench/config.toml.example — config template - tests/test_workflow_featurebench.py — 46 tests for workflow + adapter - scripts/run_featurebench_e2e.sh — E2E integration test script Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…te split The script was using --split lite which runs all ~10 tasks and wastes API tokens. Now dynamically extracts the first 2 L1 instance_ids from the lite split and passes them via --task-id for targeted validation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The headless executor's _wait_for_reads() checks completed_files (populated by prior nodes' writes), not the filesystem. Since problem_statement.md is an external input written by the FeatureBench harness before the workflow starts, no node writes it, causing the executor to time out. The prompt_template still references the file — this only removes the executor dependency tracking. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rchivist The builder node declared reads for QA output files (code-review.md, adversarial-qa.md, health-check.md) that only exist on RELOOP iterations. On the first pass, _wait_for_reads() blocked indefinitely since these files hadn't been produced yet. Similarly, the archivist declared reads for all review files, but adversarial-qa.md is skipped when gate_review HALTs. Fix: builder reads only .factory/strategy/current.md (its true first-pass dependency). Archivist reads nothing (it runs last, all files exist by then). The prompt templates still conditionally reference QA files for RELOOP context — the agents read them if present, but the executor no longer blocks on them. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two fixes for the featurebench workflow and agent adapter:
1. Builder prompt: add critical instruction not to create/recreate test
files — the FeatureBench evaluator provides its own tests and
overwrites anything in tests/. Also add notes to researcher and
strategist prompts about removed test files.
2. Agent adapter: support Vertex AI auth alongside direct API key.
- Install gcloud CLI in container setup
- get_env_setup_script handles ANTHROPIC_API_KEY, CLAUDE_CODE_USE_VERTEX,
ANTHROPIC_VERTEX_PROJECT_ID, CLOUD_ML_REGION, and ADC credentials
- config.toml.example shows both auth modes
- E2E script accepts either ANTHROPIC_API_KEY or CLAUDE_CODE_USE_VERTEX,
reads ADC file when using Vertex
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…loning factory repo The adapter previously tried to git clone the private remote-factory repo inside Docker containers, which fails without auth. Simplified to invoke the claude CLI directly in headless mode with --allowedTools and --max-turns. Changes: - Remove git clone + uv sync of remote-factory from install_script - Remove /opt/factory/.venv PATH references from install and env setup - Remove FACTORY_RUNNER env var (no longer using factory CLI) - Replace 'factory workflow run featurebench' with 'claude -p' in get_run_command - Update tests to match new adapter behavior Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The test file is deleted by the benchmark harness before the agent runs, so this instruction was impossible to follow. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ner, comparison) Phase 1: Task split generator (generate_splits.py) - loads 200 tasks from HuggingFace LiberCoders/FeatureBench, stratified-samples 40 by repo, splits 20/10/10 train/val/test into JSONL files. Phase 2: Update FeatureBench SkillOpt adapter with _SPLITS_DIR, _load_split_ids() helper, split loading in setup(), seed-based offset selection in build_train_env(), split-aware build_eval_env(). Phase 3: SKILL.md already up to date (verified via export-skills). Phase 4: run_comparison.sh - shell script to run fb infer + fb eval for baseline and/or factory agents on split tasks. Phase 5: compare_results.py - reads two eval output dirs, produces per-task comparison table, aggregate stats, and differential analysis. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add bench.py that extracts task repos from Docker images, runs the factory featurebench workflow directly, and compares results against the standard FeatureBench baseline agent. Includes CLI interface with --factory-only, --baseline-only, --split, and --skip-eval flags. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…mplate substitution, remove builder test rule
- Add prepare_run() to factory agent to write problem_statement.md into the
container before the workflow starts (was missing entirely, causing agents
to not know what to implement)
- Fix {project_path} template substitution in executor._run_agent() (was
sending literal "{project_path}" strings to agents)
- Skip --dangerously-skip-permissions when running as root in containers
- Remove builder rule 5 (run tests) — health_checker handles testing,
builder was wasting 74 min running astropy's test suite
- Add lite split files (dev/train/val/test)
Run 2 results: 1/4 resolved, 70% avg F2P (up from 0/4, 38.6% in run 1)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Redesign FeatureBench adapter so agent orchestration runs on the HOST with
only builder and health_checker executing inside the container via podman exec.
Phase 1 - Node metadata + executor container routing:
- Add metadata dict field to Node base class in primitives.py
- Add container-aware _run_agent_in_container() and _run_fn_in_container()
methods to WorkflowExecutor that route via podman exec
- Add context dict, pre_node_hook, and post_node_hook to executor
Phase 2 - Redesign workflow to 6-node hybrid pipeline:
- Simplify from 10-node two-loop QA pipeline to 6-node single-loop
- researcher (host) → strategist (host) → builder (container) →
health_checker (container) → gate_tests → archivist (host, async)
- Builder and health_checker marked with metadata={'execution_context': 'container'}
Phase 3 - Adapter refactor:
- Remove Node.js/NVM/Claude Code from container install (minimal install)
- Add _sync_to_container(), _sync_from_container(), _extract_problem_statement()
file sync methods using podman cp
- Simplify env setup (host-side execution needs fewer vars in container)
Phase 4 - Tests:
- Update tests for 6-node structure, container metadata, executor routing
- 79 tests passing across both test files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bench.py: mirror FeatureBench runtime.py initialization (masking patches, F2P test deletion, level detection), exclude .factory/ from patches. Agent.py: add PYTHONUNBUFFERED=1 and stdbuf -oL to fix stale infer.log. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
# Conflicts: # factory/featurebench/agent.py
The hybrid host/container architecture needs FeatureBench harness changes to support host-side orchestration. Revert to the working in-container approach with the 6-node pipeline and PYTHONUNBUFFERED+stdbuf buffering fix. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Override run() in FactoryAgent to execute the workflow graph on the host while routing builder/health_checker into the Docker container via docker exec. Host nodes (researcher, strategist, archivist) use the host's Claude Code directly; container nodes are routed by the executor's execution_context metadata. File sync between host and container is handled via docker cp in pre/post node hooks. Executor changes: make container runtime (docker/podman) configurable via context dict, support env script sourcing and conda env selection. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When fb infer runs from a different working directory, factory.__file__ resolves to site-packages instead of the source repo. The dist/ directory doesn't exist there, causing uv build to fail. Now checks direct_url.json from the package's dist-info metadata to find the original wheel path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Only health_checker remains in the container (needs conda env for tests). Builder now runs on the host for faster file I/O. After builder completes, the full workspace is synced into the container via docker cp before health_checker runs tests. Pipeline: researcher (host) → strategist (host) → builder (host) → health_checker (container) → gate_tests (host) → archivist (host) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add FACTORY_REGIONS env var to round-robin assign Vertex AI regions to concurrent tasks, reducing API rate limit contention. Thread-safe via extra_env on AgentRunRequest (no os.environ mutation). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All git add -A calls inside the container now ensure .factory/ is in .gitignore first, preventing workflow artifacts (reviews, strategy, events) from polluting the patch that FeatureBench evaluates. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ance checks
The health_checker agent was writing wrong RESOLVED verdicts:
- L2 tasks: no test files exist, agent ran smoke tests, wrote RESOLVED:true
- L1 tasks: agent dismissed failures as "pre-existing", wrote RESOLVED:true
This made the RELOOP iteration loop completely dead.
Fix: split gate into deterministic mechanisms:
- L1 (P2P tests exist): gate runs pytest directly via docker exec, exit code
drives PROCEED/RELOOP — no agent judgment in the verdict path
- L2 (no test files): health_checker does spec-compliance validation, gate
checks structured SPEC_COMPLIANCE: PASS/FAIL output
- Health_checker becomes diagnostics-only — never writes RESOLVED verdicts
Also adds _expand_templates() to executor for {container_name} and future
context variables in gate commands and fn node commands.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…eBench Redesign FeatureBench pipeline to use TDD pattern: researcher → strategist → adversarial_tester → builder → gate_tests → archivist The adversarial_tester now writes validation tests from the spec BEFORE the builder implements code. The gate_tests node runs those tests inside the container via docker exec. On failure, the RELOOP edge sends back to builder (not adversarial_tester), so tests are written once and implementation iterates. Key changes: - adversarial_tester reads only researcher-latest.md (not builder output) - adversarial_tester prompt is TDD-focused: spec-only, no builder references - Builder reads pre-written validation tests and adversarial-qa.md - Edges reordered: strategist → adversarial_tester → builder → gate_tests - 32 tests pass covering all node properties, edges, and TDD assertions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sentrux Quality ReportAbsoluteDiff (vs base branch) |
docker cp creates /tmp/validation_tests/validation_tests/ when the target directory already exists from a prior RELOOP iteration. Fix by removing the target first and running pytest from the directory itself. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The benchmark masks function bodies throughout the codebase, not just the main interfaces. The adversarial_tester now scans for blank/stub functions and writes tests for them. The builder now hunts for masked helpers on the first pass, not just during RELOOP iterations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The baseline outperforms TDD because it fills tiny masked helpers (Row.__init__, XMLWriter.data, check_anyuri) in utility files the spec doesn't mention. Adding a dedicated stub_filler node that scans ALL source files for blank function bodies and fills them with minimal implementations before the adversarial_tester writes tests. Pipeline: researcher → strategist → stub_filler → adversarial_tester → builder → gate → archivist Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Keep it generic — no astropy-specific examples like check_anyuri or data(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nically Replace manual file-by-file search with a Python AST walker that finds every function with a blank/stub body across the entire codebase in seconds. The agent then only needs to implement — not discover. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
AST scanner runs as a deterministic gate after stub_filler. If >3 blank function bodies remain, RELOOPs the stub_filler (max 2 iterations). Ensures the builder starts with a fully-filled codebase. Pipeline: researcher → strategist → stub_filler ⇄ gate_stubs → adversarial_tester → builder ⇄ gate_tests → archivist Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
The following files conflict:
Please rebase or merge |
Adversarial tester now runs one-shot only. On RELOOP, gate_tests routes to builder_fix (not back through adversarial_tester), so the test suite stays stable across iterations. V8 showed reloops with adversarial test rewrites caused F2P regressions (astropy 86%→44%). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three fixes for FeatureBench pipeline: 1. scan_stubs now greps for how each blank function is called elsewhere, showing callers' attribute names and arguments (fixes _precision_input vs _precision_flag naming bugs) 2. Builder prompt requires verifying ALL stubs are filled before commit and reporting a checklist (fixes missed files like xml/check.py) 3. Builder timeout 1800s → 3600s to match baseline think time Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous pattern `.{name}` only matched method calls (dot-prefixed),
missing direct function calls and attribute references. Use plain name
grep with directory exclusions and increased context (5 usages).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Skip dunder methods and names < 4 chars (too generic) - Use regex pattern matching .name or name( for precise matches - Exclude docs/, examples/ directories - Reduce from 5 to 3 usages per function - Cuts output from 156KB to 78KB on Lightning workspace Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…t timeout The executor's _execute_gate method never added the gate node's writes to completed_files. When builder_fix declared reads on gate-pytest-output.txt, _wait_for_reads timed out after 60s even though the file existed on disk. Also includes pre/post node hooks for gates, last-line verdict parsing, and max_iterations exhaust → PROCEED fallback. Co-Authored-By: Claude Opus 4.6 <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.
Summary
researcher → strategist → adversarial_tester → builder → gate_tests → archivistKey changes
researcher-latest.md, TDD-focused prompt (spec-only, no builder references)validation_tests/test_spec_compliance.pyandadversarial-qa.mdstrategist → adversarial_tester → builder → gate_testsdocker exec {container_name}for in-container pytest executionTest plan
🤖 Generated with Claude Code