[IMP-15 실행-3] classifier consumer (image + table) + pure-dict test #47
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Parent: #15 (IMP-15 Step 14 visual_check 보강)
Source plan: Claude #6 plan u2 (classifier extension) + u5 (pure-dict classifier tests)
Depends on: 실행-1 + 실행-2 (image_events + table_events arrays must exist in run_overflow_check result)
Scope (axis 3: classifier consumer + visual_check_passed)
Extend
classify_visual_runtime_checkto consume image_events + table_eventsand flip
visual_check_passedwhen any classification fires (even if zone-level overflow was clean).Touched files
src/phase_z2_classifier.py— consumer + visual_check_passed semantic widenedtests/phase_z2/test_phase_z2_visual_classifier.py— NEW (pure-dict, no Selenium)Tests required
test_image_aspect_mismatch_emits_classification: delta > 0.05 → classification + visual_check_passed=Falsetest_image_aspect_delta_below_tol_no_classification: delta < 0.05 → no classificationtest_standalone_table_overflow_emits_classification: table self-overflow → "tabular_overflow"test_table_dedup_when_wrapper_clipped: wrapper_clipped_index set → no "tabular_overflow" (dedup honored)Acceptance criteria
classificationslist includesimage_aspect_mismatch/tabular_overflowwhen triggeredvisual_check_passed = overflow.passed AND (not classifications)(intentional flip)IMAGE_ASPECT_DELTA_TOLimported fromphase_z2_pipeline(single source of truth)Out-of-scope
Estimated size
검증 요구사항 (strict):
이 이슈의 구현 변경은 같은 이슈 안에서 검증되어야 합니다.
tests: []단위는 허용되지 않습니다 (구현 + 테스트 = 같은 이슈).[Claude #1] Stage 1 — IMP-15 실행-3 classifier consumer (axis 3).
Scope-lock
Single axis:
classify_visual_runtime_checkconsumesimage_events[]+table_events[];visual_check_passedflips when ANY classification fires (even on clean zone overflow). pipeline.py 변경 X. router action / debug.json passthrough = out-of-scope.Verified preconditions (실행-1/2 landed)
IMAGE_ASPECT_DELTA_TOL=0.05phase_z2_pipeline.py:134— classifier imports (SSoT).TABLE_SCROLL_TOL_PX=5phase_z2_pipeline.py:139— same SSoT rule (issue body names only image TOL; same principle for table).image_events[]phase_z2_pipeline.py:2239-2280.delta is None⇒ skip (image not loaded, no false positive).table_events[]phase_z2_pipeline.py:2287-2325.wrapper_clipped_index is not None⇒ skip (clipped_inner dedup).Current classifier (pre-change)
classify_visual_runtime_checkphase_z2_classifier.py:336setsvisual_check_passed=overflow.passedand EARLY-RETURNS at L367-375 on passed=True. Reads onlyoverflow.zones[].clipped_inner+ zone-self. Never touches new event arrays. Early-return must be removed so image/table scans still run when zone overflow is clean.tabular_overflowcategory exists L234 (§3.2 priority 2) — table consumer reuses verbatim.image_aspect_mismatchis NEW (not in §3.2 cascade). Aspect-ratio violation ⇒ emit as independent classification entry.Acceptance criteria (verbatim)
image_aspect_mismatchwhendelta is not None AND |delta|>IMAGE_ASPECT_DELTA_TOL.tabular_overflowwhenwrapper_clipped_index is None AND (excess_x>TOL OR excess_y>TOL).visual_check_passed = overflow.passed AND (not classifications).Guardrails
placement_diagnosticsshape preserved.tests/phase_z2/test_phase_z2_visual_classifier.py(no collision, confirmed).=== EVIDENCE ===
phase_z2_classifier.pyfull (1-457) — early-return L367-375, 8-rule cascade L226-285.phase_z2_pipeline.py:120-149,2230-2410— TOL constants + event arrays + dedup pattern.classify_visual_runtime_checkin src — 3 callers in pipeline only (L4521/4579/4640) ⇒ semantic widening safe.tests/phase_z2/**/*.py— target test file does not exist.FINAL_CONSENSUS: YES
[Codex #1] Stage 1 problem-review for issue #47 (IMP-15 exec-3 classifier consumer).
Scope-lock
Confirmed. This issue is limited to
classify_visual_runtime_checkconsuming already-producedimage_events[]andtable_events[], plus wideningvisual_check_passedto fail when any classifier event is emitted.pipeline.pypassthrough/production of those arrays is already present and is out of scope for this issue. Router behavior and debug.json passthrough remain out of scope.Verified current state
src/phase_z2_pipeline.pydefinesIMAGE_ASPECT_DELTA_TOL = 0.05andTABLE_SCROLL_TOL_PX = 5; classifier should import from there for a single source of truth.run_overflow_checkreturnsimage_eventsandtable_eventsin its result object.phase_z2_pipeline.pyalready uses image delta and table overflow for fail reasons, but the classifier does not consume those arrays yet.src/phase_z2_classifier.pycurrently returns immediately whenoverflow.get("passed", False)is true, so clean zone overflow prevents image/table classification from firing.overflow.zones[].overflowedandoverflow.zones[].clipped_inner; no reads ofimage_eventsortable_eventsexist in the classifier.tabular_overflowalready exists in the classifier cascade and should be reused for table self-overflow.image_aspect_mismatchis not present in the classifier yet and must be added as a new classification category.tests/phase_z2/test_phase_z2_visual_classifier.pydoes not exist, so the requested pure-dict unit test file is missing.Stage 1 conclusion
The issue body and prior scope report are consistent with the repository state. Required implementation scope is correctly locked to
src/phase_z2_classifier.pyplus new pure-dict tests undertests/phase_z2/test_phase_z2_visual_classifier.py. No pytest was run, per Stage 1 instructions.=== EVIDENCE ===
Commands run:
rg -n "IMAGE_ASPECT_DELTA_TOL|TABLE_SCROLL_TOL_PX|image_events|table_events|classify_visual_runtime_check|tabular_overflow|visual_check_passed" src testsGet-ChildItem -LiteralPath 'D:\ad-hoc\kei\design_agent\tests\phase_z2' -ForceGet-Content -LiteralPath 'D:\ad-hoc\kei\design_agent\src\phase_z2_classifier.py' | Select-Object -Skip 210 -First 260Get-Content -LiteralPath 'D:\ad-hoc\kei\design_agent\src\phase_z2_pipeline.py' | Select-Object -Skip 120 -First 30Get-Content -LiteralPath 'D:\ad-hoc\kei\design_agent\src\phase_z2_pipeline.py' | Select-Object -Skip 2230 -First 110Files checked:
D:\ad-hoc\kei\design_agent\src\phase_z2_classifier.pyD:\ad-hoc\kei\design_agent\src\phase_z2_pipeline.pyD:\ad-hoc\kei\design_agent\tests\phase_z2Test results: not run; Stage 1 explicitly says NO pytest.
Verified facts:
IMAGE_ASPECT_DELTA_TOL = 0.05atsrc\phase_z2_pipeline.py:134.TABLE_SCROLL_TOL_PX = 5atsrc\phase_z2_pipeline.py:139.image_eventsandtable_eventsare included in therun_overflow_checkreturn object atsrc\phase_z2_pipeline.py:2327.classify_visual_runtime_checkstarts atsrc\phase_z2_classifier.py:336and currently early-returns on passed overflow atsrc\phase_z2_classifier.py:367-375.tabular_overflowcategory is emitted byclassify_overflowatsrc\phase_z2_classifier.py:233-239.tests\phase_z2\test_phase_z2_visual_classifier.pyis absent from the directory listing.FINAL_CONSENSUS: YES
📌 [Claude #1] Stage 2: 시뮬 기반 계획 수립 Round #1
Execution-3 child issue. Scope locked by Stage 1 exit report (issue body + parent #15). Single axis: classifier consumer extension +
visual_check_passedsemantic widening. Plan = 2 atomic units.=== IMPLEMENTATION_UNITS ===
summary: Extend classify_visual_runtime_check to consume image_events + table_events; widen visual_check_passed; remove overflow.passed early-return
files: [src/phase_z2_classifier.py]
tests: [tests/phase_z2/test_phase_z2_visual_classifier.py]
estimate_lines: 45
summary: NEW pure-dict test file — 4 cases (image delta>tol fires; image delta<tol no-fire; table self-overflow fires; table dedup when wrapper_clipped_index set)
files: [tests/phase_z2/test_phase_z2_visual_classifier.py]
tests: [tests/phase_z2/test_phase_z2_visual_classifier.py]
estimate_lines: 45
=== PER-UNIT RATIONALE ===
u1 —
src/phase_z2_classifier.pyIMAGE_ASPECT_DELTA_TOL,TABLE_SCROLL_TOL_PXfromphase_z2_pipeline(SSoT — no literal duplication; per Stage 1 guardrail).placement_diagnosticsbuild stays at L358-365 (passed-invariant, preserved).overflow.get("image_events", []). Skip whenev["delta"] is None(image not loaded — Stage 1 guardrail). Emitimage_aspect_mismatchclassification whenabs(delta) > IMAGE_ASPECT_DELTA_TOL. Setsource="image_event",zone_position,zone_template_id,delta,natural_ratio,rendered_ratio,src.overflow.get("table_events", []). Skip whenev["wrapper_clipped_index"] is not None(dedup with existing clipped_inner — Stage 1 guardrail). Emittabular_overflow(reuse §3.2 priority-2 category verbatim, L234) whenexcess_x > TABLE_SCROLL_TOL_PX or excess_y > TABLE_SCROLL_TOL_PX. Setsource="table_event",zone_position,zone_template_id,excess_x,excess_y.visual_check_passed = overflow.get("passed", False) and not classifications. Existing branch at L367-375 now also runs scans; ifclassificationsnon-empty whenoverflow.passed=True, return shape mirrors the failed branch (categories_seen / summary populated, unclassified_signals=[]).placement_diagnosticsin both branches. Preserve existing 8-rule cascade output for zone_self / clipped_inner sources (untouched).u2 —
tests/phase_z2/test_phase_z2_visual_classifier.py(NEW)overflowdict withpassed=True+zones=[]+image_events=[…]/table_events=[…]arrays. Pass emptydebug_zones=[].test_image_aspect_mismatch_emits_classification— delta=0.30 → 1 classification, category=image_aspect_mismatch, source=image_event,visual_check_passed=False.test_image_aspect_delta_below_tol_no_classification— delta=0.02 → 0 classifications,visual_check_passed=True.test_standalone_table_overflow_emits_classification— excess_x=20, wrapper_clipped_index=None → 1 classification, category=tabular_overflow, source=table_event,visual_check_passed=False.test_table_dedup_when_wrapper_clipped— excess_x=20, wrapper_clipped_index=3 → 0 classifications (dedup honored),visual_check_passed=True.=== OUT-OF-SCOPE (Stage 1 lock, restated) ===
pipeline.pyproduction of image_events / table_events (already landed 실행-1/2).image_aspect_mismatch(FUTURE follow-up issue).debug.jsonpassthrough (→ 실행-4).placement_diagnosticsshape (untouched).=== ROLLBACK ===
Single commit per unit.
git revert <sha>restores prior classifier semantics; NEW test file removal is no-op for production.=== EVIDENCE ===
FINAL_CONSENSUS: YES
[Codex #1] Stage 2: simulation-plan review Round #1
=== IMPLEMENTATION_UNITS ===
summary: Extend classify_visual_runtime_check to consume image_events and table_events, remove passed early-return, and widen visual_check_passed semantics
files: [src/phase_z2_classifier.py]
tests: [tests/phase_z2/test_phase_z2_visual_classifier.py]
estimate_lines: 45
summary: Add pure-dict classifier tests for image aspect mismatch, below-tolerance image delta, standalone table overflow, and wrapper-clipped table dedup
files: [tests/phase_z2/test_phase_z2_visual_classifier.py]
tests: [tests/phase_z2/test_phase_z2_visual_classifier.py]
estimate_lines: 45
u1 rationale:
u2 rationale:
Out-of-scope notes:
Rollback strategy:
=== EVIDENCE ===
Commands run:
Files checked:
Test results:
Verified facts:
FINAL_CONSENSUS: YES
Executing unit: u1
📌 [Claude #2] Stage 3 (코드 수정) Round #1 — u1 (classifier consumer) 완료
■ unit_executed:
u1(Stage 2 plan — Extend classify_visual_runtime_check)■ files_changed:
src/phase_z2_classifier.py(+61 / −11)■ diff_summary
Deferred import (avoid circular) —
from phase_z2_pipeline import IMAGE_ASPECT_DELTA_TOL, TABLE_SCROLL_TOL_PXadded insideclassify_visual_runtime_check. Module-level import would be circular:phase_z2_pipeline.py:57imports this module, and the tolerance constants are defined atphase_z2_pipeline.py:134/:139(after that import line). Deferred to call time so both modules are fully loaded. Single source of truth — no literal duplication.Removed early-return on
overflow.passed=True(wasphase_z2_classifier.py:367-375). image/table scans must run unconditionally; replaced with a 3-line comment block citing IMP-15 실행-3.Added image_events scan — emits
image_aspect_mismatchclassification whendelta is not None AND |delta| > IMAGE_ASPECT_DELTA_TOL. Each classification carriescategory,source="image_event",zone_position,zone_template_id,src,natural_ratio,rendered_ratio,delta,rule_applied(formatted with actual delta + tolerance).delta is None⇒ skip (image not loaded; no false positive).Added table_events scan — reuses existing
tabular_overflowcategory verbatim (phase_z2_classifier.py:234, spec §3.2 priority 2) whenwrapper_clipped_index is None AND (excess_x > TOL OR excess_y > TOL). Each classification carriescategory,source="table_event",zone_position,zone_template_id,excess_x,excess_y,rule_applied.wrapper_clipped_index is not None⇒ skip (dedup with existingclipped_innercascade; honors producer-side contract fromphase_z2_pipeline.py:2287-2325).Widened
visual_check_passedsemantic — final return now computesvisual_check_passed = bool(overflow.get("passed", False)) and not classifications. Any image_aspect_mismatch or tabular_overflow from the new emitters flips the gate to False even when zone overflow is clean.No changes to:
placement_diagnosticsshape — unchanged.pipeline.pyproduction of image_events / table_events — out-of-scope (already landed in 실행-1/2).■ smoke verification (pure-dict, no Selenium)
Confirms (a) early-return removed (would have short-circuited to True before), (b) image_events consumed, (c) visual_check_passed widening fires. Formal pytest cases land in u2.
■ remaining_units:
[u2]— pure-dict tests for 4 acceptance cases (image fires / image below-tol / table fires / table wrapper-clipped dedup).■ follow_up_issue_candidates: none — scope-lock holds. Router action mapping for
image_aspect_mismatchis already deferred to a FUTURE follow-up per issue body (out-of-scope this issue), and debug.json passthrough is 실행-4.■ scope adherence
■ diff size note
Plan estimated +45 lines; actual +61 / −11 (net +50). Overage is concentrated in the two classification-dict literals (multi-line for readability) and the deferred-import comment. Logic remains a single unit (u1) — splitting further would fragment the consumer extension axis.
■ next
STOP. Awaiting Codex review on u1 before Round #2 (u2 pure-dict tests).
No commit / push performed in this round (per Stage 3 protocol).
[Codex #1]
Stage 3 code-edit verification for executed unit
u1is complete.Verdict:
u1is correct for the scope executed in this round.Verified facts:
src/phase_z2_classifier.pyremoves the early return foroverflow.passed=True, so image/table event scans run even when zone-level overflow is clean.phase_z2_pipelineat call time:IMAGE_ASPECT_DELTA_TOLandTABLE_SCROLL_TOL_PX. This preserves the single source of truth while avoiding the existing module import cycle.image_eventsemitsimage_aspect_mismatchonly whendelta is not Noneandabs(delta) > IMAGE_ASPECT_DELTA_TOL.table_eventsemits the existingtabular_overflowcategory only whenwrapper_clipped_index is Noneand eitherexcess_xorexcess_yexceedsTABLE_SCROLL_TOL_PX.bool(overflow.get("passed", False)) and not classifications, so a new image/table classification flipsvisual_check_passedtoFalse.clipped_innerclassifier paths remain in place;placement_diagnosticsshape is still returned.=== EVIDENCE ===
Commands run:
git diff -- src/phase_z2_classifier.pyrg -n "IMAGE_ASPECT_DELTA_TOL|TABLE_SCROLL_TOL_PX|overflow\\.passed|image_events|table_events|visual_check_passed|image_aspect_mismatch|tabular_overflow" src/phase_z2_classifier.py src/phase_z2_pipeline.pyGet-Content -LiteralPath 'src\\phase_z2_classifier.py' | Select-Object -Skip 320 -First 190Get-Content -LiteralPath 'src\\phase_z2_pipeline.py' | Select-Object -First 160$env:PYTHONPATH='src'; python -m py_compile src\\phase_z2_classifier.py$env:PYTHONPATH='src'; <pure-dict smoke script for image/table event branches>git diff --stat -- src/phase_z2_classifier.py tests/phase_z2/test_phase_z2_visual_classifier.pyFiles checked:
D:\\ad-hoc\\kei\\design_agent\\src\\phase_z2_classifier.pyD:\\ad-hoc\\kei\\design_agent\\src\\phase_z2_pipeline.pyTest results:
python -m py_compile src\\phase_z2_classifier.py: passed.image_fire:visual_check_passed=False,categories_seen=['image_aspect_mismatch'],classifications=1image_below_tol:visual_check_passed=True,categories_seen=[],classifications=0table_fire:visual_check_passed=False,categories_seen=['tabular_overflow'],classifications=1table_dedup:visual_check_passed=True,categories_seen=[],classifications=0Remaining units: [u2]
rewind_target: continue_same
FINAL_CONSENSUS: NO
Executing unit: u2
📌 [Claude #3] Stage 3 (코드 수정) Round #2 — u2 (pure-dict classifier tests) 완료
■ unit_executed:
u2(Stage 2 plan — Add pure-dict classifier tests)■ files_changed:
tests/phase_z2/test_phase_z2_visual_classifier.py(NEW, 116 lines incl. docstring)■ diff_summary
NEW file —
tests/phase_z2/test_phase_z2_visual_classifier.py— 4 pure-dict unit tests for the newclassify_visual_runtime_checkconsumer behavior landed in u1. No Selenium / chromedriver dependency; no fixtures; no parametrize. Each test constructs an overflow dict by hand and asserts on the classifier's return shape.Imports (single source of truth) — tolerances pulled from
phase_z2_pipeline, not duplicated as literals:Tests derive thresholds from imported constants (
IMAGE_ASPECT_DELTA_TOL + 0.05,TABLE_SCROLL_TOL_PX + 10,TABLE_SCROLL_TOL_PX + 50) — if the SSoT inphase_z2_pipeline.py:134/:139shifts, the tests track automatically._base_overflow(**overrides)helper — minimal clean overflow dict (passed=True, empty zones / image_events / table_events, no slide / slide_body overflow). Each test overlays only the array it exercises. Keeps test bodies focused on the axis under inspection (image_events scan or table_events scan).Four required cases (1:1 with issue acceptance criteria):
test_image_aspect_mismatch_emits_classification—delta = IMAGE_ASPECT_DELTA_TOL + 0.05(above tolerance). Asserts:visual_check_passed is False(widened semantic flips even though zone overflow is clean)categories_seen == ["image_aspect_mismatch"]classifications[0]carriescategory="image_aspect_mismatch",source="image_event",zone_position="top",delta=<expected>.test_image_aspect_delta_below_tol_no_classification—delta = IMAGE_ASPECT_DELTA_TOL / 2.0(below tolerance). Asserts:visual_check_passed is Truecategories_seen == []andclassifications == [](no false positive).test_standalone_table_overflow_emits_classification—wrapper_clipped_index=None,excess_y = TABLE_SCROLL_TOL_PX + 10. Asserts:visual_check_passed is Falsecategories_seen == ["tabular_overflow"](existingtabular_overflowcategory reused verbatim —phase_z2_classifier.py:234)classifications[0]carriescategory="tabular_overflow",source="table_event",zone_position="bottom_l",excess_y=<expected>.test_table_dedup_when_wrapper_clipped—wrapper_clipped_index=0(non-null) withexcess_y = TABLE_SCROLL_TOL_PX + 50(well above tolerance — proves the dedup branch wins over the threshold check). Asserts:visual_check_passed is Trueclassifications == [](honors producer-side dedup contract fromphase_z2_pipeline.py:2287-2325; the wrapper's existingclipped_innercascade owns this case).No changes to:
src/phase_z2_classifier.py(already complete in u1).src/phase_z2_pipeline.py(out-of-scope — image_events / table_events production landed in 실행-1/2).■ verification
categories_seen/classificationsexact-match assertion proves u1's new emitters fire only on the intended path and that the widenedvisual_check_passed = overflow.passed AND not classificationssemantic is observable from the public API.■ remaining_units:
[](u1 + u2 both complete; Stage 2 plan exhausted).■ follow_up_issue_candidates:
pipeline.pyimage_events / table_events production-side regression coverage (separate axis, not implied by this issue).image_aspect_mismatch(explicitly deferred — issue body "Out-of-scope" section calls this FUTURE follow-up).debug.jsonpassthrough for the new event arrays (실행-4 territory).■ scope-lock: respected — only
src/phase_z2_classifier.py(u1) andtests/phase_z2/test_phase_z2_visual_classifier.py(u2) touched. No edits to pipeline.py, no router changes, no debug.json plumbing, no placement_diagnostics shape change, no sample-specific branching.STOP per orchestrator instruction. No commit, no push. Awaiting Codex Stage 3 verification on u2.
[Codex #2]
Stage 3 code-edit verification for executed unit
u2is complete.Verdict:
u2is correct, and all planned implementation units for this issue are now complete.Verified facts:
tests/phase_z2/test_phase_z2_visual_classifier.pyis a new pure-dict test file and does not use Selenium, chromedriver, browser fixtures, or pipeline execution.IMAGE_ASPECT_DELTA_TOLandTABLE_SCROLL_TOL_PXfromsrc.phase_z2_pipeline, so threshold values are not duplicated as hardcoded literals.test_image_aspect_mismatch_emits_classificationcoversdelta > IMAGE_ASPECT_DELTA_TOL, assertsimage_aspect_mismatch, and verifiesvisual_check_passed is False.test_image_aspect_delta_below_tol_no_classificationcovers below-tolerance image delta and asserts no classifications withvisual_check_passed is True.test_standalone_table_overflow_emits_classificationcovers standalone table self-overflow withwrapper_clipped_index=None, assertstabular_overflow, and verifiesvisual_check_passed is False.test_table_dedup_when_wrapper_clippedcovers non-nullwrapper_clipped_indexwith excess beyond tolerance and asserts notabular_overflow, preserving the dedup contract.image_aspect_mismatchandtabular_overfloware emitted under the required thresholds, andvisual_check_passedisoverflow.passed AND not classifications.=== EVIDENCE ===
Commands run:
git diff -- tests/phase_z2/test_phase_z2_visual_classifier.py src/phase_z2_classifier.pyGet-Content -LiteralPath 'tests\phase_z2\test_phase_z2_visual_classifier.py'rg -n "def classify_visual_runtime_check|IMAGE_ASPECT_DELTA_TOL|TABLE_SCROLL_TOL_PX|image_events|table_events|visual_check_passed|image_aspect_mismatch|tabular_overflow" src\phase_z2_classifier.py src\phase_z2_pipeline.py tests\phase_z2\test_phase_z2_visual_classifier.py$env:PYTHONPATH='src'; pytest -q tests\phase_z2\test_phase_z2_visual_classifier.pyFiles checked:
D:\ad-hoc\kei\design_agent\tests\phase_z2\test_phase_z2_visual_classifier.pyD:\ad-hoc\kei\design_agent\src\phase_z2_classifier.pyD:\ad-hoc\kei\design_agent\src\phase_z2_pipeline.pyTest results:
pytest -q tests\phase_z2\test_phase_z2_visual_classifier.py: 4 passed in 0.04s.Remaining units: none
FINAL_CONSENSUS: YES
📌 [Claude #1] Stage 4: 테스트 및 검증 Round #1 — PASS
■ Verification scope
classify_visual_runtime_checkconsumer extension forimage_events[]/table_events[]+ widenedvisual_check_passed.src/phase_z2_classifier.py(modified) andtests/phase_z2/test_phase_z2_visual_classifier.py(new).orchestrator.py,tests/orchestrator_unit/test_subprocess_cleanup.py) NOT touched in this verification — separate axis per Stage 3 exit report.■ Test results
test_image_aspect_mismatch_emits_classification— delta=TOL+0.05 ⇒ classification fires,visual_check_passed=False.test_image_aspect_delta_below_tol_no_classification— delta=TOL/2 ⇒ skip,visual_check_passed=True.test_standalone_table_overflow_emits_classification—wrapper_clipped_index=None,excess_y=TOL+10⇒tabular_overflowfires.test_table_dedup_when_wrapper_clipped—wrapper_clipped_index=0⇒ no emit (clipped_inner dedup honored).■ Diff ↔ plan conformance (Stage 2 IMPLEMENTATION_UNITS)
from phase_z2_pipeline import IMAGE_ASPECT_DELTA_TOL, TABLE_SCROLL_TOL_PX(deferred import, circular-safe; SSoT honored —src/phase_z2_pipeline.py:134, 139).overflow.passed=TrueREMOVED at former L367-375 (verified bygit diff).delta is None ⇒ continue;abs(delta) > IMAGE_ASPECT_DELTA_TOL ⇒ emit image_aspect_mismatchwith full payload (zone_position, zone_template_id, src, natural_ratio, rendered_ratio, delta, rule_applied).wrapper_clipped_index is not None ⇒ continue;excess_x > TOL or excess_y > TOL ⇒ emit tabular_overflow(priority-2 category reused verbatim).visual_check_passed = bool(overflow.get("passed", False)) and not classifications— widened semantic.placement_diagnosticsshape preserved;categories_seen/unclassified_signalsunchanged.■ Hardcoding audit (RULE 7)
0.05/5in classifier — all tolerance comparisons go through imported constants. SSoT remainssrc/phase_z2_pipeline.py:134, 139.■ Pure-dict audit (RULE 4 PZ-1)
selenium/webdriver/chromedriverimports (only the docstring mentions "no Selenium / chromedriver dependency"). Test fixtures are plain dicts assembled in-line via_base_overflow(**overrides).■ Acceptance criteria (issue #47)
classificationsincludesimage_aspect_mismatch/tabular_overflowwhen triggered.visual_check_passed = overflow.passed AND (not classifications)(intentional flip — verified bytest_image_aspect_mismatch_emits_classificationwithoverflow["passed"]=True).IMAGE_ASPECT_DELTA_TOLimported fromphase_z2_pipeline(single source of truth).■ Decision
src/phase_z2_classifier.py+tests/phase_z2/test_phase_z2_visual_classifier.py.=== EVIDENCE ===
git diff src/phase_z2_classifier.py— early-return removed, two scans added,visual_check_passedwidened (3 hunks; +61/-11).python -m pytest tests/phase_z2/test_phase_z2_visual_classifier.py -q→ 4 passed in 0.05s.python -m pytest tests/phase_z2/ -q --ignore=tests/phase_z2/test_phase_z2_visual_classifier.py→ 93 passed in 26.10s.grep -n "0\.05\|IMAGE_ASPECT_DELTA_TOL\|TABLE_SCROLL_TOL_PX" src/phase_z2_classifier.py→ no literal tolerances; only imported-constant references.grep -n "IMAGE_ASPECT_DELTA_TOL\|TABLE_SCROLL_TOL_PX" src/phase_z2_pipeline.py→ SSoT confirmed at L134, L139.grep -n "Selenium\|selenium\|webdriver\|chromedriver" tests/phase_z2/test_phase_z2_visual_classifier.py→ only docstring mention; no imports.files_changed(src/phase_z2_classifier.py+61/-11,tests/phase_z2/test_phase_z2_visual_classifier.py+116 new) matchesgit status -sworking-tree state.FINAL_CONSENSUS: YES
[Codex #1] Stage 4: test-verify Round #1 - PASS
Verification scope:
classify_visual_runtime_checkconsumesimage_events[]andtable_events[], andvisual_check_passedis widened tooverflow.passed AND no classifications.src/phase_z2_classifier.pyandtests/phase_z2/test_phase_z2_visual_classifier.py.orchestrator.py,tests/orchestrator_unit/test_subprocess_cleanup.py, and unrelated untracked local artifacts.Diff verification:
src/phase_z2_classifier.pyremoves the formeroverflow.get("passed", False)early return, so image/table event scans run even when zone overflow is clean.IMAGE_ASPECT_DELTA_TOLandTABLE_SCROLL_TOL_PXfromphase_z2_pipelineat call time; no classifier-local tolerance literals were found for the comparisons.image_events[]emitsimage_aspect_mismatchonly whendelta is not Noneandabs(delta) > IMAGE_ASPECT_DELTA_TOL.table_events[]emitstabular_overflowonly whenwrapper_clipped_index is Noneand either excess exceedsTABLE_SCROLL_TOL_PX.visual_check_passedis computed asbool(overflow.get("passed", False)) and not classifications.placement_diagnostics,categories_seen, andunclassified_signalsreturn fields are preserved.Test verification:
tests/phase_z2regression suite excluding the new file passes: 93/93.pipeline.pyproduction edit was required for this verification.=== EVIDENCE ===
Commands run:
git status --shortgit diff -- src/phase_z2_classifier.py tests/phase_z2/test_phase_z2_visual_classifier.pyrg -n 'IMAGE_ASPECT_DELTA_TOL|TABLE_SCROLL_TOL_PX|image_aspect_mismatch|tabular_overflow|visual_check_passed|wrapper_clipped_index|overflow\.get' src/phase_z2_classifier.py tests/phase_z2/test_phase_z2_visual_classifier.py src/phase_z2_pipeline.pygit diff --check -- src/phase_z2_classifier.py tests/phase_z2/test_phase_z2_visual_classifier.pypython -m pytest tests/phase_z2/test_phase_z2_visual_classifier.py -qpython -m pytest tests/phase_z2/ -q --ignore=tests/phase_z2/test_phase_z2_visual_classifier.pyFiles checked:
src/phase_z2_classifier.pytests/phase_z2/test_phase_z2_visual_classifier.pysrc/phase_z2_pipeline.pyTest results:
4 passed in 0.05s93 passed in 25.80sVerified facts:
src/phase_z2_pipeline.pydefinesIMAGE_ASPECT_DELTA_TOL = 0.05andTABLE_SCROLL_TOL_PX = 5.src/phase_z2_classifier.pyuses the imported tolerance names for image/table classification comparisons.tests/phase_z2/test_phase_z2_visual_classifier.pycontains the four required pure-dict tests: image mismatch emit, image below-tolerance skip, standalone table overflow emit, and wrapper-clipped table dedup.git diff --checkreported no whitespace errors for the scoped files.FINAL_CONSENSUS: YES
📌 [Claude #1] Stage 5: 커밋 및 푸쉬 Round #1 — COMPLETE
Korean 요약
visual_check_passedwidened) 단일 commit535c484로 생성 후slide2(Gitea, issue tracker) +origin(GitHub) 양 remote 에 push 성공.src/phase_z2_classifier.py(+61/-11),tests/phase_z2/test_phase_z2_visual_classifier.py(+116 new).git add는 두 파일에 한정 — 작업 트리에 잔류한orchestrator.py/tests/orchestrator_unit/test_subprocess_cleanup.py(별 axis: P3 subprocess fix),.orchestrator/,chromedriver/,tests/matching/,samples/uploads/,templates/catalog/등 untracked artefact 는 본 commit 에 미포함.feat(IMP-15): 실행-3 — classifier consumes image+table events접두 + 변경 요약 (early-return 제거 / 두 emitter /visual_check_passedwiden) + guardrail (8-rule cascade 보존 /placement_diagnosticsshape /pipeline.py미변경) + 테스트 4 케이스 + Stage 4 결과 (4/4 신규, 93/93 regression) 포함.535c4848fd60a1e5a57757048b6ffaad4495a3b6(git ls-remote slide2 / origin refs/heads/main직접 확인).Scope 준수
git add는 명시된 두 파일만. 작업 트리에 잔류한orchestrator.py/tests/orchestrator_unit/test_subprocess_cleanup.py의 P3 subprocess fix 변경 + 다수 untracked noise (sample / matching / scratch) 는 본 commit 에 미포함.pipeline.py변경 X (producer 는 실행-1/2 에서 이미 landed),slide_base.html/catalog/frame DB/debug.json/ router action / spec doc 모두 미변경.git diff --staged --stat결과 = Stage 3 / Stage 4 exit report 와 일치 (src/phase_z2_classifier.py72L (+61/-11),tests/phase_z2/test_phase_z2_visual_classifier.py116L new).실행 흐름
git status --short— 스코프 파일 + 다수 untracked artefact + 무관 modified 두 파일 확인.git diff --stat -- src/phase_z2_classifier.py tests/phase_z2/test_phase_z2_visual_classifier.py— 72 line 변경량 사전 검증.git add src/phase_z2_classifier.py tests/phase_z2/test_phase_z2_visual_classifier.py— 명시 파일만 (CRLF 경고만, 변환 무해).git diff --staged --stat—src/phase_z2_classifier.py+61/-11,tests/phase_z2/test_phase_z2_visual_classifier.py+116/0 확인.git diff --staged -- src/phase_z2_classifier.py | head -120— early-return 제거 + 두 신규 scan +visual_check_passedwidening 확인.git commit -m …— HEREDOC 으로 본문 보존,--amend/--no-verify사용 없음. pre-commit hook 통과.git push origin main— fast-forward2827622..535c484.git push slide2 main— fast-forward2827622..535c484(Processed 1 references).git ls-remote origin / slide2 refs/heads/main— 양측 모두535c4848…로 local HEAD 일치.잔류 unit (out-of-scope, 별도 issue)
debug.json에image_events/table_eventsraw surfacing +PHASE-Z-PIPELINE-OVERVIEW.md/PHASE-Z-PIPELINE-STATUS-BOARD.md/PHASE-Z-FIT-CLASSIFIER-ROUTER-SPEC.mdrow update.image_aspect_mismatchclassification → frame swap / sizing adapter 매핑 (FUTURE follow-up issue, 본 issue body 의 out-of-scope 라인 명시).orchestrator.py+tests/orchestrator_unit/test_subprocess_cleanup.pytext/bytes mode bug. 별 axis (2026-05-18), 본 issue 무관. 별도 commit/issue 로 분기 필요.Guardrails (post-commit)
IMAGE_ASPECT_DELTA_TOL/TABLE_SCROLL_TOL_PX는phase_z2_pipeline에서 deferred import — classifier-local literal 없음, SSoT 유지. 실행-4 / router 도 동일 상수를 import 해야 함.placement_diagnostics,categories_seen,unclassified_signalsreturn-shape 보존 — downstream consumer 의debug.json의존성 호환.wrapper_clipped_index is not None ⇒ skip tabular_overflowdedup 계약은 pipeline producer (실행-2) 와 consumer 양측에서 잠금.=== EVIDENCE ===
source_comment_ids:
FINAL_CONSENSUS: YES
[Codex #1] Stage 5: commit-push verification - COMPLETE
I verified the Stage 5 commit and remote state for Issue #47.
Decision:
535c4848fd60a1e5a57757048b6ffaad4495a3b6.src/phase_z2_classifier.pytests/phase_z2/test_phase_z2_visual_classifier.pymainrefs now point to the same SHA:origin/main=535c4848fd60a1e5a57757048b6ffaad4495a3b6slide2/main=535c4848fd60a1e5a57757048b6ffaad4495a3b6Working-tree note:
orchestrator.py,tests/orchestrator_unit/test_subprocess_cleanup.py,.orchestrator/,chromedriver/,samples/,templates/, andtests/matching/.535c4848fd60a1e5a57757048b6ffaad4495a3b6.=== EVIDENCE ===
Commands run:
git status --shortgit rev-parse HEADgit show --stat --name-only --format=fuller --no-renames HEADgit remote -vgit ls-remote origin refs/heads/maingit ls-remote slide2 refs/heads/maingit diff-tree --no-commit-id --name-status -r HEADgit show --stat --oneline --no-renames HEADFiles checked:
src/phase_z2_classifier.pytests/phase_z2/test_phase_z2_visual_classifier.pyCommit SHA:
535c4848fd60a1e5a57757048b6ffaad4495a3b6Verified facts:
git rev-parse HEADreturned535c4848fd60a1e5a57757048b6ffaad4495a3b6.git diff-tree --no-commit-id --name-status -r HEADreturned exactly:M src/phase_z2_classifier.pyA tests/phase_z2/test_phase_z2_visual_classifier.pygit show --stat --oneline --no-renames HEADreported2 files changed, 177 insertions(+), 11 deletions(-).git ls-remote origin refs/heads/mainreturned535c4848fd60a1e5a57757048b6ffaad4495a3b6.git ls-remote slide2 refs/heads/mainreturned535c4848fd60a1e5a57757048b6ffaad4495a3b6.FINAL_CONSENSUS: YES
📌 [Claude #1] Stage 6: 최종 확인 / close — Round #1
Issue body re-read (execution-issue mode)
IMP-15 실행-3 (child execution issue). Parent IMP-15 already analyzed scope; this issue's single axis = classifier consumer of
image_events[]+table_events[]withvisual_check_passedsemantic widening.Scope-lock from issue body:
classify_visual_runtime_checkto consumeimage_events+table_events.visual_check_passedwhen any classification fires (even if zone overflow is clean).src/phase_z2_classifier.py+tests/phase_z2/test_phase_z2_visual_classifier.py(NEW).Acceptance criteria — code-anchored verification
classificationslist includesimage_aspect_mismatchwhen image delta exceededsrc/phase_z2_classifier.py:435("category": "image_aspect_mismatch") emitted in image_events scan L426-447classificationslist includestabular_overflowwhen standalone table overflow firessrc/phase_z2_classifier.py:460("category": "tabular_overflow") emitted in table_events scan L449-471visual_check_passed = overflow.passed AND (not classifications)src/phase_z2_classifier.py:495— exact form:bool(overflow.get("passed", False)) and not classificationsIMAGE_ASPECT_DELTA_TOLimported fromphase_z2_pipeline(SSoT)src/phase_z2_classifier.py:360deferred import:from phase_z2_pipeline import IMAGE_ASPECT_DELTA_TOL, TABLE_SCROLL_TOL_PX(circular-safe)tests/phase_z2/test_phase_z2_visual_classifier.py:41/63/83/104— fourdef test_*functions, no chromedriver/Selenium importsTest coverage matches issue body exactly:
test_image_aspect_mismatch_emits_classification— delta > 0.05 → classification +visual_check_passed=False✅ (L41)test_image_aspect_delta_below_tol_no_classification— delta < 0.05 → no classification ✅ (L63)test_standalone_table_overflow_emits_classification— table self-overflow →"tabular_overflow"✅ (L83)test_table_dedup_when_wrapper_clipped—wrapper_clipped_indexset → notabular_overflow(dedup honored) ✅ (L104)Out-of-scope (issue body) — verified untouched:
pipeline.pyproduction (실행-1/2 already landed) — 0 changes in this commit.debug.jsonpassthrough (실행-4) — deferred.image_aspect_mismatch— deferred (FUTURE follow-up).Commit verification (3-way SHA match)
HEAD→535c4848fd60a1e5a57757048b6ffaad4495a3b6git ls-remote origin refs/heads/main→535c4848fd60a1e5a57757048b6ffaad4495a3b6git ls-remote slide2 refs/heads/main→535c4848fd60a1e5a57757048b6ffaad4495a3b6feat(IMP-15): 실행-3 — classifier consumes image+table events2827622(IMP-16 Step 14 table_self_overflow detection)src/phase_z2_classifier.py+61/-11,tests/phase_z2/test_phase_z2_visual_classifier.py+116/-0 (total 2 files, +177/-11)Goal vs result
image_events[]andtable_events[]arrays (produced by 실행-1/2) and widensvisual_check_passedsemantics so image/table mismatches surface even when zone overflow is clean.image_aspect_mismatch+tabular_overfloware ADDITIVE emitters layered on top of the existing §3.2 8-rule cascade.visual_check_passedrecomputed at L495 =overflow.passed AND not classifications. Tolerances imported from pipeline (SSoT) — no classifier-local literals.pipeline.pyproduction code untouched,placement_diagnosticsshape preserved, 8-rule cascade (clipped_inner/ zone-self) untouched (verified by Stage 4 — 93/93 regression PASS).Follow-up — IMP-15 remaining execution issues
e9b3d2e2827622(IMP-16 sibling)visual_check_passedflip535c484debug.jsonraw passthrough forimage_events[]/table_events[]image_aspect_mismatch(separate axis)Labels
No label changes proposed in this issue. Parent IMP-15 owns the rollup label state when remaining child execution units (실행-4 + router-action follow-up) close.
Close decision
CLOSE issue #47. All 5 acceptance criteria satisfied, commit on both remotes (origin + slide2) at identical SHA
535c484, scope-lock honored (2 files only), 4 pure-dict tests PASS + 93 regression PASS, deferred axes (debug.json passthrough, router action) explicitly named with follow-up paths. No unresolved questions block close.=== EVIDENCE ===
Commands:
git rev-parse HEAD→535c4848fd60a1e5a57757048b6ffaad4495a3b6git log -1 --format='%H%n%s'→535c484/feat(IMP-15): 실행-3 — classifier consumes image+table eventsgit ls-remote origin refs/heads/main→535c4848fd60a1e5a57757048b6ffaad4495a3b6git ls-remote slide2 refs/heads/main→535c4848fd60a1e5a57757048b6ffaad4495a3b6git show --stat 535c484→ 2 files changed, 177 insertions(+), 11 deletions(-)rg -n 'IMAGE_ASPECT_DELTA_TOL|TABLE_SCROLL_TOL_PX|image_aspect_mismatch|tabular_overflow|visual_check_passed' src/phase_z2_classifier.py→ import at L360, image emit L435, table emit L460,visual_check_passedwidened at L495rg -n 'def test_' tests/phase_z2/test_phase_z2_visual_classifier.py→ 4 functions at L41/63/83/104Files verified in HEAD commit:
src/phase_z2_classifier.py— early-return removed, image_events scan added (L426-447), table_events scan added (L449-471),visual_check_passedwidened (L495), SSoT import (L360).tests/phase_z2/test_phase_z2_visual_classifier.py— new, 4 pure-dict cases, tolerances imported fromsrc.phase_z2_pipeline(no hardcoded literals).Stage 4 test record (binding):
pytest tests/phase_z2/excluding new file: 93/93 PASS (regression — 8-rule cascade preserved).Reviewer chain:
FINAL_CONSENSUS: YES
[Codex #1] Stage 6 final-close verification
Final independent verification result: PASS. I verified the committed implementation against issue #47 scope and acceptance criteria, using the completed stage reports as binding contracts and checking the current repository state directly.
Scope-lock confirmed:
src/phase_z2_classifier.pyandtests/phase_z2/test_phase_z2_visual_classifier.py.pipeline.py, router action selection, debug.json passthrough, and unrelated dirty worktree files are not part of this commit.Acceptance criteria verified:
classify_visual_runtime_checkconsumesoverflow["image_events"]and emitsimage_aspect_mismatchwhendelta is not Noneandabs(delta) > IMAGE_ASPECT_DELTA_TOL.classify_visual_runtime_checkconsumesoverflow["table_events"]and emitstabular_overflowwhenwrapper_clipped_index is Noneand table excess exceedsTABLE_SCROLL_TOL_PX.wrapper_clipped_indexskips the table-event emitter.visual_check_passedis computed asbool(overflow.get("passed", False)) and not classifications.phase_z2_pipeline:IMAGE_ASPECT_DELTA_TOL = 0.05,TABLE_SCROLL_TOL_PX = 5.=== EVIDENCE ===
Commands run:
git rev-parse HEADgit diff-tree --no-commit-id --name-status -r HEADgit show --stat --oneline --no-renames HEADgit ls-remote origin refs/heads/main; git ls-remote slide2 refs/heads/mainrg -n "IMAGE_ASPECT_DELTA_TOL|TABLE_SCROLL_TOL_PX|image_aspect_mismatch|tabular_overflow|visual_check_passed|image_events|table_events|wrapper_clipped_index" src/phase_z2_classifier.pyrg -n "def test_|selenium|chromedriver|IMAGE_ASPECT_DELTA_TOL|TABLE_SCROLL_TOL_PX|image_aspect_mismatch|tabular_overflow|wrapper_clipped_index|visual_check_passed" tests/phase_z2/test_phase_z2_visual_classifier.pyrg -n "IMAGE_ASPECT_DELTA_TOL|TABLE_SCROLL_TOL_PX" src/phase_z2_pipeline.pypytest -q tests/phase_z2/test_phase_z2_visual_classifier.pygit status --shortFiles checked:
src/phase_z2_classifier.pytests/phase_z2/test_phase_z2_visual_classifier.pysrc/phase_z2_pipeline.pyVerified facts:
535c4848fd60a1e5a57757048b6ffaad4495a3b6.origin/mainis535c4848fd60a1e5a57757048b6ffaad4495a3b6.slide2/mainis535c4848fd60a1e5a57757048b6ffaad4495a3b6.M src/phase_z2_classifier.pyandA tests/phase_z2/test_phase_z2_visual_classifier.py.2 files changed, 177 insertions(+), 11 deletions(-).src/phase_z2_classifier.pyimportsIMAGE_ASPECT_DELTA_TOLandTABLE_SCROLL_TOL_PXfromphase_z2_pipelineinsideclassify_visual_runtime_check.src/phase_z2_classifier.pycontains theimage_eventsscan and theimage_aspect_mismatchemitter.src/phase_z2_classifier.pycontains thetable_eventsscan, thewrapper_clipped_index is not Nonededupe skip, and thetabular_overflowemitter.src/phase_z2_classifier.pycomputesvisual_check_passed = bool(overflow.get("passed", False)) and not classifications.src/phase_z2_pipeline.pydefinesIMAGE_ASPECT_DELTA_TOL = 0.05andTABLE_SCROLL_TOL_PX = 5.4 passed in 0.05s.FINAL_CONSENSUS: YES