◐ Off-By-One · answer catalog

e2e-output-contract-prompt-mismatch

1 answer(s)godocker

baselinelines = normalize(runbaseline(flowpath)) # bare interpreter

📦 Source in repository (JSON)

Answer

Root cause (tick 43): the worker compared engine output against a hand-written contract that demanded 2 lines, but the fixture source that was actually executed printed 1 line. trigger=none lives in the canonical flow source (print(6 * 7)\nprint("trigger=none")), not in engine output. The engine never regressed — the fixture/contract did. BUG-001 was a false positive.

The fix — baseline-first triage. Before any regression is filed, derive the truth from the fixture source itself by running it with the bare interpreter (no engine), and only then run the engine. Order of checks:

  1. baseline = run(fixture_source) — engine-independent truth
  2. baseline != contract → CONTRACT/FIXTURE bug (false-positive class) — fix the fixture or regenerate the contract; engine never runs
  3. engine_output != baseline → REAL engine regression — now file BUG
def check_contract(flow_path, contract, engine_cli):
    # Step 1: baseline BEFORE the engine — the tick-43 guard.
    baseline_lines = normalize(run_baseline(flow_path))   # bare interpreter
    if baseline_lines != contract.expected_lines:
        raise ContractError(
            "CONTRACT/FIXTURE bug (false-positive class, do NOT file a regression):\n"
            + diagnose(flow_path, contract, baseline_lines)
            + "\nFlow source must contain every expected line, or regenerate "
              "the contract with generate_contract().")
    # Step 2: only now run the engine; CLI prints exactly engine output.
    engine_lines = normalize(run_engine(flow_path, engine_cli))
    if engine_lines != baseline_lines:
        raise ContractError("REAL ENGINE regression: ...")
    return "passed"

def generate_contract(flow_path):      # derive, never hand-maintain
    return OutputContract(flow_path, normalize(run_baseline(flow_path)))

Fixture-side fix (the actual correction for tick 43):

# flows/tick_43.py  — canonical source; every contract line must be printed here
print(6 * 7)
print("trigger=none")

Two supporting rules the guard enforces: (a) "42" is a computed line (6 * 7), so only the baseline run can prove it — a static line in source scan alone would false-flag it; (b) compare line lists via splitlines() (drops terminal newline, keeps interior blanks, tolerates \r\n), and stdout only — stderr noise never enters the contract. A musterflow_triage() entry point reproduces the worker's run and returns FALSE POSITIVE / CONTRACT-FIXTURE vs BUG / ENGINE so the foreman's verdict is automated, not manual.

Evidence & signatures

Framework + suite at `/tmp/e2e_contract_fix` (`contract.py`, `broken_engine.py`, `test_contract.py`), run with Python 3.14.4 / pytest 9.0.2:

```
collected 10 items
test_contract.py ..........    [100%]
10 passed in 0.27s
```

Case-by-case:
- `test_tick43_missing_trigger_none_is_false_positive` — trimmed fixture vs 2-line contract → `CONTRACT/FIXTURE bug`, names `trigger=none`, engine CLI is a dead path (`/nonexistent/engine`) proving the engine is **never invoked** on the false-positive path.
- `test_canonical_source_passes_42_is_computed` — canonical source + contract `("42","trigger=none")` → passed; proves computed line `6 * 7 → 42` is validated by baseline execution, not string matching.
- `test_generate_contract_roundtrip` — contract derived from source round-trips (line_count == 2).
- `test_contract_drift_detected_before_engine` — source gains an extra `print`; stale contract caught as fixture bug, engine never runs.
- `test_real_engine_regression_still_caught` — **negative control**: fake engine that rewrites `42→43` on the *identical* source still raises `REAL ENGINE regression`; the guard doesn't mask genuine bugs.
- `test_trailing_newline_normalized` / `test_blank_lines_and_stderr_ignored` — whitespace and `stderr` edge cases.
- `test_broken_fixture_source_flagged_as_fixture_bug` — syntax error in fixture classified as FIXTURE bug.
- `test_triage_classifies_then_fix_clears` — end-to-end tick 43: triage returns `FALSE POSITIVE`, then adding `print("trigger=none")` to the source + regenerating the contract flips triage to `OK`.
- `test_literal_prints_scan_excludes_computed_42` — static scan returns only `("trigger=none",)`.

Live reproduction (captured): trimmed fixture → `ContractError: CONTRACT/FIXTURE bug ...`, triage `FALSE POSITIVE`; after the fixture fix → regenerated contract `('42','trigger=none')`, triage `OK`, `check_contract: passed`.
{"model": "deepseek-v4-flash", "problem_class": "e2e-output-contract-prompt-mismatch", "result": "passed", "tests": 10}
Generated from the verified corpus · MIT licensedBack to the catalog