# List of Changes **Run**: 01-code-cleanup **Mode**: automatic **Source**: self-discovered **Date**: 2026-03-30 ## Summary Two tiers: (1) Fix critical logical flow bugs — batch handling, data loss, crash prevention, and remove the fixed-batch-size assumption that contradicts the dynamic engine design. (2) Dead code cleanup, configurable paths, HTTP timeouts, and move source to `src/`. ## Changes ### C01: Move source code to `src/` directory - **File(s)**: main.py, inference.pyx, constants_inf.pyx, constants_inf.pxd, annotation.pyx, annotation.pxd, ai_config.pyx, ai_config.pxd, ai_availability_status.pyx, ai_availability_status.pxd, loader_http_client.pyx, loader_http_client.pxd, engines/, setup.py, run-tests.sh, e2e/run_local.sh, e2e/docker-compose.test.yml - **Problem**: All source code is in the repository root, mixed with config, docs, and test infrastructure. - **Change**: Move all application source files into `src/`. Update setup.py extension paths, run-tests.sh, e2e scripts, and docker-compose volumes. Keep setup.py, requirements, and tests at root. - **Rationale**: Project convention requires source under `src/`. - **Risk**: medium - **Dependencies**: None (do first — all other changes reference new paths) ### C02: Fix `_process_images` — accumulate all images, process once (LF-05, LF-06) - **File(s)**: src/inference.pyx (`_process_images`) - **Problem**: `frame_data = []` is reset inside the per-image loop, so only the last image's data survives to the outer processing loop. Non-last small images are silently dropped. Large images that exceed batch_size inside the loop are also re-processed outside the loop (double-processing). - **Change**: Accumulate frame_data across ALL images (move reset before the loop). Process all accumulated data once after the loop. Remove the inner batch-processing + status call. Each image's tiles/frames should carry their own ground_sampling_distance so mixed-GSD images process correctly. - **Rationale**: Critical data loss — multi-image requests silently drop all images except the last. - **Risk**: medium - **Dependencies**: C01, C04 ### C03: Fix `_process_video` — flush remaining frames after loop (LF-02, LF-04) - **File(s)**: src/inference.pyx (`_process_video`) - **Problem**: The `if len(batch_frames) == self.engine.get_batch_size()` gate means frames are only processed in exact-batch-size groups. When the video ends with a partial batch (1..batch_size-1 frames), those frames are silently dropped. Detections at the end of every video are potentially missed. - **Change**: After the video read loop, if `batch_frames` is non-empty, process the remaining frames as a partial batch (no padding). Change the `==` gate to `>=` as a safety measure, though with the flush it's not strictly needed. - **Rationale**: Silent data loss — last frames of every video are dropped. - **Risk**: medium - **Dependencies**: C01, C04 ### C04: Remove `split_list_extend` — replace with simple chunking without padding (LF-03, LF-08) - **File(s)**: src/inference.pyx (`split_list_extend`, `_process_images`, `detect_single_image`) - **Problem**: `split_list_extend` pads the last chunk by duplicating its final element to fill `batch_size`. This wastes compute (duplicate inference), may produce duplicate detections, and contradicts the dynamic batch design established in Step 3 (engine polymorphism). In `detect_single_image`, `[frame] * batch_size` pads a single frame to batch_size copies — same issue. - **Change**: Replace `split_list_extend` with plain chunking (no padding). Last chunk keeps its natural size. In `detect_single_image`, pass a single-frame list. Engine `run()` and `preprocess()` must handle variable-size input — verify each engine supports this or add a minimal adapter. - **Rationale**: Unnecessary compute (up to 4x for TensorRT single-image), potential duplicate detections from padding, contradicts dynamic batch design. - **Risk**: high - **Dependencies**: C01 ### C05: Fix frame-is-None crash in `_process_images` (LF-07) - **File(s)**: src/inference.pyx (`_process_images`) - **Problem**: `frame.shape` is accessed before `frame is None` check. If `cv2.imread` fails, the pipeline crashes instead of skipping the file. - **Change**: Move the None check before the shape access. - **Rationale**: Crash prevention for missing/corrupt image files. - **Risk**: low - **Dependencies**: C01 ### C06: Remove orphaned RabbitMQ declarations from constants_inf.pxd - **File(s)**: src/constants_inf.pxd - **Problem**: `QUEUE_MAXSIZE`, `COMMANDS_QUEUE`, `ANNOTATIONS_QUEUE` are declared but have no implementations. Remnants of previous RabbitMQ architecture. - **Change**: Remove the three declarations and their comments. - **Rationale**: Dead declarations mislead about system architecture. - **Risk**: low - **Dependencies**: C01 ### C07: Remove unused constants from constants_inf - **File(s)**: src/constants_inf.pxd, src/constants_inf.pyx - **Problem**: `CONFIG_FILE` (with stale "zmq" comment), `QUEUE_CONFIG_FILENAME`, `CDN_CONFIG`, `SMALL_SIZE_KB` — defined but never referenced. - **Change**: Remove all four from .pxd and .pyx. - **Rationale**: Dead constants with misleading comments. - **Risk**: low - **Dependencies**: C01 ### C08: Remove dead serialize/from_msgpack methods and msgpack imports - **File(s)**: src/annotation.pyx, src/annotation.pxd, src/ai_availability_status.pyx, src/ai_availability_status.pxd, src/ai_config.pyx, src/ai_config.pxd - **Problem**: `Annotation.serialize()`, `AIAvailabilityStatus.serialize()`, `AIRecognitionConfig.from_msgpack()` — all dead. Associated `import msgpack` / `from msgpack import unpackb` only serve these dead methods. - **Change**: Remove all three methods from .pyx and .pxd files. Remove msgpack imports. - **Rationale**: Legacy queue-era serialization with no callers. - **Risk**: low - **Dependencies**: C01 ### C09: Remove unused fields (file_data, model_batch_size, annotation_name) - **File(s)**: src/ai_config.pyx, src/ai_config.pxd, src/annotation.pyx, src/annotation.pxd, src/main.py - **Problem**: `AIRecognitionConfig.file_data` populated but never read. `AIRecognitionConfig.model_batch_size` parsed but never used (engine owns batch size). `Detection.annotation_name` set but never read. - **Change**: Remove field declarations from .pxd, remove from constructors and factory methods in .pyx. Remove `file_data` and `model_batch_size` from AIConfigDto in main.py. Remove annotation_name assignment loop in Annotation.__init__. - **Rationale**: Dead fields that mislead about responsibilities. - **Risk**: low - **Dependencies**: C01, C08 ### C10: Remove misc dead code (stop no-op, empty pxd, unused pxd imports) - **File(s)**: src/loader_http_client.pyx, src/loader_http_client.pxd, src/engines/__init__.pxd, src/engines/inference_engine.pxd - **Problem**: `LoaderHttpClient.stop()` is a no-op. `engines/__init__.pxd` is empty. `inference_engine.pxd` imports `List, Tuple` from typing and `numpy` — both unused. - **Change**: Remove stop() from .pyx and .pxd. Delete empty __init__.pxd. Remove unused imports from inference_engine.pxd. - **Rationale**: Dead code noise. - **Risk**: low - **Dependencies**: C01 ### C11: Remove msgpack from requirements.txt - **File(s)**: requirements.txt - **Problem**: `msgpack==1.1.1` has no consumers after C08 removes all msgpack usage. - **Change**: Remove from requirements.txt. - **Rationale**: Unused dependency. - **Risk**: low - **Dependencies**: C08 ### C12: Make classes.json path configurable via env var - **File(s)**: src/constants_inf.pyx - **Problem**: `open('classes.json')` is hardcoded, depends on CWD at import time. - **Change**: Read from `os.environ.get("CLASSES_JSON_PATH", "classes.json")`. - **Rationale**: Environment-appropriate configuration. - **Risk**: low - **Dependencies**: C01 ### C13: Make log directory configurable via env var - **File(s)**: src/constants_inf.pyx - **Problem**: `sink="Logs/log_inference_..."` is hardcoded. - **Change**: Read from `os.environ.get("LOG_DIR", "Logs")`. - **Rationale**: Environment configurability. - **Risk**: low - **Dependencies**: C01 ### C14: Add timeouts to LoaderHttpClient HTTP calls - **File(s)**: src/loader_http_client.pyx - **Problem**: No explicit timeout on `requests.post()` calls. Stalled loader hangs detections service. - **Change**: Add `timeout=120` to load and upload calls. - **Rationale**: Prevent service hangs. - **Risk**: low - **Dependencies**: C01 ### C15: Update architecture doc — remove msgpack from tech stack (LF-09) - **File(s)**: _docs/02_document/architecture.md - **Problem**: Tech stack lists "msgpack | 1.1.1 | Compact binary serialization for annotations and configs" but msgpack is dead code after this refactoring. - **Change**: Remove msgpack row from tech stack table. - **Rationale**: Documentation accuracy. - **Risk**: low - **Dependencies**: C08, C11