Retire the pipeline integration testing subsystem (#435) - #437
Conversation
Deletes 2,223 lines that never ran, and replaces the intent with six tests against the canonical compiler and orchestrator. WHY DELETE RATHER THAN REPAIR PipelineTestModel could not be constructed. Its __init__ passed four keyword arguments no core dataclass defines -- ModelCapabilities(max_output_tokens=), ModelRequirements(network_access=), ModelMetrics(tokens_per_second=), ModelCost(input_cost_per_token=). Fixing one revealed the next. 27 tests were permanently red against an API that has never existed. Renaming those arguments was rejected deliberately: it would have made the first layer pass while preserving a second testing architecture -- its own model, provider, validator, scoring system and orchestration facade -- inside the shipped package, with no user-facing consumer. That is the surface #430 exists to retire. Verified, not assumed: - rg for the module names AND every exported symbol returns nothing outside the three files; testing/__init__.py does not import them. - It duplicated tests/test_infrastructure.py, which works. - It shipped fictional openai/gpt-4 and anthropic/claude-* entries to users. - It was built on `mock_responses`, which CONTRIBUTING forbids outright. Git history preserves it; an in-tree archive copy would add noise. WHAT THIS UNCOVERED Routing the replacements through the canonical path immediately exposed three real bugs the parallel stack had been standing in front of: 1. select_model() results were looked up a second time. Five call sites did `get_model(await select_model(...))`, but select_model already returns a Model, so get_model()'s `":" in model_name` raised TypeError. Every registry-selected `action: generate` step failed. 2. create_test_orchestrator() built `models.registry.ModelRegistry`, which has no can_provide_models(); Orchestrator.__init__ calls it, so the helper raised on every call and could not build an orchestrator at all. The two same-named registry classes are #429. 3. The deterministic model advertised "text-generation" while the control system asks for "generate", so it was ineligible for every generate step and selection failed with NoEligibleModelsError. None were visible while a second testing stack stood in for the real one. REPLACEMENT tests/test_pipeline_model_contracts.py -- valid compile, malformed refused, model pipeline executes, structured output, execution failure surfaces, plus a strict xfail pinning #241 (a model step runs but does not validate) so it flips to a visible failure the day the two agree. Test-only models and providers stay under tests/, not in the shipped package. Collection 3215 -> 3193 (-28 retired, +6 added), 0 warnings. Blocking suite 392 -> 397 passed, 1 xfailed. Integration selection unchanged at 12 failed / 28 passed / 9 errors. Rationale recorded in docs/adr/0002-retire-pipeline-integration-infrastructure.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Legacy-suite comparison (CI, same environment)Both from the
The totals reconcile exactly: Among the tests that remain, 53 errors became 39 failures and 14 passes. An "error" here is a collection/setup failure; converting those into real outcomes is the expected effect of fixing What this does and does not proveIt proves the net red count fell by 14 and that no tests vanished unaccounted for. It does not by itself prove that zero individual tests regressed — I am running a test-level diff locally with Suggested follow-upChange the legacy job from |
Test-level regression diff — resolved: no regressionsTwo matched git worktrees, each with
45 looked alarming, so I checked every affected file rather than reasoning from the aggregate. All 45 were
Every one errors on main with: — the exact defect fixed here in Conclusion: 0 regressions. All 45 are error→failure transitions, matching the CI aggregate exactly (errors −53, failed +39, passed +14). Two methodology errors I made getting hereRecording these because both produced confident, wrong intermediate answers:
The underlying lesson is the same one behind the |
The replacement contracts added in #435 asserted only that a step key existed. That passes for a step that failed, returned nothing, or shipped its own unrendered template -- all failure modes this project has had. test_a_model_pipeline_executes now asserts the envelope (success, no error, action), the identity of the selected model, a non-empty text response, and that the pipeline parameter was rendered before the model saw it -- the pipeline now carries a real `{{ topic }}` so the "no unresolved template markers" check cannot pass vacuously. test_structured_output_pipeline_returns_an_object now asserts the payload is a Mapping and validates it against the schema the pipeline itself declares (one Python object feeds both, so they cannot drift). That assertion immediately failed, on a real defect: every other action in this project is spelled with underscores (generate_text, analyze_text, evaluate_condition, loop_complete) but only "generate-structured" dispatched. "generate_structured" fell through to the natural-language branch, which turns an unrecognised action into a *prompt* -- so the step returned a sentence instead of an object and still reported success. Both spellings now dispatch, and the declared supported_actions list matches what is actually implemented. Both are pinned by parametrised tests; removing the alias fails the underscore case. ADR 0001 described the control-flow golden pipeline as covering "conditional branching", which it does not -- the fixture's own header says dependency ordering and parallel fan-out/fan-in. Corrected, with a pointer to #333/#320 for the branching work, and the golden pipeline count corrected from three to four. Blocking suite 397 -> 398 passed (the added parametrised case), 13 skipped, 1 xfailed, 0 failed. Collection 3193 -> 3194, 0 warnings. ruff correctness lint clean on src and on every file touched here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contracts strengthened, and one real defect fell outThe review was right that these tests asserted too little.
The pipeline now carries a real
That assertion failed immediately, on a real bugEvery action in this project is spelled with underscores —
A sentence instead of an object, reported as success. Both spellings now dispatch, ADR 0001 correctedIt described the Numbers
The +1 test and +1 collected item are the second parametrised structured case; measured against this same branch head in the same environment, so the delta is exactly the change. Deliberately not in this PR
|
Closes #435. Deletes 2,223 lines that never ran; replaces the intent with six tests against the canonical compiler and orchestrator. Rationale recorded in
docs/adr/0002-retire-pipeline-integration-infrastructure.md.1. rg proof: no external consumers
src/orchestrator/testing/__init__.pydoes not import them either.2. Why deleted, not repaired
PipelineTestModelcould not be constructed. Four keyword arguments, none of which exist:ModelCapabilities(max_output_tokens=)max_tokensModelRequirements(gpu_memory_gb=0, network_access=)network_accessdoesn't existModelMetrics(tokens_per_second=)ModelCost(input_cost_per_token=, fixed_cost_per_request=)*_per_1k_tokens,base_cost_per_requestFixing one revealed the next. As instructed, I did not mechanically rename them — that would make the first layer pass while preserving a second testing architecture (its own model, provider, validator, scoring system, orchestration facade) inside the shipped package with no user-facing consumer.
3. What this uncovered — three real bugs
Routing the replacements through the canonical path immediately exposed defects the parallel stack had been standing in front of:
select_model()results were looked up twice. Five call sites didget_model(await select_model(...)), butselect_modelalready returns aModel, soget_model()'s":" in model_nameraisedTypeError: argument of type 'Model' is not a container. Every registry-selectedaction: generatestep failed.create_test_orchestrator()built the wrong registry. Two classes share the nameModelRegistry; onlymodels.registryhasregister_provider, onlymodels.model_registryhascan_provide_models— whichOrchestrator.__init__calls. The helper used the first and raised on every call. (The split itself is Consolidate the two parallel model-adapter layers (models/providers vs integrations) #429.)text-generationwhere the control system asks forgenerate— so it was ineligible for every generate step (NoEligibleModelsError).None were visible while a second testing stack stood in for the real one. That is the strongest argument for deleting rather than repairing.
4. Replacement —
tests/test_pipeline_model_contracts.pySix tests, hermetic, using the deterministic model from
tests/test_infrastructure.py, in the blocking contract/e2e layer:test_a_valid_pipeline_compilestest_a_malformed_pipeline_is_refusedtest_a_model_pipeline_executestest_structured_output_pipeline_returns_an_objecttest_execution_failure_is_reported_not_swallowedtest_a_model_pipeline_compilesThat last one is deliberate: a model pipeline runs but does not validate — strict validation resolves
action: generateas a tool name.strict=Truemeans it flips to a visible failure the day #241 is fixed, rather than sitting here forgotten.Test-only models and providers stay under
tests/, not in the shipped package.5. Verification
tests/)Legacy-suite regression comparison is running against a matched
mainbaseline and will be posted as a comment.6. Follow-ups
orchestrator validatepath — not another bespoke stack.🤖 Generated with Claude Code