===== bootstrap-pr-report-check-pr250-9fwkh-archive-artifacts-pod.log ===== [pod/bootstrap-pr-report-check-pr250-9fwkh-archive-artifacts-pod/prepare] 2026-07-12T18:42:04.629414372-05:00 2026/07/12 23:42:04 Entrypoint initialization [pod/bootstrap-pr-report-check-pr250-9fwkh-archive-artifacts-pod/place-scripts] 2026-07-12T18:42:05.563766991-05:00 2026/07/12 23:42:05 Decoded script /tekton/scripts/script-0-4xtzt [pod/bootstrap-pr-report-check-pr250-9fwkh-archive-artifacts-pod/step-upload-artifacts] 2026-07-12T18:42:10.667990512-05:00 ==> uploading /workspace/shared/artifacts/ -> s3://tekton-logs/storeminder-ai/bootstrap/pr-250/bootstrap-pr-report-check-pr250-9fwkh/artifacts/ (endpoint http://10.10.0.10:9000) [pod/bootstrap-pr-report-check-pr250-9fwkh-archive-artifacts-pod/step-upload-artifacts] 2026-07-12T18:42:11.382049240-05:00 upload: ../workspace/shared/artifacts/junit-traits.xml to s3://tekton-logs/storeminder-ai/bootstrap/pr-250/bootstrap-pr-report-check-pr250-9fwkh/artifacts/junit-traits.xml [pod/bootstrap-pr-report-check-pr250-9fwkh-archive-artifacts-pod/step-upload-artifacts] 2026-07-12T18:42:11.382431539-05:00 upload: ../workspace/shared/artifacts/junit-sendable.xml to s3://tekton-logs/storeminder-ai/bootstrap/pr-250/bootstrap-pr-report-check-pr250-9fwkh/artifacts/junit-sendable.xml [pod/bootstrap-pr-report-check-pr250-9fwkh-archive-artifacts-pod/step-upload-artifacts] 2026-07-12T18:42:11.385899627-05:00 upload: ../workspace/shared/artifacts/coverage-sendable.xml to s3://tekton-logs/storeminder-ai/bootstrap/pr-250/bootstrap-pr-report-check-pr250-9fwkh/artifacts/coverage-sendable.xml [pod/bootstrap-pr-report-check-pr250-9fwkh-archive-artifacts-pod/step-upload-artifacts] 2026-07-12T18:42:11.385931696-05:00 upload: ../workspace/shared/artifacts/coverage-traits.xml to s3://tekton-logs/storeminder-ai/bootstrap/pr-250/bootstrap-pr-report-check-pr250-9fwkh/artifacts/coverage-traits.xml [pod/bootstrap-pr-report-check-pr250-9fwkh-archive-artifacts-pod/step-upload-artifacts] 2026-07-12T18:42:11.547848181-05:00 ==> viewer URL: https://ci-logs.storeminder.ai/storeminder-ai/bootstrap/pr-250/bootstrap-pr-report-check-pr250-9fwkh/artifacts/ ===== bootstrap-pr-report-check-pr250-9fwkh-archive-logs-pod.log ===== [pod/bootstrap-pr-report-check-pr250-9fwkh-archive-logs-pod/prepare] 2026-07-12T18:42:04.635091761-05:00 2026/07/12 23:42:04 Entrypoint initialization [pod/bootstrap-pr-report-check-pr250-9fwkh-archive-logs-pod/place-scripts] 2026-07-12T18:42:05.687698365-05:00 2026/07/12 23:42:05 Decoded script /tekton/scripts/script-0-257pk [pod/bootstrap-pr-report-check-pr250-9fwkh-archive-logs-pod/place-scripts] 2026-07-12T18:42:05.711703699-05:00 2026/07/12 23:42:05 Decoded script /tekton/scripts/script-1-ssxgf [pod/bootstrap-pr-report-check-pr250-9fwkh-archive-logs-pod/step-collect-logs] 2026-07-12T18:42:13.010303747-05:00 ==> collecting pod logs for PipelineRun=bootstrap-pr-report-check-pr250-9fwkh ===== bootstrap-pr-report-check-pr250-9fwkh-clone-pod.log ===== [pod/bootstrap-pr-report-check-pr250-9fwkh-clone-pod/prepare] 2026-07-12T18:38:34.108060082-05:00 2026/07/12 23:38:34 Entrypoint initialization [pod/bootstrap-pr-report-check-pr250-9fwkh-clone-pod/place-scripts] 2026-07-12T18:38:34.855141756-05:00 2026/07/12 23:38:34 Decoded script /tekton/scripts/script-0-nmnsb [pod/bootstrap-pr-report-check-pr250-9fwkh-clone-pod/step-clone] 2026-07-12T18:38:39.144517439-05:00 From https://github.com/storeminder-ai/bootstrap [pod/bootstrap-pr-report-check-pr250-9fwkh-clone-pod/step-clone] 2026-07-12T18:38:39.144546679-05:00 * branch d15c4b94b94c40ce1e7fefa13dcc806acd7ccee5 -> FETCH_HEAD [pod/bootstrap-pr-report-check-pr250-9fwkh-clone-pod/step-clone] 2026-07-12T18:38:39.163571685-05:00 Cloned d15c4b94b94c40ce1e7fefa13dcc806acd7ccee5 ===== bootstrap-pr-report-check-pr250-9fwkh-live-build-pod.log ===== [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-prepare-dockerfile] 2026-07-12T18:39:01.172163102-05:00 Wrote Dockerfile.reportcheck: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-prepare-dockerfile] 2026-07-12T18:39:01.172944820-05:00 # Throwaway PR image for the D2 live report build. Layers the PR checkout [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-prepare-dockerfile] 2026-07-12T18:39:01.172952860-05:00 # (src + tests + fixtures + notebooks) over the prod runtime so the D1 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-prepare-dockerfile] 2026-07-12T18:39:01.172956610-05:00 # trait suite can run with real deps + real secrets (mounted by the Job). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-prepare-dockerfile] 2026-07-12T18:39:01.172962090-05:00 FROM registry.storeminder.ai/images/storeminder:latest [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-prepare-dockerfile] 2026-07-12T18:39:01.172965950-05:00 COPY . /prsrc [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-prepare-dockerfile] 2026-07-12T18:39:01.172969220-05:00 WORKDIR /prsrc [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-prepare-dockerfile] 2026-07-12T18:39:01.172972320-05:00 # Overlay the PR package over the baked one (deps already resolved in the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-prepare-dockerfile] 2026-07-12T18:39:01.172975410-05:00 # base), then add the test runner. Prefer the [test] extra (D1/#90 adds [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-prepare-dockerfile] 2026-07-12T18:39:01.172978250-05:00 # papermill/nbclient/scrapbook there); fall back to bare pytest pre-D1. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-prepare-dockerfile] 2026-07-12T18:39:01.172982820-05:00 RUN pip install --no-cache-dir --no-deps -e /prsrc && (pip install --no-cache-dir -e ".[test]" || pip install --no-cache-dir pytest) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:01.444211862-05:00 INFO[0000] Retrieving image manifest registry.storeminder.ai/images/storeminder:latest [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:01.444243282-05:00 INFO[0000] Retrieving image registry.storeminder.ai/images/storeminder:latest from registry registry.storeminder.ai [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:01.472722813-05:00 INFO[0000] Retrieving image manifest registry.storeminder.ai/images/storeminder:latest [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:01.472734263-05:00 INFO[0000] Returning cached image manifest [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:01.484029894-05:00 INFO[0000] Built cross stage deps: map[] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:01.484042484-05:00 INFO[0000] Retrieving image manifest registry.storeminder.ai/images/storeminder:latest [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:01.484045174-05:00 INFO[0000] Returning cached image manifest [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:01.484050254-05:00 INFO[0000] Retrieving image manifest registry.storeminder.ai/images/storeminder:latest [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:01.484052694-05:00 INFO[0000] Returning cached image manifest [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:01.484148854-05:00 INFO[0000] Executing 0 build triggers [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:01.484367823-05:00 INFO[0000] Building stage 'registry.storeminder.ai/images/storeminder:latest' [idx: '0', base-idx: '-1'] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:01.495953903-05:00 INFO[0000] Checking for cached layer registry.storeminder.ai/images/cache:6ec5107d3ea97e691f166493d9b7501f63dee56803729c6a4334b8edd54fa452... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:01.520881247-05:00 INFO[0000] No cached layer found for cmd RUN pip install --no-cache-dir --no-deps -e /prsrc && (pip install --no-cache-dir -e ".[test]" || pip install --no-cache-dir pytest) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:01.520894177-05:00 INFO[0000] Unpacking rootfs as cmd COPY . /prsrc requires it. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:10.090843821-05:00 INFO[0008] COPY . /prsrc [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:10.129580176-05:00 INFO[0008] Taking snapshot of files... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:10.191718698-05:00 INFO[0008] WORKDIR /prsrc [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:10.191740598-05:00 INFO[0008] Cmd: workdir [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:10.191743928-05:00 INFO[0008] Changed working directory to /prsrc [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:10.191813488-05:00 INFO[0008] No files changed in this command, skipping snapshotting. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:10.191830378-05:00 INFO[0008] RUN pip install --no-cache-dir --no-deps -e /prsrc && (pip install --no-cache-dir -e ".[test]" || pip install --no-cache-dir pytest) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:10.191833278-05:00 INFO[0008] Initializing snapshotter ... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:10.191836298-05:00 INFO[0008] Taking snapshot of full filesystem... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:19.650775956-05:00 INFO[0018] Cmd: /bin/sh [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:19.650798326-05:00 INFO[0018] Args: [-c pip install --no-cache-dir --no-deps -e /prsrc && (pip install --no-cache-dir -e ".[test]" || pip install --no-cache-dir pytest)] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:19.650801396-05:00 INFO[0018] Running: [/bin/sh -c pip install --no-cache-dir --no-deps -e /prsrc && (pip install --no-cache-dir -e ".[test]" || pip install --no-cache-dir pytest)] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:19.979684122-05:00 Obtaining file:///prsrc [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:19.981614075-05:00 Installing build dependencies: started [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.016078491-05:00 Installing build dependencies: finished with status 'done' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.016871168-05:00 Checking if build backend supports build_editable: started [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.134249261-05:00 Checking if build backend supports build_editable: finished with status 'done' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.135259308-05:00 Getting requirements to build editable: started [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.298132499-05:00 Getting requirements to build editable: finished with status 'done' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.299307695-05:00 Preparing editable metadata (pyproject.toml): started [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.454639953-05:00 Preparing editable metadata (pyproject.toml): finished with status 'done' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.496468935-05:00 Building wheels for collected packages: storeminder [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.497227162-05:00 Building editable for storeminder (pyproject.toml): started [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.668743033-05:00 Building editable for storeminder (pyproject.toml): finished with status 'done' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.669350820-05:00 Created wheel for storeminder: filename=storeminder-0.1.0-0.editable-py3-none-any.whl size=1909 sha256=5378745a61577ca4ed3b2d17db15f0a8dd265fc07ac524ac7a39603db082d42f [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.669479210-05:00 Stored in directory: /tmp/pip-ephem-wheel-cache-wvvj3kwg/wheels/12/13/ba/810e66e2be1a8a55fa7c56f5f12815f8d07be3aad27f734654 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.671842112-05:00 Successfully built storeminder [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.672040931-05:00 Installing collected packages: storeminder [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.672167780-05:00 Attempting uninstall: storeminder [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.711337821-05:00 Found existing installation: storeminder 0.1.0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.716273834-05:00 Uninstalling storeminder-0.1.0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.717825718-05:00 Successfully uninstalled storeminder-0.1.0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.770987599-05:00 Successfully installed storeminder-0.1.0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.771183839-05:00 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.894406011-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.894426391-05:00 [notice] A new release of pip is available: 24.0 -> 26.1.2 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:22.894429871-05:00 [notice] To update, run: pip install --upgrade pip [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:23.294599608-05:00 Obtaining file:///prsrc [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:23.296448501-05:00 Installing build dependencies: started [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.132419115-05:00 Installing build dependencies: finished with status 'done' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.133183382-05:00 Checking if build backend supports build_editable: started [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.236879062-05:00 Checking if build backend supports build_editable: finished with status 'done' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.237854139-05:00 Getting requirements to build editable: started [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.390018026-05:00 Getting requirements to build editable: finished with status 'done' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.391000893-05:00 Preparing editable metadata (pyproject.toml): started [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.537817849-05:00 Preparing editable metadata (pyproject.toml): finished with status 'done' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.597130408-05:00 Requirement already satisfied: pandas>=1.5.0 in /usr/local/lib/python3.11/site-packages (from storeminder==0.1.0) (3.0.3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.597819105-05:00 Requirement already satisfied: numpy>=1.23.0 in /usr/local/lib/python3.11/site-packages (from storeminder==0.1.0) (2.4.6) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.598580553-05:00 Requirement already satisfied: requests>=2.28.0 in /usr/local/lib/python3.11/site-packages (from storeminder==0.1.0) (2.34.2) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.599186830-05:00 Requirement already satisfied: openpyxl>=3.0.0 in /usr/local/lib/python3.11/site-packages (from storeminder==0.1.0) (3.1.5) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.599826968-05:00 Requirement already satisfied: anthropic>=0.25.0 in /usr/local/lib/python3.11/site-packages (from storeminder==0.1.0) (0.116.0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.600939824-05:00 Requirement already satisfied: psycopg>=3.1 in /usr/local/lib/python3.11/site-packages (from psycopg[binary,pool]>=3.1->storeminder==0.1.0) (3.3.4) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.602150900-05:00 Requirement already satisfied: boto3>=1.34 in /usr/local/lib/python3.11/site-packages (from storeminder==0.1.0) (1.43.46) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.602742588-05:00 Requirement already satisfied: PyYAML>=6.0 in /usr/local/lib/python3.11/site-packages (from storeminder==0.1.0) (6.0.3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.773261840-05:00 Collecting pytest>=7 (from storeminder==0.1.0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.838899946-05:00 Downloading pytest-9.1.1-py3-none-any.whl.metadata (7.6 kB) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.848855610-05:00 Requirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.11/site-packages (from anthropic>=0.25.0->storeminder==0.1.0) (4.14.2) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.849662057-05:00 Requirement already satisfied: distro<2,>=1.7.0 in /usr/local/lib/python3.11/site-packages (from anthropic>=0.25.0->storeminder==0.1.0) (1.9.0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.850332745-05:00 Requirement already satisfied: docstring-parser<1,>=0.15 in /usr/local/lib/python3.11/site-packages (from anthropic>=0.25.0->storeminder==0.1.0) (0.18.0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.851100962-05:00 Requirement already satisfied: httpx<1,>=0.25.0 in /usr/local/lib/python3.11/site-packages (from anthropic>=0.25.0->storeminder==0.1.0) (0.28.1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.851882649-05:00 Requirement already satisfied: jiter<1,>=0.4.0 in /usr/local/lib/python3.11/site-packages (from anthropic>=0.25.0->storeminder==0.1.0) (0.16.0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.853935602-05:00 Requirement already satisfied: pydantic<3,>=1.9.0 in /usr/local/lib/python3.11/site-packages (from anthropic>=0.25.0->storeminder==0.1.0) (2.13.4) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.854426210-05:00 Requirement already satisfied: sniffio in /usr/local/lib/python3.11/site-packages (from anthropic>=0.25.0->storeminder==0.1.0) (1.3.1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.855095578-05:00 Requirement already satisfied: typing-extensions<5,>=4.14 in /usr/local/lib/python3.11/site-packages (from anthropic>=0.25.0->storeminder==0.1.0) (4.16.0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.858411596-05:00 Requirement already satisfied: botocore<1.44.0,>=1.43.46 in /usr/local/lib/python3.11/site-packages (from boto3>=1.34->storeminder==0.1.0) (1.43.46) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.859177473-05:00 Requirement already satisfied: jmespath<2.0.0,>=0.7.1 in /usr/local/lib/python3.11/site-packages (from boto3>=1.34->storeminder==0.1.0) (1.1.0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.859805341-05:00 Requirement already satisfied: s3transfer<0.20.0,>=0.19.0 in /usr/local/lib/python3.11/site-packages (from boto3>=1.34->storeminder==0.1.0) (0.19.1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.862383562-05:00 Requirement already satisfied: et-xmlfile in /usr/local/lib/python3.11/site-packages (from openpyxl>=3.0.0->storeminder==0.1.0) (2.0.0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.902479799-05:00 Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.11/site-packages (from pandas>=1.5.0->storeminder==0.1.0) (2.9.0.post0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.935813020-05:00 Requirement already satisfied: psycopg-binary==3.3.4 in /usr/local/lib/python3.11/site-packages (from psycopg[binary,pool]>=3.1->storeminder==0.1.0) (3.3.4) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.936712057-05:00 Requirement already satisfied: psycopg-pool in /usr/local/lib/python3.11/site-packages (from psycopg[binary,pool]>=3.1->storeminder==0.1.0) (3.3.1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.969929608-05:00 Collecting iniconfig>=1.0.1 (from pytest>=7->storeminder==0.1.0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.986597129-05:00 Downloading iniconfig-2.3.0-py3-none-any.whl.metadata (2.5 kB) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:25.988707941-05:00 Requirement already satisfied: packaging>=22 in /usr/local/lib/python3.11/site-packages (from pytest>=7->storeminder==0.1.0) (26.2) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.014112851-05:00 Collecting pluggy<2,>=1.5 (from pytest>=7->storeminder==0.1.0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.033481452-05:00 Downloading pluggy-1.6.0-py3-none-any.whl.metadata (4.8 kB) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.035649184-05:00 Requirement already satisfied: pygments>=2.7.2 in /usr/local/lib/python3.11/site-packages (from pytest>=7->storeminder==0.1.0) (2.20.0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.042023821-05:00 Requirement already satisfied: charset_normalizer<4,>=2 in /usr/local/lib/python3.11/site-packages (from requests>=2.28.0->storeminder==0.1.0) (3.4.9) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.042832218-05:00 Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.11/site-packages (from requests>=2.28.0->storeminder==0.1.0) (3.18) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.043561896-05:00 Requirement already satisfied: urllib3<3,>=1.26 in /usr/local/lib/python3.11/site-packages (from requests>=2.28.0->storeminder==0.1.0) (2.7.0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.044183643-05:00 Requirement already satisfied: certifi>=2023.5.7 in /usr/local/lib/python3.11/site-packages (from requests>=2.28.0->storeminder==0.1.0) (2026.6.17) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.083613773-05:00 Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.11/site-packages (from httpx<1,>=0.25.0->anthropic>=0.25.0->storeminder==0.1.0) (1.0.9) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.088948013-05:00 Requirement already satisfied: h11>=0.16 in /usr/local/lib/python3.11/site-packages (from httpcore==1.*->httpx<1,>=0.25.0->anthropic>=0.25.0->storeminder==0.1.0) (0.16.0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.109601800-05:00 Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.11/site-packages (from pydantic<3,>=1.9.0->anthropic>=0.25.0->storeminder==0.1.0) (0.7.0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.110323417-05:00 Requirement already satisfied: pydantic-core==2.46.4 in /usr/local/lib/python3.11/site-packages (from pydantic<3,>=1.9.0->anthropic>=0.25.0->storeminder==0.1.0) (2.46.4) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.111290704-05:00 Requirement already satisfied: typing-inspection>=0.4.2 in /usr/local/lib/python3.11/site-packages (from pydantic<3,>=1.9.0->anthropic>=0.25.0->storeminder==0.1.0) (0.4.2) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.119840003-05:00 Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.11/site-packages (from python-dateutil>=2.8.2->pandas>=1.5.0->storeminder==0.1.0) (1.17.0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.175954403-05:00 Downloading pytest-9.1.1-py3-none-any.whl (386 kB) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.388022676-05:00 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 386.5/386.5 kB 1.9 MB/s eta 0:00:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.406900628-05:00 Downloading iniconfig-2.3.0-py3-none-any.whl (7.5 kB) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.422220234-05:00 Downloading pluggy-1.6.0-py3-none-any.whl (20 kB) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.432042609-05:00 Checking if build backend supports build_editable: started [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.535117691-05:00 Checking if build backend supports build_editable: finished with status 'done' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.535598569-05:00 Building wheels for collected packages: storeminder [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.536598295-05:00 Building editable for storeminder (pyproject.toml): started [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.701720216-05:00 Building editable for storeminder (pyproject.toml): finished with status 'done' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.702395283-05:00 Created wheel for storeminder: filename=storeminder-0.1.0-0.editable-py3-none-any.whl size=1909 sha256=6bc8c5f2b744f06f1923c34fad4649dc467150ea4860c1b5e07d330eee193ee0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.702514273-05:00 Stored in directory: /tmp/pip-ephem-wheel-cache-lmo0mvme/wheels/12/13/ba/810e66e2be1a8a55fa7c56f5f12815f8d07be3aad27f734654 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:26.704629745-05:00 Successfully built storeminder [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:27.383637119-05:00 Installing collected packages: pluggy, iniconfig, pytest, storeminder [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:27.604968688-05:00 Attempting uninstall: storeminder [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:27.645967811-05:00 Found existing installation: storeminder 0.1.0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:27.647432476-05:00 Uninstalling storeminder-0.1.0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:27.788672441-05:00 Successfully uninstalled storeminder-0.1.0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:27.959707880-05:00 Successfully installed iniconfig-2.3.0 pluggy-1.6.0 pytest-9.1.1 storeminder-0.1.0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:27.959878969-05:00 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:28.099613329-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:28.099633369-05:00 [notice] A new release of pip is available: 24.0 -> 26.1.2 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:28.099637039-05:00 [notice] To update, run: pip install --upgrade pip [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:28.190416344-05:00 INFO[0026] Taking snapshot of full filesystem... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:29.863124443-05:00 INFO[0028] Pushing layer registry.storeminder.ai/images/cache:6ec5107d3ea97e691f166493d9b7501f63dee56803729c6a4334b8edd54fa452 to cache now [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:29.863324023-05:00 INFO[0028] Pushing image to registry.storeminder.ai/images/cache:6ec5107d3ea97e691f166493d9b7501f63dee56803729c6a4334b8edd54fa452 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:30.101290219-05:00 INFO[0028] Pushed registry.storeminder.ai/images/cache@sha256:03573cc84fbb7f1d6173e6d2720b474047940e6e940b9fbca6f8cf5cd499877e [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:30.101910147-05:00 INFO[0028] Pushing image to registry.storeminder.ai/images/storeminder-report-check:pr250-d15c4b94b94c40ce1e7fefa13dcc806acd7ccee5 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-build-pr-image] 2026-07-12T18:39:30.403735074-05:00 INFO[0029] Pushed registry.storeminder.ai/images/storeminder-report-check@sha256:1db7ab1773ee012ea6be176bf5c678b848eb49e9e9e0ca6bafe5eec3d40dd5eb [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:39:30.802874532-05:00 /tekton/scripts/script-2-4n65x: line 25: steps.build-pr-image.exitCode.path: command not found [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:39:30.906656130-05:00 job.batch/report-live-pr250-d15c4b9 created [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:41:59.797412776-05:00 ----- live Job logs (report-live-pr250-d15c4b9) ----- [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347613306-05:00 Running live report trait suite (real Vertex + DB, DRY_RUN): tests/test_report_traits_live.py [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347632376-05:00 ============================= test session starts ============================== [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347636626-05:00 platform linux -- Python 3.11.15, pytest-9.1.1, pluggy-1.6.0 -- /usr/local/bin/python [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347641146-05:00 cachedir: .pytest_cache [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347644986-05:00 rootdir: /prsrc [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347649676-05:00 configfile: pyproject.toml [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347652586-05:00 plugins: anyio-4.14.2 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347655386-05:00 collecting ... collected 10 items [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347658026-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347660716-05:00 tests/test_report_traits_live.py::test_daily_builds_and_renders ERROR [ 10%] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347663406-05:00 tests/test_report_traits_live.py::test_daily_is_sendable_and_no_weekly_leak ERROR [ 20%] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347666036-05:00 tests/test_report_traits_live.py::test_daily_headline_labeled_total_sales ERROR [ 30%] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347668696-05:00 tests/test_report_traits_live.py::test_daily_sms_not_truncated ERROR [ 40%] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347671336-05:00 tests/test_report_traits_live.py::test_daily_artifact_bundle_written ERROR [ 50%] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347673986-05:00 tests/test_report_traits_live.py::test_weekly_builds_and_renders ERROR [ 60%] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347677356-05:00 tests/test_report_traits_live.py::test_weekly_is_sendable ERROR [ 70%] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347680126-05:00 tests/test_report_traits_live.py::test_weekly_headline_labeled_total_sales ERROR [ 80%] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347682806-05:00 tests/test_report_traits_live.py::test_weekly_sms_not_truncated ERROR [ 90%] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347685475-05:00 tests/test_report_traits_live.py::test_weekly_artifact_bundle_written ERROR [100%] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347687995-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347690695-05:00 ==================================== ERRORS ==================================== [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347693365-05:00 _______________ ERROR at setup of test_daily_builds_and_renders ________________ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347695905-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347698565-05:00 @pytest.fixture(scope="module") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347701965-05:00 def daily_report(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347704665-05:00 > return _run("daily") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347708095-05:00 ^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347710585-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347713175-05:00 tests/test_report_traits_live.py:94: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347715895-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347718565-05:00 tests/test_report_traits_live.py:75: in _run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347721795-05:00 res = fn(store, end_date=d, dry_run=True, deliver=False, clean=True) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347724585-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347727195-05:00 src/storeminder/runner.py:340: in run_daily [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347729845-05:00 return _run_report(params, clean=clean) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347733965-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347736565-05:00 src/storeminder/runner.py:193: in _run_report [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347739145-05:00 result = run_pipeline( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347741705-05:00 src/storeminder/crew.py:463: in run_pipeline [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347755905-05:00 report_result = _agent.run_daily( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347757945-05:00 src/storeminder/agents/synthesis_agent.py:1023: in run_daily [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347759665-05:00 return self.run(report_type="daily", **kwargs) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347761235-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347762895-05:00 src/storeminder/agents/synthesis_agent.py:1073: in run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347764465-05:00 user = self._build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347766105-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347767625-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347769315-05:00 self = [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347771415-05:00 report_date = datetime.date(2026, 7, 10), store_id = 'Fastime' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347773155-05:00 trans = {'total_transactions': 618, 'open_hour_start': 4, 'open_hour_end': 21, 'report_date_filter': '2026-07-10', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347774865-05:00 scan = {'total_scan_rows': 1940, 'report_date_filter': '2026-07-10', 'total_sales': 6436.97, 'avg_item_price': 3.88, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347776805-05:00 baseline = {'sample_count': 15, 'day_of_week': 4, 'total_revenue': 16270.22, 'total_transactions': 590.87, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347778495-05:00 flags = [{'metric': 'rank_drop', 'label': 'Rank Drop: COPENHAGEN FINE CUT', 'today': 40, 'baseline': 13, ...}, {'metric': 'ran...': 29, ...}, {'metric': 'rank_drop', 'label': 'Rank Drop: BUD LIGHT BEER 12 O', 'today': 33, 'baseline': 22, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347780845-05:00 fuel_section = '## 2. Fuel \n**2,583 gal | $9,446 | 279 fuel transactions** \nFuel volume came in at 2,583 gallons, down from 2,758...w | Confirm pump access and functionality during 21:00–23:00 window | Measure: Fuel volume in last two operating hours' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347782605-05:00 inside_section = '## 3. Inside Sales\n\n**Inside Sales: $6,436.97 (+9.5% vs normal Monday)** \nTobacco / Nicotine surged, but Foodserv...e (1 void, $25.64) and override logs | Because: Void rate 100% above baseline | Measure: Closeout discipline confirmed' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347784295-05:00 weather = {'yesterday': {'temp_max': 90.1, 'temp_min': 76.8, 'precip_in': 0.0, 'conditions': 'Partly cloudy'}, 'today_forecast':...rtly cloudy'}, 'rain_hours_today': [], 'weather_impact_assessment': 'Low — weather was mild, not a significant factor'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347790935-05:00 external_section = '## External Context\n\n**Google Reviews (5 reviews)** \nAverage rating of recent reviews (3.0/5) is below overall st...y 12:00 PM \n- Review staff scheduling: confirm if high-performing employee (Luna) is on shift today — Do by 10:00 AM' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347793845-05:00 loyalty_section = '## Loyalty & Promotions\n\n**Redemption Summary**\n| Promotion | Item | Today | Period Avg | Delta | Status |\n|-----...y EOD Friday \n- Confirm in-store visibility for underperforming tobacco and energy drink promos — Do by EOD Thursday' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347797795-05:00 ops_section = '## 5. Operations\n\n**Transactions & Throughput** \n962 total transactions | Avg ticket: $17.15 (+2% vs normal Frida...on void authorization protocol | Because: elevated voids in high-volume period | Measure: Reduce voids to ≤2 per shift' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347800145-05:00 fuel_pricing_section = '## Fuel Pricing\n\n**Current Market** \n| Station | Regular | Midgrade | Premium | Diesel | Distance | \n|---------...1mi and priced at $3.400. \n- Update competitor price tracking—several stations have outdated or missing diesel data.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347801955-05:00 prior_actions = None, report_type = 'daily', weekly_days = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347803555-05:00 dow_profile = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347809455-05:00 kwargs = {'category_baselines': None, 'schedule_section': None, 'weekly_category_totals': None, 'ext_fetch_date': '2026-07-12', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347811015-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347812595-05:00 def _build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347814215-05:00 self, report_date, store_id, trans, scan, baseline, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347815895-05:00 flags, fuel_section, inside_section, weather, external_section, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347817515-05:00 loyalty_section, ops_section, fuel_pricing_section, prior_actions, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347819135-05:00 report_type: str = "daily", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347820765-05:00 weekly_days: Optional[List[Dict]] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347822295-05:00 dow_profile: Optional[Dict] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347823925-05:00 **kwargs, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347825515-05:00 ) -> str: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347827065-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347829365-05:00 # ── KPI table ───────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347831035-05:00 kpi_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347832895-05:00 "| Metric | Today | Baseline | Delta | Priority |", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347834955-05:00 "|--------|-------|----------|-------|----------|", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347836535-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347838095-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347839735-05:00 # Keys that live in scan_summary (inside sales) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347841375-05:00 _scan_keys = {'total_sales', 'avg_item_price', 'total_scan_rows'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347842965-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347844625-05:00 def kpi_row(label, key, fmt_str=",.0f", prefix="", priority=None): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347846395-05:00 # Prefer scan for inside-sales metrics, trans for everything else [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347848925-05:00 if key in _scan_keys: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347850605-05:00 t = scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347852235-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347853885-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347855515-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347857075-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347858625-05:00 return [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347860255-05:00 t_str = f"{prefix}{t:{fmt_str}}" if isinstance(t, (int, float)) else str(t) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347861915-05:00 b_str = f"{prefix}{b:{fmt_str}}" if isinstance(b, (int, float)) else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347863585-05:00 delta = f"{((t-b)/b*100):+.1f}%" if isinstance(t,(int,float)) and isinstance(b,(int,float)) and b else "—" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347865155-05:00 # Priority: use flag severity if exists, else spec default [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347866775-05:00 flag = next((f for f in flags if f['metric'] == key), None) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347868315-05:00 pri = flag['severity'] if flag else (priority or "—") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347869965-05:00 kpi_lines.append(f"| {label} | {t_str} | {b_str} | {delta} | {pri} |") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347871515-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347873085-05:00 kpi_row("Total Transactions", "total_transactions", ",.0f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347874675-05:00 kpi_row("Total Sales", "total_revenue", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347876835-05:00 kpi_row("Inside Sales", "total_sales", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347884025-05:00 kpi_row("Avg Inside Ticket", "avg_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347885795-05:00 kpi_row("Avg Fuel Ticket", "avg_fuel_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347887455-05:00 kpi_row("Fuel Gallons", "fuel_gallons", ",.1f", "", "HIGH") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347889095-05:00 kpi_row("Fuel Sales $", "fuel_sales", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347890715-05:00 kpi_row("Fuel Transactions", "fuel_transactions", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347893155-05:00 kpi_row("Avg Gallons/Tx", "avg_gallons_per_tx", ",.2f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347897795-05:00 kpi_row("Forecourt Conv %", "forecourt_conversion_pct", ",.1f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347900385-05:00 kpi_row("Void Count", "void_count", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347903005-05:00 kpi_row("Void Total $", "void_total_amt", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347905765-05:00 kpi_row("Inside/100 Gal (weekly avg)", "inside_per_100_gal", ",.0f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347908195-05:00 kpi_row("Eff. Fuel Price", "effective_fuel_price", ",.3f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347910645-05:00 kpi_row("Inside Share %", "inside_sales_share_pct", ",.1f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347913295-05:00 kpi_summary = "\n".join(kpi_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347915815-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347926355-05:00 # ── Derived metrics for snapshot header ──────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347930895-05:00 def _dm(key, label, prefix="", suffix="", decimals=2): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347933285-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347936385-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347938715-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347940875-05:00 return None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347942445-05:00 t_str = f"{prefix}{float(t):,.{decimals}f}{suffix}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347943985-05:00 if b and float(b) > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347945645-05:00 pct = (float(t) - float(b)) / float(b) * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347947225-05:00 return f"{label}: {t_str} ({pct:+.1f}% vs baseline)" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347948785-05:00 return f"{label}: {t_str}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347950375-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347951965-05:00 dm_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347953595-05:00 v = _dm('inside_per_100_gal', 'Inside/100gal', '$', '', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347955165-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347956805-05:00 v = _dm('effective_fuel_price', 'Effective fuel price', '$', '/gal', 3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347958465-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347960225-05:00 v = _dm('inside_sales_share_pct','Inside share of sales', '', '%', 1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347961785-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347963355-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347964905-05:00 gap = trans.get('fuel_inside_gap_pct') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347966484-05:00 if gap is not None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347968124-05:00 direction = "FUEL OUTPACING INSIDE" if gap > 0 else "INSIDE OUTPACING FUEL" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347973124-05:00 dm_lines.append(f"Fuel-vs-Inside gap: {gap:+.1f}pp ({direction})") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347974704-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347976404-05:00 derived_metrics = "\n".join(dm_lines) if dm_lines else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347977974-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347980254-05:00 # ── Flags summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347981834-05:00 if flags: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347983474-05:00 flags_str = "\n".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347985114-05:00 f"- [{f['severity']}] {f['label']}: {f['note']}" for f in flags) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347986694-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347988284-05:00 flags_str = "No significant anomalies detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347990604-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347993534-05:00 # ── Shift summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347995804-05:00 shifts = trans.get('shift_summary', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347997864-05:00 if shifts: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.347999864-05:00 shift_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348001904-05:00 for name, data in shifts.items(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348004094-05:00 rev = data.get('revenue', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348006444-05:00 tx = data.get('transactions', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348008624-05:00 gals = data.get('fuel_gallons', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348010774-05:00 shift_lines.append(f"- {name}: ${rev:,.2f} inside | {tx} tx | {gals:,.1f} gal fuel") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348012934-05:00 shift_summary = "\n".join(shift_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348015174-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348018554-05:00 shift_summary = "Shift data not available." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348020734-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348023134-05:00 # ── Gap / zero-tx hours ─────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348025364-05:00 gaps = trans.get('top_hourly_gaps', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348027694-05:00 zero_tx = trans.get('zero_tx_hours', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348030154-05:00 gap_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348032554-05:00 if gaps: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348034944-05:00 gap_str = ", ".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348037284-05:00 "{:02d}:00 ({} tx)".format(g['_hour'], g['tx_count']) for g in gaps) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348038854-05:00 gap_lines.append(f"Top gap hours: {gap_str}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348040484-05:00 if zero_tx: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348042164-05:00 gap_lines.append(f"Zero-tx hours (open window): {zero_tx}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348043834-05:00 gap_summary = "\n".join(gap_lines) if gap_lines else "No unusual gaps detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348045774-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348047874-05:00 # Authoritative store hours (issue #128): when Google Places supplies the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348049544-05:00 # store's real hours, prepend them and instruct the model to treat [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348051174-05:00 # off-hours zero activity as expected closure rather than hedging. The [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348053364-05:00 # gap/zero-tx figures above are already computed against this same window. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348058024-05:00 store_hours_val = kwargs.get('store_hours') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348065514-05:00 if store_hours_val and store_hours_val.get('weekday_text'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348067524-05:00 hrs_lines = ["AUTHORITATIVE STORE HOURS (Google Places — treat as ground truth):"] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348069254-05:00 hrs_lines += [f" {ln}" for ln in store_hours_val['weekday_text']] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348070834-05:00 if store_hours_val.get('open_24h'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348072444-05:00 hrs_lines.append(" This store operates 24 hours on at least one day.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348074014-05:00 hrs_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348075674-05:00 "Zero-transaction hours OUTSIDE these posted hours are EXPECTED " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348077384-05:00 "CLOSURE — do NOT flag them as gaps and do NOT hedge about whether " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348078984-05:00 "the store was open. Treat zero/low activity DURING posted open " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348080684-05:00 "hours as a real gap worth investigating.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348082334-05:00 gap_summary = "\n".join(hrs_lines) + "\n\n" + gap_summary [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348084224-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348085964-05:00 # ── Weather ─────────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348087564-05:00 if weather: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348089114-05:00 temp = weather.get('temp', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348090674-05:00 precip = weather.get('precipitation_in', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348092264-05:00 conds = list(weather.get('conditions', {}).keys()) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348093884-05:00 impact = weather.get('weather_impact_assessment', '') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348095434-05:00 yd = weather.get('yesterday', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348096994-05:00 td = weather.get('today_forecast', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348098534-05:00 rain_hrs= weather.get('rain_hours_today', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348100094-05:00 weather_str = ( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348101654-05:00 f"Report day: {yd.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348103874-05:00 f"{yd.get('temp_min','?')}–{yd.get('temp_max','?')}°F | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348105534-05:00 f"{yd.get('precip_in',0)}\" precip\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348107174-05:00 f"Today forecast: {td.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348108834-05:00 f"{td.get('temp_min','?')}–{td.get('temp_max','?')}°F\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348110444-05:00 f"Impact assessment: {impact}\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348112134-05:00 + (f"Rain hours: {rain_hrs}\n" if rain_hrs else "") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348113794-05:00 ) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348115354-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348116954-05:00 weather_str = "No weather data provided." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348118604-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348120294-05:00 # ── Schedule section ────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348121964-05:00 schedule_section = kwargs.get('schedule_section', None) or '' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348123584-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348125284-05:00 # ── Category baselines ──────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348129804-05:00 cat_baselines = kwargs.get('category_baselines', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348131484-05:00 # weekly_category_totals: actual accumulated weekly revenue per category [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348133144-05:00 # passed from Cell 39 _week_cat — USE THESE VERBATIM in the report table [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348134954-05:00 weekly_cat_totals = kwargs.get('weekly_category_totals', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348136534-05:00 _is_weekly = (report_type == 'weekly') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348138154-05:00 # Authoritative per-category revenue comes from the owner-mapped [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348139764-05:00 # category_mix (preprocessor). Post-mapping it is aggregated by taxonomy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348141384-05:00 # name and already reconciles to inside sales, so it — NOT the seeded [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348142994-05:00 # baselines — must drive the category table. Previously this block only [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348144614-05:00 # emitted categories that had a seeded history baseline; when baselines [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348146794-05:00 # were sparse or unseeded the model received no authoritative totals and [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348148464-05:00 # dumped the bulk of inside sales into "Other / unclassified" (issue #81). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348150054-05:00 _cat_mix_rows = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348152424-05:00 c for c in scan.get('category_mix', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348154064-05:00 if isinstance(c.get('category'), str) and str(c.get('category')).strip() [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348155614-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348157244-05:00 _period_lbl = 'This Week' if _is_weekly else 'Today' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348158824-05:00 if _is_weekly and weekly_cat_totals: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348160444-05:00 # Legacy notebook path passes pre-accumulated weekly totals verbatim. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348162074-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348163744-05:00 "WEEKLY Category Totals — USE THESE EXACT NUMBERS in Section 3 table.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348165404-05:00 "DO NOT invent or estimate different values. These are computed from actual data.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348167044-05:00 f" {'Category':<30} {'This Week':>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348168604-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348174184-05:00 for cat, week_rev in sorted(weekly_cat_totals.items(), [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348176824-05:00 key=lambda x: -x[1]): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348179164-05:00 if not isinstance(cat, str) or not cat.strip(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348181604-05:00 continue [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348183234-05:00 avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348184894-05:00 if avg > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348186944-05:00 pct = (week_rev - avg) / avg * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348188564-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348190274-05:00 f" {cat:<30} ${week_rev:>10,.0f} ${avg:>10,.0f}/day {pct:>+7.1f}%") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348191844-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348193584-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348195254-05:00 f" {cat:<30} ${week_rev:>10,.0f} (no baseline)") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348196924-05:00 category_baselines_str = "\n".join(_cb_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348199234-05:00 elif _cat_mix_rows: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348200894-05:00 _classified = sum((c.get('revenue') or 0) for c in _cat_mix_rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348205794-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348207554-05:00 f"Per-category inside revenue ({_period_lbl}) — USE THESE EXACT NUMBERS " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348209214-05:00 f"in the Section 3 category table. They are computed from the owner's " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348210974-05:00 f"merchandise-code taxonomy and already sum to inside sales " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348212954-05:00 f"(${_classified:,.0f}). Only the row explicitly labelled " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348214564-05:00 f"'Other / Unclassified' is unclassified — do NOT move any other " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348216324-05:00 f"category's revenue into 'Other', and do NOT invent different values.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348217914-05:00 f" {'Category':<30} {_period_lbl:>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348219494-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348221314-05:00 for c in sorted(_cat_mix_rows, key=lambda x: -(x.get('revenue') or 0)): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348222874-05:00 cat = str(c['category']) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348224854-05:00 rev = c.get('revenue') or 0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348226484-05:00 > avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348228024-05:00 ^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348229684-05:00 E AttributeError: 'NoneType' object has no attribute 'get' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348231174-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348232744-05:00 src/storeminder/agents/synthesis_agent.py:1426: AttributeError [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348234454-05:00 ---------------------------- Captured stdout setup ----------------------------- [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348236103-05:00 Found 2 XML file(s) in /scratch/v6/Fastime/2026-07-10/raw [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348238513-05:00 Parsed: 2026-07-10.369.xml (966 tx rows, 1940 scan rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348240163-05:00 Parsed: 2026-07-10.527.xml (966 tx rows, 1940 scan rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348241673-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348243213-05:00 Transactions : 966 rows [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348244833-05:00 Date range : 2026-07-09 20:46:32 → 2026-07-10 20:34:48 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348246483-05:00 Types : [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348248103-05:00 Type [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348249773-05:00 sale 618 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348251453-05:00 network sale 277 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348253853-05:00 error_correct 40 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348255423-05:00 safedrop 15 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348256983-05:00 cashier_open 4 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348258593-05:00 cashier_close 4 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348260143-05:00 cashier_audit 4 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348261683-05:00 payout 2 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348263233-05:00 refundevent 1 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348264813-05:00 void 1 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348266293-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348267923-05:00 Scanned items: 1940 rows [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348269553-05:00 Line types : [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348271113-05:00 Line Type [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348272703-05:00 item 1626 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348274293-05:00 fuel 279 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348275863-05:00 merch_code 35 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348277503-05:00 Fuel volume: 2,582.638 gal [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348278993-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348280623-05:00 Saved → /scratch/v6/Fastime/2026-07-10/curated/combined_raw_transactions_20260712_233947.csv [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348283083-05:00 Saved → /scratch/v6/Fastime/2026-07-10/curated/combined_raw_scanned_20260712_233947.csv [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348287403-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348289053-05:00 ============================================================ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348290663-05:00 C-Store Analytics — v4 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348292253-05:00 Store: Fastime | Date: 2026-07-10 | Lang: English [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348293933-05:00 ============================================================ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348295413-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348296943-05:00 📂 [1/10] Loading data... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348298903-05:00 ✓ Loaded 1,940 scan rows | cols: ['time', 'date', 'Type', 'Transaction ID', 'Event Seq ID', 'Store ID', 'Register ID', 'Cashier ID', 'Payment Type', 'Payment Sub Type', 'Source File', 'Line Seq', 'Line Status', 'category', 'UPC Code', 'item', 'Entry Method', 'Merchandise Code', 'Sales Qty', 'Sales UOM', 'amount', 'Regular Price', 'Actual Price', 'Price Override', 'Price Override Reason', 'Promotion ID', 'Promotion Reason', 'Promotion Amount', 'Tax Level', 'Age Restricted', 'Min Age', 'Fuel Grade ID', 'Fuel Position ID', 'Fuel UOM'] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348304893-05:00 ✓ Loaded 966 trans rows | cols: ['Time', 'End Time', 'Business Date', 'Type', 'Transaction ID', 'Event Seq ID', 'Store ID', 'Register ID', 'Cashier ID', 'Till ID', 'Outside Sale', 'Suspended', 'Training Mode', 'Offline', 'Gross Amount', 'Net Amount', 'Tax Amount', 'Total Amount', 'Grand Direction', 'Item Line Count', 'Fuel Line Count', 'Tender Count', 'Tender Type', 'Payment Sub Type', 'Change Amount', 'Void Reason', 'Cash In Drawer', 'Food Stamps', 'Financial Sub-Type', 'Financial Amount', 'Financial Tender', 'Memo', 'Loyalty Discount', 'Loyalty Program', 'Report Seq', 'Report Interval', 'Report Period', 'Report Begin', 'Report End', 'Source File', 'Vendor', 'Vendor Model', 'datetime'] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348307413-05:00 Places hours status: 200 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348309793-05:00 ✓ Store hours (Google): 4:00–21:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348313053-05:00 ✓ Scan: 1,940 rows | Trans: 618 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348314753-05:00 ✓ Fuel merged from scan: 2,582.6 gal | $9,446.27 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348316253-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348317813-05:00 📊 [2/10] Loading baseline... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348319483-05:00 ✓ Baseline: 15 same-DOW samples [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348320983-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348322593-05:00 🔍 [3/10] Anomaly detection... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348324173-05:00 ✓ 18 HIGH / 22 MEDIUM flags [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348325713-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348328003-05:00 🌐 [4/10] External context... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348329873-05:00 Places API (New) status: 200 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348331543-05:00 → 5 reviews returned (overall rating 4.3, 532 total ratings on Google) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348333103-05:00 ✓ Reviews: 5 fetched, avg 3.0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348334673-05:00 ✓ Weather: Partly cloudy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348336253-05:00 Routes API status: 200 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348337943-05:00 ✓ Traffic: Google Routes API (live road traffic) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348339623-05:00 ✓ External: 1827 chars | reviews=5 | traffic=LOW [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348341153-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348342773-05:00 ⛽ [5/10] Fuel Agent... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348344413-05:00 ✓ Fuel section written [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348345883-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348347453-05:00 💰 [6/10] Fuel Pricing Agent... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348349033-05:00 Fetching competitor fuel prices... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348350603-05:00 → 16 gas stations found with price data [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348352173-05:00 ✓ Fuel pricing section written [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348353683-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348355233-05:00 🛒 [7/10] Category Management Agent... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348360073-05:00 ✓ Category management section written [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348361683-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348363233-05:00 🎁 [8/10] Loyalty & Promotions Agent... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348364923-05:00 ✓ Loyalty section written [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348366423-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348367973-05:00 ⚙️ [9/10] Operations Agent... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348369573-05:00 ✓ Operations section written [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348371083-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348372643-05:00 📝 [10/10] Synthesis Agent... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348374343-05:00 _________ ERROR at setup of test_daily_is_sendable_and_no_weekly_leak __________ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348375833-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348377373-05:00 @pytest.fixture(scope="module") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348378963-05:00 def daily_report(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348380673-05:00 > return _run("daily") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348382223-05:00 ^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348383713-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348385243-05:00 tests/test_report_traits_live.py:94: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348386903-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348388483-05:00 tests/test_report_traits_live.py:75: in _run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348390133-05:00 res = fn(store, end_date=d, dry_run=True, deliver=False, clean=True) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348392203-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348393773-05:00 src/storeminder/runner.py:340: in run_daily [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348395313-05:00 return _run_report(params, clean=clean) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348396853-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348398393-05:00 src/storeminder/runner.py:193: in _run_report [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348399943-05:00 result = run_pipeline( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348401493-05:00 src/storeminder/crew.py:463: in run_pipeline [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348403023-05:00 report_result = _agent.run_daily( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348404623-05:00 src/storeminder/agents/synthesis_agent.py:1023: in run_daily [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348406173-05:00 return self.run(report_type="daily", **kwargs) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348407773-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348409773-05:00 src/storeminder/agents/synthesis_agent.py:1073: in run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348412153-05:00 user = self._build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348413753-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348415233-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348416853-05:00 self = [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348418503-05:00 report_date = datetime.date(2026, 7, 10), store_id = 'Fastime' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348420183-05:00 trans = {'total_transactions': 618, 'open_hour_start': 4, 'open_hour_end': 21, 'report_date_filter': '2026-07-10', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348421853-05:00 scan = {'total_scan_rows': 1940, 'report_date_filter': '2026-07-10', 'total_sales': 6436.97, 'avg_item_price': 3.88, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348423543-05:00 baseline = {'sample_count': 15, 'day_of_week': 4, 'total_revenue': 16270.22, 'total_transactions': 590.87, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348425243-05:00 flags = [{'metric': 'rank_drop', 'label': 'Rank Drop: COPENHAGEN FINE CUT', 'today': 40, 'baseline': 13, ...}, {'metric': 'ran...': 29, ...}, {'metric': 'rank_drop', 'label': 'Rank Drop: BUD LIGHT BEER 12 O', 'today': 33, 'baseline': 22, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348426913-05:00 fuel_section = '## 2. Fuel \n**2,583 gal | $9,446 | 279 fuel transactions** \nFuel volume came in at 2,583 gallons, down from 2,758...w | Confirm pump access and functionality during 21:00–23:00 window | Measure: Fuel volume in last two operating hours' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348433723-05:00 inside_section = '## 3. Inside Sales\n\n**Inside Sales: $6,436.97 (+9.5% vs normal Monday)** \nTobacco / Nicotine surged, but Foodserv...e (1 void, $25.64) and override logs | Because: Void rate 100% above baseline | Measure: Closeout discipline confirmed' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348436333-05:00 weather = {'yesterday': {'temp_max': 90.1, 'temp_min': 76.8, 'precip_in': 0.0, 'conditions': 'Partly cloudy'}, 'today_forecast':...rtly cloudy'}, 'rain_hours_today': [], 'weather_impact_assessment': 'Low — weather was mild, not a significant factor'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348438873-05:00 external_section = '## External Context\n\n**Google Reviews (5 reviews)** \nAverage rating of recent reviews (3.0/5) is below overall st...y 12:00 PM \n- Review staff scheduling: confirm if high-performing employee (Luna) is on shift today — Do by 10:00 AM' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348441583-05:00 loyalty_section = '## Loyalty & Promotions\n\n**Redemption Summary**\n| Promotion | Item | Today | Period Avg | Delta | Status |\n|-----...y EOD Friday \n- Confirm in-store visibility for underperforming tobacco and energy drink promos — Do by EOD Thursday' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348443553-05:00 ops_section = '## 5. Operations\n\n**Transactions & Throughput** \n962 total transactions | Avg ticket: $17.15 (+2% vs normal Frida...on void authorization protocol | Because: elevated voids in high-volume period | Measure: Reduce voids to ≤2 per shift' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348445263-05:00 fuel_pricing_section = '## Fuel Pricing\n\n**Current Market** \n| Station | Regular | Midgrade | Premium | Diesel | Distance | \n|---------...1mi and priced at $3.400. \n- Update competitor price tracking—several stations have outdated or missing diesel data.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348447273-05:00 prior_actions = None, report_type = 'daily', weekly_days = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348448913-05:00 dow_profile = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348450633-05:00 kwargs = {'category_baselines': None, 'schedule_section': None, 'weekly_category_totals': None, 'ext_fetch_date': '2026-07-12', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348452143-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348453723-05:00 def _build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348455313-05:00 self, report_date, store_id, trans, scan, baseline, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348456983-05:00 flags, fuel_section, inside_section, weather, external_section, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348458523-05:00 loyalty_section, ops_section, fuel_pricing_section, prior_actions, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348460103-05:00 report_type: str = "daily", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348461683-05:00 weekly_days: Optional[List[Dict]] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348463243-05:00 dow_profile: Optional[Dict] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348464823-05:00 **kwargs, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348466413-05:00 ) -> str: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348467973-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348469753-05:00 # ── KPI table ───────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348471373-05:00 kpi_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348472973-05:00 "| Metric | Today | Baseline | Delta | Priority |", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348474623-05:00 "|--------|-------|----------|-------|----------|", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348476273-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348477973-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348479553-05:00 # Keys that live in scan_summary (inside sales) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348481223-05:00 _scan_keys = {'total_sales', 'avg_item_price', 'total_scan_rows'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348485493-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348487153-05:00 def kpi_row(label, key, fmt_str=",.0f", prefix="", priority=None): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348488803-05:00 # Prefer scan for inside-sales metrics, trans for everything else [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348490383-05:00 if key in _scan_keys: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348491973-05:00 t = scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348493623-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348495293-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348496913-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348498473-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348500053-05:00 return [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348501753-05:00 t_str = f"{prefix}{t:{fmt_str}}" if isinstance(t, (int, float)) else str(t) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348503383-05:00 b_str = f"{prefix}{b:{fmt_str}}" if isinstance(b, (int, float)) else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348506102-05:00 delta = f"{((t-b)/b*100):+.1f}%" if isinstance(t,(int,float)) and isinstance(b,(int,float)) and b else "—" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348507762-05:00 # Priority: use flag severity if exists, else spec default [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348509342-05:00 flag = next((f for f in flags if f['metric'] == key), None) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348511062-05:00 pri = flag['severity'] if flag else (priority or "—") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348512742-05:00 kpi_lines.append(f"| {label} | {t_str} | {b_str} | {delta} | {pri} |") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348514322-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348515982-05:00 kpi_row("Total Transactions", "total_transactions", ",.0f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348517642-05:00 kpi_row("Total Sales", "total_revenue", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348519262-05:00 kpi_row("Inside Sales", "total_sales", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348520912-05:00 kpi_row("Avg Inside Ticket", "avg_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348525482-05:00 kpi_row("Avg Fuel Ticket", "avg_fuel_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348527912-05:00 kpi_row("Fuel Gallons", "fuel_gallons", ",.1f", "", "HIGH") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348530382-05:00 kpi_row("Fuel Sales $", "fuel_sales", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348532932-05:00 kpi_row("Fuel Transactions", "fuel_transactions", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348534912-05:00 kpi_row("Avg Gallons/Tx", "avg_gallons_per_tx", ",.2f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348536552-05:00 kpi_row("Forecourt Conv %", "forecourt_conversion_pct", ",.1f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348538102-05:00 kpi_row("Void Count", "void_count", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348539792-05:00 kpi_row("Void Total $", "void_total_amt", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348541352-05:00 kpi_row("Inside/100 Gal (weekly avg)", "inside_per_100_gal", ",.0f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348542902-05:00 kpi_row("Eff. Fuel Price", "effective_fuel_price", ",.3f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348544462-05:00 kpi_row("Inside Share %", "inside_sales_share_pct", ",.1f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348546022-05:00 kpi_summary = "\n".join(kpi_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348548292-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348549962-05:00 # ── Derived metrics for snapshot header ──────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348554192-05:00 def _dm(key, label, prefix="", suffix="", decimals=2): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348555992-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348557572-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348559162-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348560752-05:00 return None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348562372-05:00 t_str = f"{prefix}{float(t):,.{decimals}f}{suffix}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348564032-05:00 if b and float(b) > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348565602-05:00 pct = (float(t) - float(b)) / float(b) * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348567262-05:00 return f"{label}: {t_str} ({pct:+.1f}% vs baseline)" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348569822-05:00 return f"{label}: {t_str}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348571402-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348572982-05:00 dm_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348574632-05:00 v = _dm('inside_per_100_gal', 'Inside/100gal', '$', '', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348576202-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348577752-05:00 v = _dm('effective_fuel_price', 'Effective fuel price', '$', '/gal', 3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348580002-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348581662-05:00 v = _dm('inside_sales_share_pct','Inside share of sales', '', '%', 1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348583282-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348584882-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348586502-05:00 gap = trans.get('fuel_inside_gap_pct') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348588072-05:00 if gap is not None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348589642-05:00 direction = "FUEL OUTPACING INSIDE" if gap > 0 else "INSIDE OUTPACING FUEL" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348591282-05:00 dm_lines.append(f"Fuel-vs-Inside gap: {gap:+.1f}pp ({direction})") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348592972-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348594962-05:00 derived_metrics = "\n".join(dm_lines) if dm_lines else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348597022-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348599002-05:00 # ── Flags summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348600662-05:00 if flags: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348602232-05:00 flags_str = "\n".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348603782-05:00 f"- [{f['severity']}] {f['label']}: {f['note']}" for f in flags) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348605452-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348607012-05:00 flags_str = "No significant anomalies detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348608572-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348610292-05:00 # ── Shift summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348611972-05:00 shifts = trans.get('shift_summary', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348613572-05:00 if shifts: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348615162-05:00 shift_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348616722-05:00 for name, data in shifts.items(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348618282-05:00 rev = data.get('revenue', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348619812-05:00 tx = data.get('transactions', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348621432-05:00 gals = data.get('fuel_gallons', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348623412-05:00 shift_lines.append(f"- {name}: ${rev:,.2f} inside | {tx} tx | {gals:,.1f} gal fuel") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348627592-05:00 shift_summary = "\n".join(shift_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348629192-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348630752-05:00 shift_summary = "Shift data not available." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348632302-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348633982-05:00 # ── Gap / zero-tx hours ─────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348635552-05:00 gaps = trans.get('top_hourly_gaps', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348637132-05:00 zero_tx = trans.get('zero_tx_hours', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348638812-05:00 gap_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348640392-05:00 if gaps: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348641982-05:00 gap_str = ", ".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348643622-05:00 "{:02d}:00 ({} tx)".format(g['_hour'], g['tx_count']) for g in gaps) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348645192-05:00 gap_lines.append(f"Top gap hours: {gap_str}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348646772-05:00 if zero_tx: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348658062-05:00 gap_lines.append(f"Zero-tx hours (open window): {zero_tx}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348661912-05:00 gap_summary = "\n".join(gap_lines) if gap_lines else "No unusual gaps detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348664442-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348667092-05:00 # Authoritative store hours (issue #128): when Google Places supplies the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348669492-05:00 # store's real hours, prepend them and instruct the model to treat [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348671912-05:00 # off-hours zero activity as expected closure rather than hedging. The [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348674072-05:00 # gap/zero-tx figures above are already computed against this same window. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348675902-05:00 store_hours_val = kwargs.get('store_hours') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348677682-05:00 if store_hours_val and store_hours_val.get('weekday_text'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348679402-05:00 hrs_lines = ["AUTHORITATIVE STORE HOURS (Google Places — treat as ground truth):"] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348681252-05:00 hrs_lines += [f" {ln}" for ln in store_hours_val['weekday_text']] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348683172-05:00 if store_hours_val.get('open_24h'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348685102-05:00 hrs_lines.append(" This store operates 24 hours on at least one day.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348686712-05:00 hrs_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348688602-05:00 "Zero-transaction hours OUTSIDE these posted hours are EXPECTED " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348690182-05:00 "CLOSURE — do NOT flag them as gaps and do NOT hedge about whether " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348695102-05:00 "the store was open. Treat zero/low activity DURING posted open " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348696772-05:00 "hours as a real gap worth investigating.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348698342-05:00 gap_summary = "\n".join(hrs_lines) + "\n\n" + gap_summary [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348699912-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348704112-05:00 # ── Weather ─────────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348706022-05:00 if weather: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348707632-05:00 temp = weather.get('temp', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348709212-05:00 precip = weather.get('precipitation_in', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348710832-05:00 conds = list(weather.get('conditions', {}).keys()) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348718082-05:00 impact = weather.get('weather_impact_assessment', '') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348719682-05:00 yd = weather.get('yesterday', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348721332-05:00 td = weather.get('today_forecast', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348722942-05:00 rain_hrs= weather.get('rain_hours_today', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348724512-05:00 weather_str = ( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348726122-05:00 f"Report day: {yd.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348727772-05:00 f"{yd.get('temp_min','?')}–{yd.get('temp_max','?')}°F | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348729412-05:00 f"{yd.get('precip_in',0)}\" precip\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348731072-05:00 f"Today forecast: {td.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348732702-05:00 f"{td.get('temp_min','?')}–{td.get('temp_max','?')}°F\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348734252-05:00 f"Impact assessment: {impact}\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348735872-05:00 + (f"Rain hours: {rain_hrs}\n" if rain_hrs else "") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348737472-05:00 ) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348739032-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348740642-05:00 weather_str = "No weather data provided." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348742232-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348744892-05:00 # ── Schedule section ────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348746612-05:00 schedule_section = kwargs.get('schedule_section', None) or '' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348748222-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348749932-05:00 # ── Category baselines ──────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348751592-05:00 cat_baselines = kwargs.get('category_baselines', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348753222-05:00 # weekly_category_totals: actual accumulated weekly revenue per category [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348754852-05:00 # passed from Cell 39 _week_cat — USE THESE VERBATIM in the report table [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348756502-05:00 weekly_cat_totals = kwargs.get('weekly_category_totals', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348758082-05:00 _is_weekly = (report_type == 'weekly') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348759702-05:00 # Authoritative per-category revenue comes from the owner-mapped [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348761282-05:00 # category_mix (preprocessor). Post-mapping it is aggregated by taxonomy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348762832-05:00 # name and already reconciles to inside sales, so it — NOT the seeded [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348764442-05:00 # baselines — must drive the category table. Previously this block only [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348765992-05:00 # emitted categories that had a seeded history baseline; when baselines [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348767752-05:00 # were sparse or unseeded the model received no authoritative totals and [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348769372-05:00 # dumped the bulk of inside sales into "Other / unclassified" (issue #81). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348770962-05:00 _cat_mix_rows = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348772662-05:00 c for c in scan.get('category_mix', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348774282-05:00 if isinstance(c.get('category'), str) and str(c.get('category')).strip() [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348776001-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348777681-05:00 _period_lbl = 'This Week' if _is_weekly else 'Today' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348782901-05:00 if _is_weekly and weekly_cat_totals: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348784561-05:00 # Legacy notebook path passes pre-accumulated weekly totals verbatim. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348786171-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348787791-05:00 "WEEKLY Category Totals — USE THESE EXACT NUMBERS in Section 3 table.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348789581-05:00 "DO NOT invent or estimate different values. These are computed from actual data.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348791221-05:00 f" {'Category':<30} {'This Week':>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348792801-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348794461-05:00 for cat, week_rev in sorted(weekly_cat_totals.items(), [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348796101-05:00 key=lambda x: -x[1]): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348797771-05:00 if not isinstance(cat, str) or not cat.strip(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348799411-05:00 continue [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348801021-05:00 avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348802601-05:00 if avg > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348804231-05:00 pct = (week_rev - avg) / avg * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348805771-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348807401-05:00 f" {cat:<30} ${week_rev:>10,.0f} ${avg:>10,.0f}/day {pct:>+7.1f}%") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348808991-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348810541-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348812191-05:00 f" {cat:<30} ${week_rev:>10,.0f} (no baseline)") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348815861-05:00 category_baselines_str = "\n".join(_cb_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348817521-05:00 elif _cat_mix_rows: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348819151-05:00 _classified = sum((c.get('revenue') or 0) for c in _cat_mix_rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348820721-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348822301-05:00 f"Per-category inside revenue ({_period_lbl}) — USE THESE EXACT NUMBERS " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348823931-05:00 f"in the Section 3 category table. They are computed from the owner's " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348825581-05:00 f"merchandise-code taxonomy and already sum to inside sales " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348827231-05:00 f"(${_classified:,.0f}). Only the row explicitly labelled " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348828921-05:00 f"'Other / Unclassified' is unclassified — do NOT move any other " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348830531-05:00 f"category's revenue into 'Other', and do NOT invent different values.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348832141-05:00 f" {'Category':<30} {_period_lbl:>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348833721-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348835871-05:00 for c in sorted(_cat_mix_rows, key=lambda x: -(x.get('revenue') or 0)): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348837471-05:00 cat = str(c['category']) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348839071-05:00 rev = c.get('revenue') or 0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348840711-05:00 > avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348842611-05:00 ^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348844751-05:00 E AttributeError: 'NoneType' object has no attribute 'get' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348846251-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348847891-05:00 src/storeminder/agents/synthesis_agent.py:1426: AttributeError [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348849561-05:00 __________ ERROR at setup of test_daily_headline_labeled_total_sales ___________ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348854581-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348856601-05:00 @pytest.fixture(scope="module") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348858161-05:00 def daily_report(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348860191-05:00 > return _run("daily") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348861851-05:00 ^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348863391-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348864961-05:00 tests/test_report_traits_live.py:94: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348866551-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348868251-05:00 tests/test_report_traits_live.py:75: in _run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348869861-05:00 res = fn(store, end_date=d, dry_run=True, deliver=False, clean=True) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348871521-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348873671-05:00 src/storeminder/runner.py:340: in run_daily [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348875291-05:00 return _run_report(params, clean=clean) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348876861-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348878481-05:00 src/storeminder/runner.py:193: in _run_report [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348880061-05:00 result = run_pipeline( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348881651-05:00 src/storeminder/crew.py:463: in run_pipeline [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348883241-05:00 report_result = _agent.run_daily( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348884811-05:00 src/storeminder/agents/synthesis_agent.py:1023: in run_daily [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348886361-05:00 return self.run(report_type="daily", **kwargs) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348887971-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348889531-05:00 src/storeminder/agents/synthesis_agent.py:1073: in run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348891081-05:00 user = self._build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348892801-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348894321-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348895961-05:00 self = [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348897571-05:00 report_date = datetime.date(2026, 7, 10), store_id = 'Fastime' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348899371-05:00 trans = {'total_transactions': 618, 'open_hour_start': 4, 'open_hour_end': 21, 'report_date_filter': '2026-07-10', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348901041-05:00 scan = {'total_scan_rows': 1940, 'report_date_filter': '2026-07-10', 'total_sales': 6436.97, 'avg_item_price': 3.88, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348903011-05:00 baseline = {'sample_count': 15, 'day_of_week': 4, 'total_revenue': 16270.22, 'total_transactions': 590.87, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348905191-05:00 flags = [{'metric': 'rank_drop', 'label': 'Rank Drop: COPENHAGEN FINE CUT', 'today': 40, 'baseline': 13, ...}, {'metric': 'ran...': 29, ...}, {'metric': 'rank_drop', 'label': 'Rank Drop: BUD LIGHT BEER 12 O', 'today': 33, 'baseline': 22, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348907621-05:00 fuel_section = '## 2. Fuel \n**2,583 gal | $9,446 | 279 fuel transactions** \nFuel volume came in at 2,583 gallons, down from 2,758...w | Confirm pump access and functionality during 21:00–23:00 window | Measure: Fuel volume in last two operating hours' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348909321-05:00 inside_section = '## 3. Inside Sales\n\n**Inside Sales: $6,436.97 (+9.5% vs normal Monday)** \nTobacco / Nicotine surged, but Foodserv...e (1 void, $25.64) and override logs | Because: Void rate 100% above baseline | Measure: Closeout discipline confirmed' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348911021-05:00 weather = {'yesterday': {'temp_max': 90.1, 'temp_min': 76.8, 'precip_in': 0.0, 'conditions': 'Partly cloudy'}, 'today_forecast':...rtly cloudy'}, 'rain_hours_today': [], 'weather_impact_assessment': 'Low — weather was mild, not a significant factor'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348934311-05:00 external_section = '## External Context\n\n**Google Reviews (5 reviews)** \nAverage rating of recent reviews (3.0/5) is below overall st...y 12:00 PM \n- Review staff scheduling: confirm if high-performing employee (Luna) is on shift today — Do by 10:00 AM' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348939221-05:00 loyalty_section = '## Loyalty & Promotions\n\n**Redemption Summary**\n| Promotion | Item | Today | Period Avg | Delta | Status |\n|-----...y EOD Friday \n- Confirm in-store visibility for underperforming tobacco and energy drink promos — Do by EOD Thursday' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348940871-05:00 ops_section = '## 5. Operations\n\n**Transactions & Throughput** \n962 total transactions | Avg ticket: $17.15 (+2% vs normal Frida...on void authorization protocol | Because: elevated voids in high-volume period | Measure: Reduce voids to ≤2 per shift' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348942451-05:00 fuel_pricing_section = '## Fuel Pricing\n\n**Current Market** \n| Station | Regular | Midgrade | Premium | Diesel | Distance | \n|---------...1mi and priced at $3.400. \n- Update competitor price tracking—several stations have outdated or missing diesel data.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348944031-05:00 prior_actions = None, report_type = 'daily', weekly_days = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348945631-05:00 dow_profile = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348947541-05:00 kwargs = {'category_baselines': None, 'schedule_section': None, 'weekly_category_totals': None, 'ext_fetch_date': '2026-07-12', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348949061-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348950641-05:00 def _build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348952241-05:00 self, report_date, store_id, trans, scan, baseline, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348953971-05:00 flags, fuel_section, inside_section, weather, external_section, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348955551-05:00 loyalty_section, ops_section, fuel_pricing_section, prior_actions, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348957211-05:00 report_type: str = "daily", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348959661-05:00 weekly_days: Optional[List[Dict]] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348961321-05:00 dow_profile: Optional[Dict] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348962901-05:00 **kwargs, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348964491-05:00 ) -> str: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348966041-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348967721-05:00 # ── KPI table ───────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348969321-05:00 kpi_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348970981-05:00 "| Metric | Today | Baseline | Delta | Priority |", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348973221-05:00 "|--------|-------|----------|-------|----------|", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348974861-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348976431-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348978051-05:00 # Keys that live in scan_summary (inside sales) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348980221-05:00 _scan_keys = {'total_sales', 'avg_item_price', 'total_scan_rows'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348981931-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348983511-05:00 def kpi_row(label, key, fmt_str=",.0f", prefix="", priority=None): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348985091-05:00 # Prefer scan for inside-sales metrics, trans for everything else [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348986751-05:00 if key in _scan_keys: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348988331-05:00 t = scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348989921-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348991491-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348993131-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348997551-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.348999151-05:00 return [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349000781-05:00 t_str = f"{prefix}{t:{fmt_str}}" if isinstance(t, (int, float)) else str(t) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349002401-05:00 b_str = f"{prefix}{b:{fmt_str}}" if isinstance(b, (int, float)) else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349004501-05:00 delta = f"{((t-b)/b*100):+.1f}%" if isinstance(t,(int,float)) and isinstance(b,(int,float)) and b else "—" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349006101-05:00 # Priority: use flag severity if exists, else spec default [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349007651-05:00 flag = next((f for f in flags if f['metric'] == key), None) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349009211-05:00 pri = flag['severity'] if flag else (priority or "—") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349010841-05:00 kpi_lines.append(f"| {label} | {t_str} | {b_str} | {delta} | {pri} |") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349012411-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349014071-05:00 kpi_row("Total Transactions", "total_transactions", ",.0f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349015731-05:00 kpi_row("Total Sales", "total_revenue", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349017421-05:00 kpi_row("Inside Sales", "total_sales", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349019011-05:00 kpi_row("Avg Inside Ticket", "avg_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349020641-05:00 kpi_row("Avg Fuel Ticket", "avg_fuel_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349022301-05:00 kpi_row("Fuel Gallons", "fuel_gallons", ",.1f", "", "HIGH") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349023951-05:00 kpi_row("Fuel Sales $", "fuel_sales", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349025581-05:00 kpi_row("Fuel Transactions", "fuel_transactions", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349027201-05:00 kpi_row("Avg Gallons/Tx", "avg_gallons_per_tx", ",.2f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349031611-05:00 kpi_row("Forecourt Conv %", "forecourt_conversion_pct", ",.1f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349033311-05:00 kpi_row("Void Count", "void_count", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349034931-05:00 kpi_row("Void Total $", "void_total_amt", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349036651-05:00 kpi_row("Inside/100 Gal (weekly avg)", "inside_per_100_gal", ",.0f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349039381-05:00 kpi_row("Eff. Fuel Price", "effective_fuel_price", ",.3f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349041001-05:00 kpi_row("Inside Share %", "inside_sales_share_pct", ",.1f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349042581-05:00 kpi_summary = "\n".join(kpi_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349044171-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349046051-05:00 # ── Derived metrics for snapshot header ──────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349047751-05:00 def _dm(key, label, prefix="", suffix="", decimals=2): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349049381-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349051041-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349052610-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349054220-05:00 return None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349055810-05:00 t_str = f"{prefix}{float(t):,.{decimals}f}{suffix}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349059980-05:00 if b and float(b) > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349061600-05:00 pct = (float(t) - float(b)) / float(b) * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349063280-05:00 return f"{label}: {t_str} ({pct:+.1f}% vs baseline)" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349064890-05:00 return f"{label}: {t_str}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349066460-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349068750-05:00 dm_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349070420-05:00 v = _dm('inside_per_100_gal', 'Inside/100gal', '$', '', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349072040-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349073810-05:00 v = _dm('effective_fuel_price', 'Effective fuel price', '$', '/gal', 3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349075440-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349077090-05:00 v = _dm('inside_sales_share_pct','Inside share of sales', '', '%', 1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349078710-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349080280-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349082230-05:00 gap = trans.get('fuel_inside_gap_pct') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349083850-05:00 if gap is not None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349085600-05:00 direction = "FUEL OUTPACING INSIDE" if gap > 0 else "INSIDE OUTPACING FUEL" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349087230-05:00 dm_lines.append(f"Fuel-vs-Inside gap: {gap:+.1f}pp ({direction})") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349088810-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349090540-05:00 derived_metrics = "\n".join(dm_lines) if dm_lines else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349092130-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349093810-05:00 # ── Flags summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349095380-05:00 if flags: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349096940-05:00 flags_str = "\n".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349098780-05:00 f"- [{f['severity']}] {f['label']}: {f['note']}" for f in flags) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349100370-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349101920-05:00 flags_str = "No significant anomalies detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349103510-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349105170-05:00 # ── Shift summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349106750-05:00 shifts = trans.get('shift_summary', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349108350-05:00 if shifts: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349109920-05:00 shift_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349111480-05:00 for name, data in shifts.items(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349113080-05:00 rev = data.get('revenue', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349114740-05:00 tx = data.get('transactions', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349116430-05:00 gals = data.get('fuel_gallons', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349118220-05:00 shift_lines.append(f"- {name}: ${rev:,.2f} inside | {tx} tx | {gals:,.1f} gal fuel") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349119810-05:00 shift_summary = "\n".join(shift_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349121400-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349123080-05:00 shift_summary = "Shift data not available." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349124640-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349126220-05:00 # ── Gap / zero-tx hours ─────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349128010-05:00 gaps = trans.get('top_hourly_gaps', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349132190-05:00 zero_tx = trans.get('zero_tx_hours', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349133810-05:00 gap_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349135490-05:00 if gaps: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349137080-05:00 gap_str = ", ".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349138760-05:00 "{:02d}:00 ({} tx)".format(g['_hour'], g['tx_count']) for g in gaps) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349140370-05:00 gap_lines.append(f"Top gap hours: {gap_str}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349141950-05:00 if zero_tx: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349143570-05:00 gap_lines.append(f"Zero-tx hours (open window): {zero_tx}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349145230-05:00 gap_summary = "\n".join(gap_lines) if gap_lines else "No unusual gaps detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349146810-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349148600-05:00 # Authoritative store hours (issue #128): when Google Places supplies the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349150200-05:00 # store's real hours, prepend them and instruct the model to treat [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349151770-05:00 # off-hours zero activity as expected closure rather than hedging. The [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349153430-05:00 # gap/zero-tx figures above are already computed against this same window. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349157050-05:00 store_hours_val = kwargs.get('store_hours') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349158680-05:00 if store_hours_val and store_hours_val.get('weekday_text'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349160280-05:00 hrs_lines = ["AUTHORITATIVE STORE HOURS (Google Places — treat as ground truth):"] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349161950-05:00 hrs_lines += [f" {ln}" for ln in store_hours_val['weekday_text']] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349163550-05:00 if store_hours_val.get('open_24h'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349165100-05:00 hrs_lines.append(" This store operates 24 hours on at least one day.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349166680-05:00 hrs_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349168360-05:00 "Zero-transaction hours OUTSIDE these posted hours are EXPECTED " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349170170-05:00 "CLOSURE — do NOT flag them as gaps and do NOT hedge about whether " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349171820-05:00 "the store was open. Treat zero/low activity DURING posted open " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349173390-05:00 "hours as a real gap worth investigating.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349174950-05:00 gap_summary = "\n".join(hrs_lines) + "\n\n" + gap_summary [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349176520-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349178160-05:00 # ── Weather ─────────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349179750-05:00 if weather: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349181330-05:00 temp = weather.get('temp', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349182980-05:00 precip = weather.get('precipitation_in', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349184550-05:00 conds = list(weather.get('conditions', {}).keys()) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349186160-05:00 impact = weather.get('weather_impact_assessment', '') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349187830-05:00 yd = weather.get('yesterday', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349189380-05:00 td = weather.get('today_forecast', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349191430-05:00 rain_hrs= weather.get('rain_hours_today', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349193030-05:00 weather_str = ( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349194610-05:00 f"Report day: {yd.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349196430-05:00 f"{yd.get('temp_min','?')}–{yd.get('temp_max','?')}°F | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349200590-05:00 f"{yd.get('precip_in',0)}\" precip\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349202320-05:00 f"Today forecast: {td.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349204010-05:00 f"{td.get('temp_min','?')}–{td.get('temp_max','?')}°F\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349205600-05:00 f"Impact assessment: {impact}\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349207240-05:00 + (f"Rain hours: {rain_hrs}\n" if rain_hrs else "") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349208950-05:00 ) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349210510-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349212240-05:00 weather_str = "No weather data provided." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349213800-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349215940-05:00 # ── Schedule section ────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349217600-05:00 schedule_section = kwargs.get('schedule_section', None) or '' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349219190-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349220840-05:00 # ── Category baselines ──────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349222450-05:00 cat_baselines = kwargs.get('category_baselines', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349224590-05:00 # weekly_category_totals: actual accumulated weekly revenue per category [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349226530-05:00 # passed from Cell 39 _week_cat — USE THESE VERBATIM in the report table [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349228130-05:00 weekly_cat_totals = kwargs.get('weekly_category_totals', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349229740-05:00 _is_weekly = (report_type == 'weekly') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349231420-05:00 # Authoritative per-category revenue comes from the owner-mapped [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349233050-05:00 # category_mix (preprocessor). Post-mapping it is aggregated by taxonomy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349234660-05:00 # name and already reconciles to inside sales, so it — NOT the seeded [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349236270-05:00 # baselines — must drive the category table. Previously this block only [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349237900-05:00 # emitted categories that had a seeded history baseline; when baselines [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349240140-05:00 # were sparse or unseeded the model received no authoritative totals and [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349242270-05:00 # dumped the bulk of inside sales into "Other / unclassified" (issue #81). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349243900-05:00 _cat_mix_rows = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349245510-05:00 c for c in scan.get('category_mix', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349247180-05:00 if isinstance(c.get('category'), str) and str(c.get('category')).strip() [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349248820-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349250480-05:00 _period_lbl = 'This Week' if _is_weekly else 'Today' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349252090-05:00 if _is_weekly and weekly_cat_totals: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349253710-05:00 # Legacy notebook path passes pre-accumulated weekly totals verbatim. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349255280-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349257720-05:00 "WEEKLY Category Totals — USE THESE EXACT NUMBERS in Section 3 table.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349259300-05:00 "DO NOT invent or estimate different values. These are computed from actual data.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349260910-05:00 f" {'Category':<30} {'This Week':>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349262480-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349274170-05:00 for cat, week_rev in sorted(weekly_cat_totals.items(), [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349275940-05:00 key=lambda x: -x[1]): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349277580-05:00 if not isinstance(cat, str) or not cat.strip(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349279280-05:00 continue [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349280940-05:00 avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349282500-05:00 if avg > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349284970-05:00 pct = (week_rev - avg) / avg * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349286930-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349288880-05:00 f" {cat:<30} ${week_rev:>10,.0f} ${avg:>10,.0f}/day {pct:>+7.1f}%") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349290550-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349292830-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349295250-05:00 f" {cat:<30} ${week_rev:>10,.0f} (no baseline)") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349296880-05:00 category_baselines_str = "\n".join(_cb_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349298450-05:00 elif _cat_mix_rows: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349300640-05:00 _classified = sum((c.get('revenue') or 0) for c in _cat_mix_rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349302670-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349304930-05:00 f"Per-category inside revenue ({_period_lbl}) — USE THESE EXACT NUMBERS " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349306610-05:00 f"in the Section 3 category table. They are computed from the owner's " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349308490-05:00 f"merchandise-code taxonomy and already sum to inside sales " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349310370-05:00 f"(${_classified:,.0f}). Only the row explicitly labelled " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349312590-05:00 f"'Other / Unclassified' is unclassified — do NOT move any other " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349314170-05:00 f"category's revenue into 'Other', and do NOT invent different values.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349316360-05:00 f" {'Category':<30} {_period_lbl:>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349318170-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349320270-05:00 for c in sorted(_cat_mix_rows, key=lambda x: -(x.get('revenue') or 0)): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349322000-05:00 cat = str(c['category']) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349323660-05:00 rev = c.get('revenue') or 0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349325250-05:00 > avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349326910-05:00 ^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349328520-05:00 E AttributeError: 'NoneType' object has no attribute 'get' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349330000-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349332050-05:00 src/storeminder/agents/synthesis_agent.py:1426: AttributeError [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349333760-05:00 ________________ ERROR at setup of test_daily_sms_not_truncated ________________ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349335280-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349336890-05:00 @pytest.fixture(scope="module") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349338599-05:00 def daily_report(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349340219-05:00 > return _run("daily") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349341839-05:00 ^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349343309-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349344909-05:00 tests/test_report_traits_live.py:94: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349346559-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349348159-05:00 tests/test_report_traits_live.py:75: in _run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349349799-05:00 res = fn(store, end_date=d, dry_run=True, deliver=False, clean=True) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349354059-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349355659-05:00 src/storeminder/runner.py:340: in run_daily [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349357289-05:00 return _run_report(params, clean=clean) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349358909-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349360479-05:00 src/storeminder/runner.py:193: in _run_report [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349362369-05:00 result = run_pipeline( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349364049-05:00 src/storeminder/crew.py:463: in run_pipeline [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349365699-05:00 report_result = _agent.run_daily( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349367299-05:00 src/storeminder/agents/synthesis_agent.py:1023: in run_daily [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349368919-05:00 return self.run(report_type="daily", **kwargs) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349370739-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349372349-05:00 src/storeminder/agents/synthesis_agent.py:1073: in run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349373939-05:00 user = self._build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349375599-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349377109-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349378739-05:00 self = [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349380489-05:00 report_date = datetime.date(2026, 7, 10), store_id = 'Fastime' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349382129-05:00 trans = {'total_transactions': 618, 'open_hour_start': 4, 'open_hour_end': 21, 'report_date_filter': '2026-07-10', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349383929-05:00 scan = {'total_scan_rows': 1940, 'report_date_filter': '2026-07-10', 'total_sales': 6436.97, 'avg_item_price': 3.88, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349386329-05:00 baseline = {'sample_count': 15, 'day_of_week': 4, 'total_revenue': 16270.22, 'total_transactions': 590.87, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349388419-05:00 flags = [{'metric': 'rank_drop', 'label': 'Rank Drop: COPENHAGEN FINE CUT', 'today': 40, 'baseline': 13, ...}, {'metric': 'ran...': 29, ...}, {'metric': 'rank_drop', 'label': 'Rank Drop: BUD LIGHT BEER 12 O', 'today': 33, 'baseline': 22, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349390119-05:00 fuel_section = '## 2. Fuel \n**2,583 gal | $9,446 | 279 fuel transactions** \nFuel volume came in at 2,583 gallons, down from 2,758...w | Confirm pump access and functionality during 21:00–23:00 window | Measure: Fuel volume in last two operating hours' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349394239-05:00 inside_section = '## 3. Inside Sales\n\n**Inside Sales: $6,436.97 (+9.5% vs normal Monday)** \nTobacco / Nicotine surged, but Foodserv...e (1 void, $25.64) and override logs | Because: Void rate 100% above baseline | Measure: Closeout discipline confirmed' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349396099-05:00 weather = {'yesterday': {'temp_max': 90.1, 'temp_min': 76.8, 'precip_in': 0.0, 'conditions': 'Partly cloudy'}, 'today_forecast':...rtly cloudy'}, 'rain_hours_today': [], 'weather_impact_assessment': 'Low — weather was mild, not a significant factor'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349397749-05:00 external_section = '## External Context\n\n**Google Reviews (5 reviews)** \nAverage rating of recent reviews (3.0/5) is below overall st...y 12:00 PM \n- Review staff scheduling: confirm if high-performing employee (Luna) is on shift today — Do by 10:00 AM' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349399389-05:00 loyalty_section = '## Loyalty & Promotions\n\n**Redemption Summary**\n| Promotion | Item | Today | Period Avg | Delta | Status |\n|-----...y EOD Friday \n- Confirm in-store visibility for underperforming tobacco and energy drink promos — Do by EOD Thursday' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349401049-05:00 ops_section = '## 5. Operations\n\n**Transactions & Throughput** \n962 total transactions | Avg ticket: $17.15 (+2% vs normal Frida...on void authorization protocol | Because: elevated voids in high-volume period | Measure: Reduce voids to ≤2 per shift' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349405309-05:00 fuel_pricing_section = '## Fuel Pricing\n\n**Current Market** \n| Station | Regular | Midgrade | Premium | Diesel | Distance | \n|---------...1mi and priced at $3.400. \n- Update competitor price tracking—several stations have outdated or missing diesel data.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349406959-05:00 prior_actions = None, report_type = 'daily', weekly_days = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349408639-05:00 dow_profile = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349410319-05:00 kwargs = {'category_baselines': None, 'schedule_section': None, 'weekly_category_totals': None, 'ext_fetch_date': '2026-07-12', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349411819-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349413709-05:00 def _build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349415399-05:00 self, report_date, store_id, trans, scan, baseline, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349417729-05:00 flags, fuel_section, inside_section, weather, external_section, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349419329-05:00 loyalty_section, ops_section, fuel_pricing_section, prior_actions, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349420969-05:00 report_type: str = "daily", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349422609-05:00 weekly_days: Optional[List[Dict]] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349424219-05:00 dow_profile: Optional[Dict] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349425809-05:00 **kwargs, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349427369-05:00 ) -> str: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349428999-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349430629-05:00 # ── KPI table ───────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349432219-05:00 kpi_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349433869-05:00 "| Metric | Today | Baseline | Delta | Priority |", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349435469-05:00 "|--------|-------|----------|-------|----------|", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349437059-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349438629-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349440179-05:00 # Keys that live in scan_summary (inside sales) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349441829-05:00 _scan_keys = {'total_sales', 'avg_item_price', 'total_scan_rows'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349443389-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349445169-05:00 def kpi_row(label, key, fmt_str=",.0f", prefix="", priority=None): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349446799-05:00 # Prefer scan for inside-sales metrics, trans for everything else [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349448449-05:00 if key in _scan_keys: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349450059-05:00 t = scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349451639-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349453279-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349454869-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349456949-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349458609-05:00 return [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349460689-05:00 t_str = f"{prefix}{t:{fmt_str}}" if isinstance(t, (int, float)) else str(t) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349462279-05:00 b_str = f"{prefix}{b:{fmt_str}}" if isinstance(b, (int, float)) else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349464409-05:00 delta = f"{((t-b)/b*100):+.1f}%" if isinstance(t,(int,float)) and isinstance(b,(int,float)) and b else "—" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349465969-05:00 # Priority: use flag severity if exists, else spec default [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349467509-05:00 flag = next((f for f in flags if f['metric'] == key), None) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349471559-05:00 pri = flag['severity'] if flag else (priority or "—") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349473219-05:00 kpi_lines.append(f"| {label} | {t_str} | {b_str} | {delta} | {pri} |") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349474809-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349476389-05:00 kpi_row("Total Transactions", "total_transactions", ",.0f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349478049-05:00 kpi_row("Total Sales", "total_revenue", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349479629-05:00 kpi_row("Inside Sales", "total_sales", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349481179-05:00 kpi_row("Avg Inside Ticket", "avg_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349485329-05:00 kpi_row("Avg Fuel Ticket", "avg_fuel_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349486969-05:00 kpi_row("Fuel Gallons", "fuel_gallons", ",.1f", "", "HIGH") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349488679-05:00 kpi_row("Fuel Sales $", "fuel_sales", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349490249-05:00 kpi_row("Fuel Transactions", "fuel_transactions", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349491939-05:00 kpi_row("Avg Gallons/Tx", "avg_gallons_per_tx", ",.2f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349493479-05:00 kpi_row("Forecourt Conv %", "forecourt_conversion_pct", ",.1f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349495009-05:00 kpi_row("Void Count", "void_count", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349496559-05:00 kpi_row("Void Total $", "void_total_amt", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349498109-05:00 kpi_row("Inside/100 Gal (weekly avg)", "inside_per_100_gal", ",.0f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349499659-05:00 kpi_row("Eff. Fuel Price", "effective_fuel_price", ",.3f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349501219-05:00 kpi_row("Inside Share %", "inside_sales_share_pct", ",.1f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349502779-05:00 kpi_summary = "\n".join(kpi_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349504379-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349506049-05:00 # ── Derived metrics for snapshot header ──────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349507639-05:00 def _dm(key, label, prefix="", suffix="", decimals=2): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349509289-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349510849-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349512759-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349514409-05:00 return None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349516009-05:00 t_str = f"{prefix}{float(t):,.{decimals}f}{suffix}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349517619-05:00 if b and float(b) > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349519199-05:00 pct = (float(t) - float(b)) / float(b) * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349520739-05:00 return f"{label}: {t_str} ({pct:+.1f}% vs baseline)" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349522309-05:00 return f"{label}: {t_str}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349523859-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349525449-05:00 dm_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349527099-05:00 v = _dm('inside_per_100_gal', 'Inside/100gal', '$', '', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349528679-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349530259-05:00 v = _dm('effective_fuel_price', 'Effective fuel price', '$', '/gal', 3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349534179-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349535779-05:00 v = _dm('inside_sales_share_pct','Inside share of sales', '', '%', 1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349537339-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349538909-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349540489-05:00 gap = trans.get('fuel_inside_gap_pct') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349542079-05:00 if gap is not None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349544379-05:00 direction = "FUEL OUTPACING INSIDE" if gap > 0 else "INSIDE OUTPACING FUEL" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349546029-05:00 dm_lines.append(f"Fuel-vs-Inside gap: {gap:+.1f}pp ({direction})") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349547589-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349549219-05:00 derived_metrics = "\n".join(dm_lines) if dm_lines else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349550779-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349552449-05:00 # ── Flags summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349554009-05:00 if flags: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349555569-05:00 flags_str = "\n".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349557189-05:00 f"- [{f['severity']}] {f['label']}: {f['note']}" for f in flags) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349558769-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349560329-05:00 flags_str = "No significant anomalies detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349561889-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349563459-05:00 # ── Shift summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349565029-05:00 shifts = trans.get('shift_summary', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349566609-05:00 if shifts: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349568209-05:00 shift_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349569769-05:00 for name, data in shifts.items(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349571349-05:00 rev = data.get('revenue', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349573469-05:00 tx = data.get('transactions', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349575019-05:00 gals = data.get('fuel_gallons', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349576659-05:00 shift_lines.append(f"- {name}: ${rev:,.2f} inside | {tx} tx | {gals:,.1f} gal fuel") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349578199-05:00 shift_summary = "\n".join(shift_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349579759-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349581289-05:00 shift_summary = "Shift data not available." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349582839-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349584459-05:00 # ── Gap / zero-tx hours ─────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349586059-05:00 gaps = trans.get('top_hourly_gaps', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349587619-05:00 zero_tx = trans.get('zero_tx_hours', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349589159-05:00 gap_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349590729-05:00 if gaps: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349592269-05:00 gap_str = ", ".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349593829-05:00 "{:02d}:00 ({} tx)".format(g['_hour'], g['tx_count']) for g in gaps) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349595379-05:00 gap_lines.append(f"Top gap hours: {gap_str}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349596939-05:00 if zero_tx: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349612299-05:00 gap_lines.append(f"Zero-tx hours (open window): {zero_tx}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349619299-05:00 gap_summary = "\n".join(gap_lines) if gap_lines else "No unusual gaps detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349631728-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349634168-05:00 # Authoritative store hours (issue #128): when Google Places supplies the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349636288-05:00 # store's real hours, prepend them and instruct the model to treat [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349638298-05:00 # off-hours zero activity as expected closure rather than hedging. The [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349640468-05:00 # gap/zero-tx figures above are already computed against this same window. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349642718-05:00 store_hours_val = kwargs.get('store_hours') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349644808-05:00 if store_hours_val and store_hours_val.get('weekday_text'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349651568-05:00 hrs_lines = ["AUTHORITATIVE STORE HOURS (Google Places — treat as ground truth):"] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349653788-05:00 hrs_lines += [f" {ln}" for ln in store_hours_val['weekday_text']] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349656498-05:00 if store_hours_val.get('open_24h'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349658578-05:00 hrs_lines.append(" This store operates 24 hours on at least one day.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349660938-05:00 hrs_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349663148-05:00 "Zero-transaction hours OUTSIDE these posted hours are EXPECTED " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349665588-05:00 "CLOSURE — do NOT flag them as gaps and do NOT hedge about whether " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349667668-05:00 "the store was open. Treat zero/low activity DURING posted open " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349669708-05:00 "hours as a real gap worth investigating.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349671868-05:00 gap_summary = "\n".join(hrs_lines) + "\n\n" + gap_summary [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349674088-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349677318-05:00 # ── Weather ─────────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349679528-05:00 if weather: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349681628-05:00 temp = weather.get('temp', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349683648-05:00 precip = weather.get('precipitation_in', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349685668-05:00 conds = list(weather.get('conditions', {}).keys()) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349688478-05:00 impact = weather.get('weather_impact_assessment', '') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349690498-05:00 yd = weather.get('yesterday', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349692528-05:00 td = weather.get('today_forecast', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349694448-05:00 rain_hrs= weather.get('rain_hours_today', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349696518-05:00 weather_str = ( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349698478-05:00 f"Report day: {yd.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349700508-05:00 f"{yd.get('temp_min','?')}–{yd.get('temp_max','?')}°F | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349703398-05:00 f"{yd.get('precip_in',0)}\" precip\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349706408-05:00 f"Today forecast: {td.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349708508-05:00 f"{td.get('temp_min','?')}–{td.get('temp_max','?')}°F\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349710538-05:00 f"Impact assessment: {impact}\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349712568-05:00 + (f"Rain hours: {rain_hrs}\n" if rain_hrs else "") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349714748-05:00 ) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349716738-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349718738-05:00 weather_str = "No weather data provided." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349724578-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349727558-05:00 # ── Schedule section ────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349729678-05:00 schedule_section = kwargs.get('schedule_section', None) or '' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349731668-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349735098-05:00 # ── Category baselines ──────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349737168-05:00 cat_baselines = kwargs.get('category_baselines', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349739228-05:00 # weekly_category_totals: actual accumulated weekly revenue per category [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349741338-05:00 # passed from Cell 39 _week_cat — USE THESE VERBATIM in the report table [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349743398-05:00 weekly_cat_totals = kwargs.get('weekly_category_totals', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349746088-05:00 _is_weekly = (report_type == 'weekly') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349748308-05:00 # Authoritative per-category revenue comes from the owner-mapped [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349750308-05:00 # category_mix (preprocessor). Post-mapping it is aggregated by taxonomy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349752358-05:00 # name and already reconciles to inside sales, so it — NOT the seeded [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349754418-05:00 # baselines — must drive the category table. Previously this block only [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349756868-05:00 # emitted categories that had a seeded history baseline; when baselines [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349758998-05:00 # were sparse or unseeded the model received no authoritative totals and [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349760968-05:00 # dumped the bulk of inside sales into "Other / unclassified" (issue #81). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349763058-05:00 _cat_mix_rows = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349765698-05:00 c for c in scan.get('category_mix', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349767748-05:00 if isinstance(c.get('category'), str) and str(c.get('category')).strip() [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349769878-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349771968-05:00 _period_lbl = 'This Week' if _is_weekly else 'Today' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349774368-05:00 if _is_weekly and weekly_cat_totals: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349776548-05:00 # Legacy notebook path passes pre-accumulated weekly totals verbatim. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349778508-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349780508-05:00 "WEEKLY Category Totals — USE THESE EXACT NUMBERS in Section 3 table.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349782858-05:00 "DO NOT invent or estimate different values. These are computed from actual data.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349784868-05:00 f" {'Category':<30} {'This Week':>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349786908-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349788948-05:00 for cat, week_rev in sorted(weekly_cat_totals.items(), [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349791028-05:00 key=lambda x: -x[1]): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349796298-05:00 if not isinstance(cat, str) or not cat.strip(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349798348-05:00 continue [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349800448-05:00 avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349802728-05:00 if avg > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349804748-05:00 pct = (week_rev - avg) / avg * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349806798-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349811898-05:00 f" {cat:<30} ${week_rev:>10,.0f} ${avg:>10,.0f}/day {pct:>+7.1f}%") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349813968-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349816038-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349818068-05:00 f" {cat:<30} ${week_rev:>10,.0f} (no baseline)") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349820188-05:00 category_baselines_str = "\n".join(_cb_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349822408-05:00 elif _cat_mix_rows: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349824418-05:00 _classified = sum((c.get('revenue') or 0) for c in _cat_mix_rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349826558-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349828898-05:00 f"Per-category inside revenue ({_period_lbl}) — USE THESE EXACT NUMBERS " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349830938-05:00 f"in the Section 3 category table. They are computed from the owner's " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349833018-05:00 f"merchandise-code taxonomy and already sum to inside sales " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349835048-05:00 f"(${_classified:,.0f}). Only the row explicitly labelled " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349837108-05:00 f"'Other / Unclassified' is unclassified — do NOT move any other " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349839208-05:00 f"category's revenue into 'Other', and do NOT invent different values.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349841208-05:00 f" {'Category':<30} {_period_lbl:>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349843308-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349845378-05:00 for c in sorted(_cat_mix_rows, key=lambda x: -(x.get('revenue') or 0)): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349847388-05:00 cat = str(c['category']) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349850298-05:00 rev = c.get('revenue') or 0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349852268-05:00 > avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349854398-05:00 ^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349856978-05:00 E AttributeError: 'NoneType' object has no attribute 'get' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349858898-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349861008-05:00 src/storeminder/agents/synthesis_agent.py:1426: AttributeError [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349863098-05:00 _____________ ERROR at setup of test_daily_artifact_bundle_written _____________ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349864958-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349866898-05:00 @pytest.fixture(scope="module") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349868848-05:00 def daily_report(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349870918-05:00 > return _run("daily") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349872968-05:00 ^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349874838-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349876878-05:00 tests/test_report_traits_live.py:94: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349878908-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349881518-05:00 tests/test_report_traits_live.py:75: in _run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349883788-05:00 res = fn(store, end_date=d, dry_run=True, deliver=False, clean=True) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349885818-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349887948-05:00 src/storeminder/runner.py:340: in run_daily [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349890028-05:00 return _run_report(params, clean=clean) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349892068-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349894108-05:00 src/storeminder/runner.py:193: in _run_report [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349896058-05:00 result = run_pipeline( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349897988-05:00 src/storeminder/crew.py:463: in run_pipeline [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349903358-05:00 report_result = _agent.run_daily( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349905378-05:00 src/storeminder/agents/synthesis_agent.py:1023: in run_daily [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349907588-05:00 return self.run(report_type="daily", **kwargs) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349909637-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349911647-05:00 src/storeminder/agents/synthesis_agent.py:1073: in run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349913697-05:00 user = self._build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349915697-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349926077-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349932177-05:00 self = [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349934297-05:00 report_date = datetime.date(2026, 7, 10), store_id = 'Fastime' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349936507-05:00 trans = {'total_transactions': 618, 'open_hour_start': 4, 'open_hour_end': 21, 'report_date_filter': '2026-07-10', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349938677-05:00 scan = {'total_scan_rows': 1940, 'report_date_filter': '2026-07-10', 'total_sales': 6436.97, 'avg_item_price': 3.88, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349940757-05:00 baseline = {'sample_count': 15, 'day_of_week': 4, 'total_revenue': 16270.22, 'total_transactions': 590.87, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349943357-05:00 flags = [{'metric': 'rank_drop', 'label': 'Rank Drop: COPENHAGEN FINE CUT', 'today': 40, 'baseline': 13, ...}, {'metric': 'ran...': 29, ...}, {'metric': 'rank_drop', 'label': 'Rank Drop: BUD LIGHT BEER 12 O', 'today': 33, 'baseline': 22, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349946427-05:00 fuel_section = '## 2. Fuel \n**2,583 gal | $9,446 | 279 fuel transactions** \nFuel volume came in at 2,583 gallons, down from 2,758...w | Confirm pump access and functionality during 21:00–23:00 window | Measure: Fuel volume in last two operating hours' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349948537-05:00 inside_section = '## 3. Inside Sales\n\n**Inside Sales: $6,436.97 (+9.5% vs normal Monday)** \nTobacco / Nicotine surged, but Foodserv...e (1 void, $25.64) and override logs | Because: Void rate 100% above baseline | Measure: Closeout discipline confirmed' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349953457-05:00 weather = {'yesterday': {'temp_max': 90.1, 'temp_min': 76.8, 'precip_in': 0.0, 'conditions': 'Partly cloudy'}, 'today_forecast':...rtly cloudy'}, 'rain_hours_today': [], 'weather_impact_assessment': 'Low — weather was mild, not a significant factor'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349955677-05:00 external_section = '## External Context\n\n**Google Reviews (5 reviews)** \nAverage rating of recent reviews (3.0/5) is below overall st...y 12:00 PM \n- Review staff scheduling: confirm if high-performing employee (Luna) is on shift today — Do by 10:00 AM' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349957827-05:00 loyalty_section = '## Loyalty & Promotions\n\n**Redemption Summary**\n| Promotion | Item | Today | Period Avg | Delta | Status |\n|-----...y EOD Friday \n- Confirm in-store visibility for underperforming tobacco and energy drink promos — Do by EOD Thursday' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349959917-05:00 ops_section = '## 5. Operations\n\n**Transactions & Throughput** \n962 total transactions | Avg ticket: $17.15 (+2% vs normal Frida...on void authorization protocol | Because: elevated voids in high-volume period | Measure: Reduce voids to ≤2 per shift' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349962127-05:00 fuel_pricing_section = '## Fuel Pricing\n\n**Current Market** \n| Station | Regular | Midgrade | Premium | Diesel | Distance | \n|---------...1mi and priced at $3.400. \n- Update competitor price tracking—several stations have outdated or missing diesel data.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349964107-05:00 prior_actions = None, report_type = 'daily', weekly_days = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349966177-05:00 dow_profile = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349971647-05:00 kwargs = {'category_baselines': None, 'schedule_section': None, 'weekly_category_totals': None, 'ext_fetch_date': '2026-07-12', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349973697-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349975817-05:00 def _build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349978027-05:00 self, report_date, store_id, trans, scan, baseline, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349980147-05:00 flags, fuel_section, inside_section, weather, external_section, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349982207-05:00 loyalty_section, ops_section, fuel_pricing_section, prior_actions, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349984277-05:00 report_type: str = "daily", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349986337-05:00 weekly_days: Optional[List[Dict]] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349988267-05:00 dow_profile: Optional[Dict] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349990237-05:00 **kwargs, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349992297-05:00 ) -> str: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349994257-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349996407-05:00 # ── KPI table ───────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.349998527-05:00 kpi_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350000537-05:00 "| Metric | Today | Baseline | Delta | Priority |", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350002607-05:00 "|--------|-------|----------|-------|----------|", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350004597-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350006537-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350008557-05:00 # Keys that live in scan_summary (inside sales) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350010707-05:00 _scan_keys = {'total_sales', 'avg_item_price', 'total_scan_rows'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350012687-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350014717-05:00 def kpi_row(label, key, fmt_str=",.0f", prefix="", priority=None): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350016747-05:00 # Prefer scan for inside-sales metrics, trans for everything else [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350018777-05:00 if key in _scan_keys: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350020797-05:00 t = scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350022817-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350024987-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350027007-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350029067-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350031127-05:00 return [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350033147-05:00 t_str = f"{prefix}{t:{fmt_str}}" if isinstance(t, (int, float)) else str(t) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350035137-05:00 b_str = f"{prefix}{b:{fmt_str}}" if isinstance(b, (int, float)) else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350037597-05:00 delta = f"{((t-b)/b*100):+.1f}%" if isinstance(t,(int,float)) and isinstance(b,(int,float)) and b else "—" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350039607-05:00 # Priority: use flag severity if exists, else spec default [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350041587-05:00 flag = next((f for f in flags if f['metric'] == key), None) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350043607-05:00 pri = flag['severity'] if flag else (priority or "—") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350045677-05:00 kpi_lines.append(f"| {label} | {t_str} | {b_str} | {delta} | {pri} |") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350047727-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350049827-05:00 kpi_row("Total Transactions", "total_transactions", ",.0f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350051827-05:00 kpi_row("Total Sales", "total_revenue", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350053967-05:00 kpi_row("Inside Sales", "total_sales", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350058937-05:00 kpi_row("Avg Inside Ticket", "avg_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350060977-05:00 kpi_row("Avg Fuel Ticket", "avg_fuel_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350062977-05:00 kpi_row("Fuel Gallons", "fuel_gallons", ",.1f", "", "HIGH") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350064957-05:00 kpi_row("Fuel Sales $", "fuel_sales", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350069657-05:00 kpi_row("Fuel Transactions", "fuel_transactions", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350071897-05:00 kpi_row("Avg Gallons/Tx", "avg_gallons_per_tx", ",.2f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350074187-05:00 kpi_row("Forecourt Conv %", "forecourt_conversion_pct", ",.1f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350076247-05:00 kpi_row("Void Count", "void_count", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350078687-05:00 kpi_row("Void Total $", "void_total_amt", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350080727-05:00 kpi_row("Inside/100 Gal (weekly avg)", "inside_per_100_gal", ",.0f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350082707-05:00 kpi_row("Eff. Fuel Price", "effective_fuel_price", ",.3f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350084837-05:00 kpi_row("Inside Share %", "inside_sales_share_pct", ",.1f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350086917-05:00 kpi_summary = "\n".join(kpi_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350088887-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350091987-05:00 # ── Derived metrics for snapshot header ──────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350094077-05:00 def _dm(key, label, prefix="", suffix="", decimals=2): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350096107-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350098187-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350100227-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350102217-05:00 return None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350104207-05:00 t_str = f"{prefix}{float(t):,.{decimals}f}{suffix}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350106237-05:00 if b and float(b) > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350108267-05:00 pct = (float(t) - float(b)) / float(b) * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350110287-05:00 return f"{label}: {t_str} ({pct:+.1f}% vs baseline)" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350112317-05:00 return f"{label}: {t_str}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350114307-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350116307-05:00 dm_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350118357-05:00 v = _dm('inside_per_100_gal', 'Inside/100gal', '$', '', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350120347-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350122427-05:00 v = _dm('effective_fuel_price', 'Effective fuel price', '$', '/gal', 3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350124497-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350126537-05:00 v = _dm('inside_sales_share_pct','Inside share of sales', '', '%', 1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350128507-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350131267-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350133247-05:00 gap = trans.get('fuel_inside_gap_pct') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350135437-05:00 if gap is not None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350137647-05:00 direction = "FUEL OUTPACING INSIDE" if gap > 0 else "INSIDE OUTPACING FUEL" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350143097-05:00 dm_lines.append(f"Fuel-vs-Inside gap: {gap:+.1f}pp ({direction})") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350145307-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350147637-05:00 derived_metrics = "\n".join(dm_lines) if dm_lines else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350149617-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350151727-05:00 # ── Flags summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350153977-05:00 if flags: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350155947-05:00 flags_str = "\n".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350158017-05:00 f"- [{f['severity']}] {f['label']}: {f['note']}" for f in flags) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350160077-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350162137-05:00 flags_str = "No significant anomalies detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350164147-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350166767-05:00 # ── Shift summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350168777-05:00 shifts = trans.get('shift_summary', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350170857-05:00 if shifts: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350172857-05:00 shift_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350174827-05:00 for name, data in shifts.items(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350176877-05:00 rev = data.get('revenue', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350178867-05:00 tx = data.get('transactions', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350180897-05:00 gals = data.get('fuel_gallons', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350182947-05:00 shift_lines.append(f"- {name}: ${rev:,.2f} inside | {tx} tx | {gals:,.1f} gal fuel") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350184947-05:00 shift_summary = "\n".join(shift_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350186987-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350189027-05:00 shift_summary = "Shift data not available." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350191067-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350193126-05:00 # ── Gap / zero-tx hours ─────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350195196-05:00 gaps = trans.get('top_hourly_gaps', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350197176-05:00 zero_tx = trans.get('zero_tx_hours', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350199166-05:00 gap_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350201146-05:00 if gaps: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350203136-05:00 gap_str = ", ".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350205156-05:00 "{:02d}:00 ({} tx)".format(g['_hour'], g['tx_count']) for g in gaps) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350207156-05:00 gap_lines.append(f"Top gap hours: {gap_str}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350209116-05:00 if zero_tx: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350211156-05:00 gap_lines.append(f"Zero-tx hours (open window): {zero_tx}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350213276-05:00 gap_summary = "\n".join(gap_lines) if gap_lines else "No unusual gaps detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350215236-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350217356-05:00 # Authoritative store hours (issue #128): when Google Places supplies the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350219616-05:00 # store's real hours, prepend them and instruct the model to treat [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350224026-05:00 # off-hours zero activity as expected closure rather than hedging. The [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350226916-05:00 # gap/zero-tx figures above are already computed against this same window. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350228886-05:00 store_hours_val = kwargs.get('store_hours') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350234646-05:00 if store_hours_val and store_hours_val.get('weekday_text'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350236696-05:00 hrs_lines = ["AUTHORITATIVE STORE HOURS (Google Places — treat as ground truth):"] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350238686-05:00 hrs_lines += [f" {ln}" for ln in store_hours_val['weekday_text']] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350240646-05:00 if store_hours_val.get('open_24h'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350242706-05:00 hrs_lines.append(" This store operates 24 hours on at least one day.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350244656-05:00 hrs_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350246696-05:00 "Zero-transaction hours OUTSIDE these posted hours are EXPECTED " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350248706-05:00 "CLOSURE — do NOT flag them as gaps and do NOT hedge about whether " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350250766-05:00 "the store was open. Treat zero/low activity DURING posted open " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350252716-05:00 "hours as a real gap worth investigating.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350254736-05:00 gap_summary = "\n".join(hrs_lines) + "\n\n" + gap_summary [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350256706-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350258816-05:00 # ── Weather ─────────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350261666-05:00 if weather: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350263756-05:00 temp = weather.get('temp', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350265716-05:00 precip = weather.get('precipitation_in', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350267726-05:00 conds = list(weather.get('conditions', {}).keys()) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350269866-05:00 impact = weather.get('weather_impact_assessment', '') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350271806-05:00 yd = weather.get('yesterday', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350273986-05:00 td = weather.get('today_forecast', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350276006-05:00 rain_hrs= weather.get('rain_hours_today', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350277986-05:00 weather_str = ( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350279976-05:00 f"Report day: {yd.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350281916-05:00 f"{yd.get('temp_min','?')}–{yd.get('temp_max','?')}°F | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350283936-05:00 f"{yd.get('precip_in',0)}\" precip\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350285966-05:00 f"Today forecast: {td.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350287966-05:00 f"{td.get('temp_min','?')}–{td.get('temp_max','?')}°F\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350290026-05:00 f"Impact assessment: {impact}\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350292076-05:00 + (f"Rain hours: {rain_hrs}\n" if rain_hrs else "") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350294056-05:00 ) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350296066-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350298026-05:00 weather_str = "No weather data provided." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350299986-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350302556-05:00 # ── Schedule section ────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350304606-05:00 schedule_section = kwargs.get('schedule_section', None) or '' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350306646-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350308716-05:00 # ── Category baselines ──────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350313426-05:00 cat_baselines = kwargs.get('category_baselines', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350315486-05:00 # weekly_category_totals: actual accumulated weekly revenue per category [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350317866-05:00 # passed from Cell 39 _week_cat — USE THESE VERBATIM in the report table [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350320456-05:00 weekly_cat_totals = kwargs.get('weekly_category_totals', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350322456-05:00 _is_weekly = (report_type == 'weekly') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350324466-05:00 # Authoritative per-category revenue comes from the owner-mapped [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350326556-05:00 # category_mix (preprocessor). Post-mapping it is aggregated by taxonomy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350328816-05:00 # name and already reconciles to inside sales, so it — NOT the seeded [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350330876-05:00 # baselines — must drive the category table. Previously this block only [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350333706-05:00 # emitted categories that had a seeded history baseline; when baselines [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350335706-05:00 # were sparse or unseeded the model received no authoritative totals and [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350337696-05:00 # dumped the bulk of inside sales into "Other / unclassified" (issue #81). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350339796-05:00 _cat_mix_rows = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350341816-05:00 c for c in scan.get('category_mix', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350343826-05:00 if isinstance(c.get('category'), str) and str(c.get('category')).strip() [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350345826-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350347786-05:00 _period_lbl = 'This Week' if _is_weekly else 'Today' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350349846-05:00 if _is_weekly and weekly_cat_totals: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350352476-05:00 # Legacy notebook path passes pre-accumulated weekly totals verbatim. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350354466-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350356506-05:00 "WEEKLY Category Totals — USE THESE EXACT NUMBERS in Section 3 table.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350360766-05:00 "DO NOT invent or estimate different values. These are computed from actual data.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350362896-05:00 f" {'Category':<30} {'This Week':>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350364866-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350366856-05:00 for cat, week_rev in sorted(weekly_cat_totals.items(), [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350368996-05:00 key=lambda x: -x[1]): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350370986-05:00 if not isinstance(cat, str) or not cat.strip(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350372966-05:00 continue [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350375036-05:00 avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350376966-05:00 if avg > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350379016-05:00 pct = (week_rev - avg) / avg * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350380976-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350382986-05:00 f" {cat:<30} ${week_rev:>10,.0f} ${avg:>10,.0f}/day {pct:>+7.1f}%") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350385036-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350387076-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350389086-05:00 f" {cat:<30} ${week_rev:>10,.0f} (no baseline)") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350391506-05:00 category_baselines_str = "\n".join(_cb_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350393476-05:00 elif _cat_mix_rows: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350395506-05:00 _classified = sum((c.get('revenue') or 0) for c in _cat_mix_rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350400376-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350402446-05:00 f"Per-category inside revenue ({_period_lbl}) — USE THESE EXACT NUMBERS " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350404496-05:00 f"in the Section 3 category table. They are computed from the owner's " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350406506-05:00 f"merchandise-code taxonomy and already sum to inside sales " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350408476-05:00 f"(${_classified:,.0f}). Only the row explicitly labelled " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350410466-05:00 f"'Other / Unclassified' is unclassified — do NOT move any other " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350412536-05:00 f"category's revenue into 'Other', and do NOT invent different values.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350414556-05:00 f" {'Category':<30} {_period_lbl:>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350416526-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350418536-05:00 for c in sorted(_cat_mix_rows, key=lambda x: -(x.get('revenue') or 0)): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350420626-05:00 cat = str(c['category']) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350422636-05:00 rev = c.get('revenue') or 0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350424606-05:00 > avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350427136-05:00 ^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350429226-05:00 E AttributeError: 'NoneType' object has no attribute 'get' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350431136-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350433236-05:00 src/storeminder/agents/synthesis_agent.py:1426: AttributeError [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350435286-05:00 _______________ ERROR at setup of test_weekly_builds_and_renders _______________ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350437146-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350439116-05:00 @pytest.fixture(scope="module") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350441156-05:00 def weekly_report(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350443106-05:00 > return _run("weekly") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350445066-05:00 ^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350446946-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350448966-05:00 tests/test_report_traits_live.py:99: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350451136-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350453196-05:00 tests/test_report_traits_live.py:75: in _run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350455206-05:00 res = fn(store, end_date=d, dry_run=True, deliver=False, clean=True) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350457256-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350459416-05:00 src/storeminder/runner.py:359: in run_weekly [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350461376-05:00 return _run_report(params, clean=clean) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350463326-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350465366-05:00 src/storeminder/runner.py:193: in _run_report [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350467316-05:00 result = run_pipeline( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350469326-05:00 src/storeminder/crew.py:456: in run_pipeline [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350471326-05:00 report_result = _agent.run_weekly( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350473686-05:00 src/storeminder/agents/synthesis_agent.py:1028: in run_weekly [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350476186-05:00 return self.run(report_type="weekly", **kwargs) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350478216-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350480225-05:00 src/storeminder/agents/synthesis_agent.py:1073: in run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350482255-05:00 user = self._build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350484775-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350489785-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350491845-05:00 self = [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350494205-05:00 report_date = datetime.date(2026, 7, 10), store_id = 'Fastime' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350496275-05:00 trans = {'total_transactions': 618, 'open_hour_start': 4, 'open_hour_end': 21, 'report_date_filter': '2026-07-10', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350498365-05:00 scan = {'total_scan_rows': 1940, 'report_date_filter': '2026-07-10', 'total_sales': 6436.97, 'avg_item_price': 3.88, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350500475-05:00 baseline = {'day_of_week': 21, 'total_revenue': 100081.6, 'total_transactions': 3568.94, 'fuel_gallons': 16961.39, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350502675-05:00 flags = [{'metric': 'rank_drop', 'label': 'Rank Drop: COPENHAGEN FINE CUT', 'today': 40, 'baseline': 13, ...}, {'metric': 'ran...': 29, ...}, {'metric': 'rank_drop', 'label': 'Rank Drop: BUD LIGHT BEER 12 O', 'today': 33, 'baseline': 22, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350507605-05:00 fuel_section = '## 2. Fuel \n**2,583 gal | $9,446 | 279 fuel transactions** \nFuel volume came in at 2,583 gallons, down from 2,758...nfirm pump access and functionality during 22:00–23:00 if store is open | Measure: Fuel dispensed in evening gap hours' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350509665-05:00 inside_section = '## 3. Inside Sales\n\n**Inside Sales: $6,437 (+9.5% vs normal Tuesday)** \nTobacco / Nicotine surged on strong Marlb...EGG ↑14, ADD CHEESE ↑8) | Because: Modifier use rising but Plate Lunch down 60% | Measure: Attach rate per food ticket' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350511775-05:00 weather = {'yesterday': {'temp_max': 88.3, 'temp_min': 75.4, 'precip_in': 0.0, 'conditions': 'Clear'}, 'today_forecast': {'temp_...cloudy'}, 'rain_hours_today': [], 'weather_impact_assessment': 'Low — weather was mild, not a significant factor', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350513865-05:00 external_section = '## External Context\n\n**Google Reviews (5 reviews)** \nAverage rating of recent reviews (3.0/5) is below store’s ov...- Verify staff scheduling: confirm Luna’s shift coverage and retrain team on food handling consistency — Do by 3:00 PM' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350515845-05:00 loyalty_section = '## Loyalty & Promotions\n\n**Redemption Summary**\n| Promotion | Item | Today | Period Avg | Delta | Status |\n|-----...te and consider extending top performers like *Red Bull Original* and *Doritos Nacho Cheese* — evaluate by end of week' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350518025-05:00 ops_section = '## 5. Operations\n\n**Transactions & Throughput** \n962 total transactions | Avg ticket: $17.10 (+12% vs normal Frid... daily early fuel/retail volume.* \n\nNo additional hours recommended — optimize existing ownership and shift timing.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350521755-05:00 fuel_pricing_section = '## Fuel Pricing\n\n**Current Market** \n| Station | Regular | Midgrade | Premium | Diesel | Distance | \n|---------...y 8:00 AM tomorrow (2026-07-11). \n- Monitor Murphy USA and FASTIME for updates; both changed prices in last 3 hours.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350523835-05:00 prior_actions = None, report_type = 'weekly' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350525835-05:00 weekly_days = [{'date': '2026-07-04', 'dow_name': 'Saturday', 'total_revenue': 7392.12, 'fuel_gallons': 1142.1, ...}, {'date': '2026...42.4, ...}, {'date': '2026-07-09', 'dow_name': 'Thursday', 'total_revenue': 16227.6, 'fuel_gallons': 2780.6, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350527855-05:00 dow_profile = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350530115-05:00 kwargs = {'category_baselines': None, 'schedule_section': None, 'weekly_category_totals': None, 'ext_fetch_date': '2026-07-12', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350532015-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350534095-05:00 def _build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350536075-05:00 self, report_date, store_id, trans, scan, baseline, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350541105-05:00 flags, fuel_section, inside_section, weather, external_section, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350543185-05:00 loyalty_section, ops_section, fuel_pricing_section, prior_actions, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350545125-05:00 report_type: str = "daily", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350547655-05:00 weekly_days: Optional[List[Dict]] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350549595-05:00 dow_profile: Optional[Dict] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350551555-05:00 **kwargs, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350554235-05:00 ) -> str: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350556245-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350558325-05:00 # ── KPI table ───────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350560325-05:00 kpi_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350562325-05:00 "| Metric | Today | Baseline | Delta | Priority |", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350564325-05:00 "|--------|-------|----------|-------|----------|", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350566325-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350568325-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350570285-05:00 # Keys that live in scan_summary (inside sales) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350572285-05:00 _scan_keys = {'total_sales', 'avg_item_price', 'total_scan_rows'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350574455-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350576435-05:00 def kpi_row(label, key, fmt_str=",.0f", prefix="", priority=None): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350578385-05:00 # Prefer scan for inside-sales metrics, trans for everything else [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350580345-05:00 if key in _scan_keys: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350582285-05:00 t = scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350584255-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350586255-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350588255-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350590265-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350592235-05:00 return [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350594245-05:00 t_str = f"{prefix}{t:{fmt_str}}" if isinstance(t, (int, float)) else str(t) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350596235-05:00 b_str = f"{prefix}{b:{fmt_str}}" if isinstance(b, (int, float)) else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350598235-05:00 delta = f"{((t-b)/b*100):+.1f}%" if isinstance(t,(int,float)) and isinstance(b,(int,float)) and b else "—" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350600185-05:00 # Priority: use flag severity if exists, else spec default [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350602095-05:00 flag = next((f for f in flags if f['metric'] == key), None) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350606325-05:00 pri = flag['severity'] if flag else (priority or "—") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350608545-05:00 kpi_lines.append(f"| {label} | {t_str} | {b_str} | {delta} | {pri} |") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350610495-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350613005-05:00 kpi_row("Total Transactions", "total_transactions", ",.0f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350615185-05:00 kpi_row("Total Sales", "total_revenue", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350617825-05:00 kpi_row("Inside Sales", "total_sales", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350619865-05:00 kpi_row("Avg Inside Ticket", "avg_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350621845-05:00 kpi_row("Avg Fuel Ticket", "avg_fuel_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350626885-05:00 kpi_row("Fuel Gallons", "fuel_gallons", ",.1f", "", "HIGH") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350628925-05:00 kpi_row("Fuel Sales $", "fuel_sales", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350630955-05:00 kpi_row("Fuel Transactions", "fuel_transactions", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350632895-05:00 kpi_row("Avg Gallons/Tx", "avg_gallons_per_tx", ",.2f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350634845-05:00 kpi_row("Forecourt Conv %", "forecourt_conversion_pct", ",.1f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350636745-05:00 kpi_row("Void Count", "void_count", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350638685-05:00 kpi_row("Void Total $", "void_total_amt", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350640655-05:00 kpi_row("Inside/100 Gal (weekly avg)", "inside_per_100_gal", ",.0f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350642615-05:00 kpi_row("Eff. Fuel Price", "effective_fuel_price", ",.3f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350644575-05:00 kpi_row("Inside Share %", "inside_sales_share_pct", ",.1f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350646585-05:00 kpi_summary = "\n".join(kpi_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350648515-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350651025-05:00 # ── Derived metrics for snapshot header ──────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350653275-05:00 def _dm(key, label, prefix="", suffix="", decimals=2): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350655245-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350657365-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350659365-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350661325-05:00 return None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350663325-05:00 t_str = f"{prefix}{float(t):,.{decimals}f}{suffix}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350665355-05:00 if b and float(b) > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350667325-05:00 pct = (float(t) - float(b)) / float(b) * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350669315-05:00 return f"{label}: {t_str} ({pct:+.1f}% vs baseline)" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350671355-05:00 return f"{label}: {t_str}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350673345-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350675325-05:00 dm_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350677335-05:00 v = _dm('inside_per_100_gal', 'Inside/100gal', '$', '', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350679305-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350681385-05:00 v = _dm('effective_fuel_price', 'Effective fuel price', '$', '/gal', 3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350683345-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350685335-05:00 v = _dm('inside_sales_share_pct','Inside share of sales', '', '%', 1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350687295-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350689485-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350691495-05:00 gap = trans.get('fuel_inside_gap_pct') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350693485-05:00 if gap is not None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350695485-05:00 direction = "FUEL OUTPACING INSIDE" if gap > 0 else "INSIDE OUTPACING FUEL" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350697505-05:00 dm_lines.append(f"Fuel-vs-Inside gap: {gap:+.1f}pp ({direction})") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350699455-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350701455-05:00 derived_metrics = "\n".join(dm_lines) if dm_lines else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350703405-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350705505-05:00 # ── Flags summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350710565-05:00 if flags: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350712535-05:00 flags_str = "\n".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350714615-05:00 f"- [{f['severity']}] {f['label']}: {f['note']}" for f in flags) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350716595-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350718545-05:00 flags_str = "No significant anomalies detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350720515-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350722555-05:00 # ── Shift summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350724715-05:00 shifts = trans.get('shift_summary', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350726685-05:00 if shifts: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350728625-05:00 shift_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350730565-05:00 for name, data in shifts.items(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350733385-05:00 rev = data.get('revenue', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350735335-05:00 tx = data.get('transactions', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350737315-05:00 gals = data.get('fuel_gallons', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350739415-05:00 shift_lines.append(f"- {name}: ${rev:,.2f} inside | {tx} tx | {gals:,.1f} gal fuel") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350741435-05:00 shift_summary = "\n".join(shift_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350743425-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350745415-05:00 shift_summary = "Shift data not available." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350747335-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350756775-05:00 # ── Gap / zero-tx hours ─────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350760235-05:00 gaps = trans.get('top_hourly_gaps', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350762885-05:00 zero_tx = trans.get('zero_tx_hours', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350765324-05:00 gap_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350767734-05:00 if gaps: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350770414-05:00 gap_str = ", ".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350774494-05:00 "{:02d}:00 ({} tx)".format(g['_hour'], g['tx_count']) for g in gaps) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350776204-05:00 gap_lines.append(f"Top gap hours: {gap_str}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350778174-05:00 if zero_tx: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350779914-05:00 gap_lines.append(f"Zero-tx hours (open window): {zero_tx}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350781604-05:00 gap_summary = "\n".join(gap_lines) if gap_lines else "No unusual gaps detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350783204-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350784864-05:00 # Authoritative store hours (issue #128): when Google Places supplies the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350786614-05:00 # store's real hours, prepend them and instruct the model to treat [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350788284-05:00 # off-hours zero activity as expected closure rather than hedging. The [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350790754-05:00 # gap/zero-tx figures above are already computed against this same window. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350792454-05:00 store_hours_val = kwargs.get('store_hours') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350794124-05:00 if store_hours_val and store_hours_val.get('weekday_text'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350795954-05:00 hrs_lines = ["AUTHORITATIVE STORE HOURS (Google Places — treat as ground truth):"] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350797624-05:00 hrs_lines += [f" {ln}" for ln in store_hours_val['weekday_text']] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350804044-05:00 if store_hours_val.get('open_24h'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350806454-05:00 hrs_lines.append(" This store operates 24 hours on at least one day.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350808044-05:00 hrs_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350809804-05:00 "Zero-transaction hours OUTSIDE these posted hours are EXPECTED " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350811414-05:00 "CLOSURE — do NOT flag them as gaps and do NOT hedge about whether " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350813004-05:00 "the store was open. Treat zero/low activity DURING posted open " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350814674-05:00 "hours as a real gap worth investigating.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350816294-05:00 gap_summary = "\n".join(hrs_lines) + "\n\n" + gap_summary [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350817834-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350819524-05:00 # ── Weather ─────────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350821164-05:00 if weather: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350823254-05:00 temp = weather.get('temp', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350825194-05:00 precip = weather.get('precipitation_in', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350826804-05:00 conds = list(weather.get('conditions', {}).keys()) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350828434-05:00 impact = weather.get('weather_impact_assessment', '') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350830044-05:00 yd = weather.get('yesterday', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350831764-05:00 td = weather.get('today_forecast', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350833484-05:00 rain_hrs= weather.get('rain_hours_today', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350835054-05:00 weather_str = ( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350836674-05:00 f"Report day: {yd.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350838344-05:00 f"{yd.get('temp_min','?')}–{yd.get('temp_max','?')}°F | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350840044-05:00 f"{yd.get('precip_in',0)}\" precip\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350841814-05:00 f"Today forecast: {td.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350843454-05:00 f"{td.get('temp_min','?')}–{td.get('temp_max','?')}°F\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350845144-05:00 f"Impact assessment: {impact}\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350846774-05:00 + (f"Rain hours: {rain_hrs}\n" if rain_hrs else "") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350848344-05:00 ) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350849964-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350851524-05:00 weather_str = "No weather data provided." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350853144-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350854834-05:00 # ── Schedule section ────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350856654-05:00 schedule_section = kwargs.get('schedule_section', None) or '' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350858244-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350860004-05:00 # ── Category baselines ──────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350861744-05:00 cat_baselines = kwargs.get('category_baselines', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350863694-05:00 # weekly_category_totals: actual accumulated weekly revenue per category [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350865604-05:00 # passed from Cell 39 _week_cat — USE THESE VERBATIM in the report table [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350872674-05:00 weekly_cat_totals = kwargs.get('weekly_category_totals', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350874854-05:00 _is_weekly = (report_type == 'weekly') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350876994-05:00 # Authoritative per-category revenue comes from the owner-mapped [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350878774-05:00 # category_mix (preprocessor). Post-mapping it is aggregated by taxonomy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350880804-05:00 # name and already reconciles to inside sales, so it — NOT the seeded [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350882494-05:00 # baselines — must drive the category table. Previously this block only [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350884144-05:00 # emitted categories that had a seeded history baseline; when baselines [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350885854-05:00 # were sparse or unseeded the model received no authoritative totals and [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350887584-05:00 # dumped the bulk of inside sales into "Other / unclassified" (issue #81). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350889404-05:00 _cat_mix_rows = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350893284-05:00 c for c in scan.get('category_mix', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350895474-05:00 if isinstance(c.get('category'), str) and str(c.get('category')).strip() [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350897254-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350899004-05:00 _period_lbl = 'This Week' if _is_weekly else 'Today' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350900624-05:00 if _is_weekly and weekly_cat_totals: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350902264-05:00 # Legacy notebook path passes pre-accumulated weekly totals verbatim. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350903874-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350905514-05:00 "WEEKLY Category Totals — USE THESE EXACT NUMBERS in Section 3 table.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350907304-05:00 "DO NOT invent or estimate different values. These are computed from actual data.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350909034-05:00 f" {'Category':<30} {'This Week':>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350910704-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350912804-05:00 for cat, week_rev in sorted(weekly_cat_totals.items(), [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350914674-05:00 key=lambda x: -x[1]): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350916324-05:00 if not isinstance(cat, str) or not cat.strip(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350923044-05:00 continue [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350926294-05:00 avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350928904-05:00 if avg > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350930594-05:00 pct = (week_rev - avg) / avg * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350932184-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350934364-05:00 f" {cat:<30} ${week_rev:>10,.0f} ${avg:>10,.0f}/day {pct:>+7.1f}%") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350936074-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350937654-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350939834-05:00 f" {cat:<30} ${week_rev:>10,.0f} (no baseline)") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350941464-05:00 category_baselines_str = "\n".join(_cb_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350943114-05:00 elif _cat_mix_rows: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350944754-05:00 _classified = sum((c.get('revenue') or 0) for c in _cat_mix_rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350946414-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350948044-05:00 f"Per-category inside revenue ({_period_lbl}) — USE THESE EXACT NUMBERS " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350950354-05:00 f"in the Section 3 category table. They are computed from the owner's " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350955394-05:00 f"merchandise-code taxonomy and already sum to inside sales " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350957074-05:00 f"(${_classified:,.0f}). Only the row explicitly labelled " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350958754-05:00 f"'Other / Unclassified' is unclassified — do NOT move any other " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350960444-05:00 f"category's revenue into 'Other', and do NOT invent different values.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350962084-05:00 f" {'Category':<30} {_period_lbl:>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350963754-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350965544-05:00 for c in sorted(_cat_mix_rows, key=lambda x: -(x.get('revenue') or 0)): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350967094-05:00 cat = str(c['category']) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350968664-05:00 rev = c.get('revenue') or 0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350970304-05:00 > avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350971984-05:00 ^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350973614-05:00 E AttributeError: 'NoneType' object has no attribute 'get' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350975114-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350976704-05:00 src/storeminder/agents/synthesis_agent.py:1426: AttributeError [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350978334-05:00 ---------------------------- Captured stdout setup ----------------------------- [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350979974-05:00 Found 14 XML file(s) in /scratch/v6/Fastime/2026-07-10/raw [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350981584-05:00 Parsed: 2026-07-04.363.xml (460 tx rows, 810 scan rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350983214-05:00 Parsed: 2026-07-04.521.xml (460 tx rows, 810 scan rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350985664-05:00 Parsed: 2026-07-05.364.xml (442 tx rows, 789 scan rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350987214-05:00 Parsed: 2026-07-05.522.xml (442 tx rows, 789 scan rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350988844-05:00 Parsed: 2026-07-06.365.xml (853 tx rows, 1623 scan rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350990544-05:00 Parsed: 2026-07-06.523.xml (853 tx rows, 1623 scan rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350992864-05:00 Parsed: 2026-07-07.366.xml (873 tx rows, 1716 scan rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350994484-05:00 Parsed: 2026-07-07.524.xml (873 tx rows, 1716 scan rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350996064-05:00 Parsed: 2026-07-08.367.xml (953 tx rows, 1837 scan rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350997674-05:00 Parsed: 2026-07-08.525.xml (953 tx rows, 1837 scan rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.350999314-05:00 Parsed: 2026-07-09.368.xml (958 tx rows, 1876 scan rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351000894-05:00 Parsed: 2026-07-09.526.xml (958 tx rows, 1876 scan rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351002434-05:00 Parsed: 2026-07-10.369.xml (966 tx rows, 1940 scan rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351004064-05:00 Parsed: 2026-07-10.527.xml (966 tx rows, 1940 scan rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351005534-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351007084-05:00 Transactions : 5505 rows [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351008734-05:00 Date range : 2026-07-03 19:56:24 → 2026-07-10 20:34:48 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351010834-05:00 Types : [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351012444-05:00 Type [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351014094-05:00 sale 3542 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351015664-05:00 network sale 1572 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351017224-05:00 error_correct 233 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351018834-05:00 safedrop 67 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351020374-05:00 cashier_open 20 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351022024-05:00 cashier_close 20 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351023604-05:00 void 19 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351025154-05:00 cashier_audit 19 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351029814-05:00 payout 11 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351031464-05:00 refundevent 2 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351032944-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351034564-05:00 Scanned items: 10591 rows [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351036234-05:00 Line types : [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351037894-05:00 Line Type [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351039554-05:00 item 8891 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351041164-05:00 fuel 1583 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351042753-05:00 merch_code 117 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351045173-05:00 Fuel volume: 15,703.814 gal [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351046643-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351048263-05:00 Saved → /scratch/v6/Fastime/2026-07-10/curated/combined_raw_transactions_20260712_234108.csv [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351052063-05:00 Saved → /scratch/v6/Fastime/2026-07-10/curated/combined_raw_scanned_20260712_234108.csv [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351053643-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351055243-05:00 ============================================================ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351056873-05:00 C-Store Analytics — v4 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351058523-05:00 Store: Fastime | Date: 2026-07-10 | Lang: English [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351060123-05:00 ============================================================ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351061613-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351063143-05:00 📂 [1/10] Loading data... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351066183-05:00 ✓ Loaded 10,591 scan rows | cols: ['time', 'date', 'Type', 'Transaction ID', 'Event Seq ID', 'Store ID', 'Register ID', 'Cashier ID', 'Payment Type', 'Payment Sub Type', 'Source File', 'Line Seq', 'Line Status', 'category', 'UPC Code', 'item', 'Entry Method', 'Merchandise Code', 'Sales Qty', 'Sales UOM', 'amount', 'Regular Price', 'Actual Price', 'Price Override', 'Price Override Reason', 'Promotion ID', 'Promotion Reason', 'Promotion Amount', 'Tax Level', 'Age Restricted', 'Min Age', 'Fuel Grade ID', 'Fuel Position ID', 'Fuel UOM'] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351068043-05:00 ✓ Loaded 5,505 trans rows | cols: ['Time', 'End Time', 'Business Date', 'Type', 'Transaction ID', 'Event Seq ID', 'Store ID', 'Register ID', 'Cashier ID', 'Till ID', 'Outside Sale', 'Suspended', 'Training Mode', 'Offline', 'Gross Amount', 'Net Amount', 'Tax Amount', 'Total Amount', 'Grand Direction', 'Item Line Count', 'Fuel Line Count', 'Tender Count', 'Tender Type', 'Payment Sub Type', 'Change Amount', 'Void Reason', 'Cash In Drawer', 'Food Stamps', 'Financial Sub-Type', 'Financial Amount', 'Financial Tender', 'Memo', 'Loyalty Discount', 'Loyalty Program', 'Report Seq', 'Report Interval', 'Report Period', 'Report Begin', 'Report End', 'Source File', 'Vendor', 'Vendor Model', 'datetime'] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351069673-05:00 Places hours status: 200 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351071373-05:00 ✓ Store hours (Google): 4:00–21:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351073023-05:00 ✓ Scan: 1,940 rows | Trans: 618 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351074653-05:00 ✓ Fuel merged from scan: 2,582.6 gal | $9,446.27 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351076123-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351077673-05:00 📊 [2/10] Loading baseline... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351079323-05:00 ✓ Baseline: 15 same-DOW samples [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351080813-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351082383-05:00 🔍 [3/10] Anomaly detection... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351083963-05:00 ✓ 18 HIGH / 22 MEDIUM flags [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351085463-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351087043-05:00 🌐 [4/10] External context... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351088673-05:00 Places API (New) status: 200 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351090313-05:00 → 5 reviews returned (overall rating 4.3, 532 total ratings on Google) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351092033-05:00 ✓ Reviews: 5 fetched, avg 3.0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351093613-05:00 ✓ Weather: Clear [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351095183-05:00 Routes API status: 200 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351099793-05:00 ✓ Traffic: Google Routes API (live road traffic) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351101443-05:00 ✓ External: 1734 chars | reviews=5 | traffic=LOW [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351102933-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351104553-05:00 ⛽ [5/10] Fuel Agent... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351106623-05:00 ✓ Fuel section written [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351108143-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351109703-05:00 💰 [6/10] Fuel Pricing Agent... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351111423-05:00 Fetching competitor fuel prices... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351113143-05:00 → 16 gas stations found with price data [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351114753-05:00 ✓ Fuel pricing section written [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351116233-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351117943-05:00 🛒 [7/10] Category Management Agent... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351120253-05:00 ✓ Category management section written [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351121763-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351123323-05:00 🎁 [8/10] Loyalty & Promotions Agent... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351124973-05:00 ✓ Loyalty section written [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351126473-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351128013-05:00 ⚙️ [9/10] Operations Agent... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351129643-05:00 ✓ Operations section written [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351131183-05:00 📅 Generating schedule optimisation... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351132733-05:00 ✓ Weekly per-day breakdown: 7 trading day(s) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351134973-05:00 ✓ Weekly baseline: summed across 7 day(s) (totals scaled, ratios recomputed) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351136483-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351138003-05:00 📝 [10/10] Synthesis Agent... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351139673-05:00 __________________ ERROR at setup of test_weekly_is_sendable ___________________ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351141163-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351142783-05:00 @pytest.fixture(scope="module") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351144373-05:00 def weekly_report(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351146623-05:00 > return _run("weekly") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351148183-05:00 ^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351149673-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351151263-05:00 tests/test_report_traits_live.py:99: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351152973-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351154543-05:00 tests/test_report_traits_live.py:75: in _run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351156163-05:00 res = fn(store, end_date=d, dry_run=True, deliver=False, clean=True) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351157803-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351159413-05:00 src/storeminder/runner.py:359: in run_weekly [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351161023-05:00 return _run_report(params, clean=clean) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351162643-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351164203-05:00 src/storeminder/runner.py:193: in _run_report [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351165753-05:00 result = run_pipeline( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351167353-05:00 src/storeminder/crew.py:456: in run_pipeline [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351168943-05:00 report_result = _agent.run_weekly( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351170473-05:00 src/storeminder/agents/synthesis_agent.py:1028: in run_weekly [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351172073-05:00 return self.run(report_type="weekly", **kwargs) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351174103-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351175743-05:00 src/storeminder/agents/synthesis_agent.py:1073: in run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351177763-05:00 user = self._build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351179483-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351184343-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351186053-05:00 self = [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351190013-05:00 report_date = datetime.date(2026, 7, 10), store_id = 'Fastime' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351191753-05:00 trans = {'total_transactions': 618, 'open_hour_start': 4, 'open_hour_end': 21, 'report_date_filter': '2026-07-10', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351193483-05:00 scan = {'total_scan_rows': 1940, 'report_date_filter': '2026-07-10', 'total_sales': 6436.97, 'avg_item_price': 3.88, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351195153-05:00 baseline = {'day_of_week': 21, 'total_revenue': 100081.6, 'total_transactions': 3568.94, 'fuel_gallons': 16961.39, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351197233-05:00 flags = [{'metric': 'rank_drop', 'label': 'Rank Drop: COPENHAGEN FINE CUT', 'today': 40, 'baseline': 13, ...}, {'metric': 'ran...': 29, ...}, {'metric': 'rank_drop', 'label': 'Rank Drop: BUD LIGHT BEER 12 O', 'today': 33, 'baseline': 22, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351198963-05:00 fuel_section = '## 2. Fuel \n**2,583 gal | $9,446 | 279 fuel transactions** \nFuel volume came in at 2,583 gallons, down from 2,758...nfirm pump access and functionality during 22:00–23:00 if store is open | Measure: Fuel dispensed in evening gap hours' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351201163-05:00 inside_section = '## 3. Inside Sales\n\n**Inside Sales: $6,437 (+9.5% vs normal Tuesday)** \nTobacco / Nicotine surged on strong Marlb...EGG ↑14, ADD CHEESE ↑8) | Because: Modifier use rising but Plate Lunch down 60% | Measure: Attach rate per food ticket' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351202823-05:00 weather = {'yesterday': {'temp_max': 88.3, 'temp_min': 75.4, 'precip_in': 0.0, 'conditions': 'Clear'}, 'today_forecast': {'temp_...cloudy'}, 'rain_hours_today': [], 'weather_impact_assessment': 'Low — weather was mild, not a significant factor', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351209973-05:00 external_section = '## External Context\n\n**Google Reviews (5 reviews)** \nAverage rating of recent reviews (3.0/5) is below store’s ov...- Verify staff scheduling: confirm Luna’s shift coverage and retrain team on food handling consistency — Do by 3:00 PM' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351212273-05:00 loyalty_section = '## Loyalty & Promotions\n\n**Redemption Summary**\n| Promotion | Item | Today | Period Avg | Delta | Status |\n|-----...te and consider extending top performers like *Red Bull Original* and *Doritos Nacho Cheese* — evaluate by end of week' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351214023-05:00 ops_section = '## 5. Operations\n\n**Transactions & Throughput** \n962 total transactions | Avg ticket: $17.10 (+12% vs normal Frid... daily early fuel/retail volume.* \n\nNo additional hours recommended — optimize existing ownership and shift timing.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351215623-05:00 fuel_pricing_section = '## Fuel Pricing\n\n**Current Market** \n| Station | Regular | Midgrade | Premium | Diesel | Distance | \n|---------...y 8:00 AM tomorrow (2026-07-11). \n- Monitor Murphy USA and FASTIME for updates; both changed prices in last 3 hours.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351217283-05:00 prior_actions = None, report_type = 'weekly' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351218923-05:00 weekly_days = [{'date': '2026-07-04', 'dow_name': 'Saturday', 'total_revenue': 7392.12, 'fuel_gallons': 1142.1, ...}, {'date': '2026...42.4, ...}, {'date': '2026-07-09', 'dow_name': 'Thursday', 'total_revenue': 16227.6, 'fuel_gallons': 2780.6, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351220503-05:00 dow_profile = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351222183-05:00 kwargs = {'category_baselines': None, 'schedule_section': None, 'weekly_category_totals': None, 'ext_fetch_date': '2026-07-12', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351223653-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351225183-05:00 def _build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351226833-05:00 self, report_date, store_id, trans, scan, baseline, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351228463-05:00 flags, fuel_section, inside_section, weather, external_section, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351232853-05:00 loyalty_section, ops_section, fuel_pricing_section, prior_actions, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351234513-05:00 report_type: str = "daily", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351236043-05:00 weekly_days: Optional[List[Dict]] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351237633-05:00 dow_profile: Optional[Dict] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351239193-05:00 **kwargs, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351240753-05:00 ) -> str: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351242413-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351244033-05:00 # ── KPI table ───────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351245613-05:00 kpi_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351247903-05:00 "| Metric | Today | Baseline | Delta | Priority |", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351249483-05:00 "|--------|-------|----------|-------|----------|", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351251053-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351252673-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351254263-05:00 # Keys that live in scan_summary (inside sales) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351255993-05:00 _scan_keys = {'total_sales', 'avg_item_price', 'total_scan_rows'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351257583-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351259213-05:00 def kpi_row(label, key, fmt_str=",.0f", prefix="", priority=None): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351260843-05:00 # Prefer scan for inside-sales metrics, trans for everything else [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351262383-05:00 if key in _scan_keys: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351263953-05:00 t = scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351265533-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351269613-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351271253-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351272813-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351274543-05:00 return [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351276163-05:00 t_str = f"{prefix}{t:{fmt_str}}" if isinstance(t, (int, float)) else str(t) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351277773-05:00 b_str = f"{prefix}{b:{fmt_str}}" if isinstance(b, (int, float)) else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351279333-05:00 delta = f"{((t-b)/b*100):+.1f}%" if isinstance(t,(int,float)) and isinstance(b,(int,float)) and b else "—" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351280873-05:00 # Priority: use flag severity if exists, else spec default [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351282523-05:00 flag = next((f for f in flags if f['metric'] == key), None) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351284153-05:00 pri = flag['severity'] if flag else (priority or "—") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351285783-05:00 kpi_lines.append(f"| {label} | {t_str} | {b_str} | {delta} | {pri} |") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351287353-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351288933-05:00 kpi_row("Total Transactions", "total_transactions", ",.0f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351290533-05:00 kpi_row("Total Sales", "total_revenue", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351292103-05:00 kpi_row("Inside Sales", "total_sales", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351293663-05:00 kpi_row("Avg Inside Ticket", "avg_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351295223-05:00 kpi_row("Avg Fuel Ticket", "avg_fuel_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351296773-05:00 kpi_row("Fuel Gallons", "fuel_gallons", ",.1f", "", "HIGH") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351306913-05:00 kpi_row("Fuel Sales $", "fuel_sales", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351308503-05:00 kpi_row("Fuel Transactions", "fuel_transactions", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351310143-05:00 kpi_row("Avg Gallons/Tx", "avg_gallons_per_tx", ",.2f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351311712-05:00 kpi_row("Forecourt Conv %", "forecourt_conversion_pct", ",.1f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351313302-05:00 kpi_row("Void Count", "void_count", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351314972-05:00 kpi_row("Void Total $", "void_total_amt", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351316892-05:00 kpi_row("Inside/100 Gal (weekly avg)", "inside_per_100_gal", ",.0f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351318522-05:00 kpi_row("Eff. Fuel Price", "effective_fuel_price", ",.3f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351320062-05:00 kpi_row("Inside Share %", "inside_sales_share_pct", ",.1f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351321662-05:00 kpi_summary = "\n".join(kpi_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351323212-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351324982-05:00 # ── Derived metrics for snapshot header ──────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351326552-05:00 def _dm(key, label, prefix="", suffix="", decimals=2): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351328172-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351329842-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351331402-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351333092-05:00 return None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351334642-05:00 t_str = f"{prefix}{float(t):,.{decimals}f}{suffix}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351336202-05:00 if b and float(b) > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351337822-05:00 pct = (float(t) - float(b)) / float(b) * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351339442-05:00 return f"{label}: {t_str} ({pct:+.1f}% vs baseline)" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351340972-05:00 return f"{label}: {t_str}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351342562-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351344132-05:00 dm_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351345752-05:00 v = _dm('inside_per_100_gal', 'Inside/100gal', '$', '', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351347362-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351348972-05:00 v = _dm('effective_fuel_price', 'Effective fuel price', '$', '/gal', 3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351350532-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351352072-05:00 v = _dm('inside_sales_share_pct','Inside share of sales', '', '%', 1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351353642-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351355202-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351356782-05:00 gap = trans.get('fuel_inside_gap_pct') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351358362-05:00 if gap is not None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351360052-05:00 direction = "FUEL OUTPACING INSIDE" if gap > 0 else "INSIDE OUTPACING FUEL" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351361632-05:00 dm_lines.append(f"Fuel-vs-Inside gap: {gap:+.1f}pp ({direction})") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351363162-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351364892-05:00 derived_metrics = "\n".join(dm_lines) if dm_lines else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351366452-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351368172-05:00 # ── Flags summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351372192-05:00 if flags: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351373782-05:00 flags_str = "\n".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351375362-05:00 f"- [{f['severity']}] {f['label']}: {f['note']}" for f in flags) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351376932-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351378492-05:00 flags_str = "No significant anomalies detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351380052-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351383682-05:00 # ── Shift summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351385322-05:00 shifts = trans.get('shift_summary', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351386922-05:00 if shifts: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351388492-05:00 shift_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351390052-05:00 for name, data in shifts.items(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351391632-05:00 rev = data.get('revenue', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351393192-05:00 tx = data.get('transactions', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351394802-05:00 gals = data.get('fuel_gallons', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351396442-05:00 shift_lines.append(f"- {name}: ${rev:,.2f} inside | {tx} tx | {gals:,.1f} gal fuel") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351398032-05:00 shift_summary = "\n".join(shift_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351399622-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351401262-05:00 shift_summary = "Shift data not available." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351402822-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351404662-05:00 # ── Gap / zero-tx hours ─────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351406572-05:00 gaps = trans.get('top_hourly_gaps', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351408172-05:00 zero_tx = trans.get('zero_tx_hours', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351409752-05:00 gap_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351411322-05:00 if gaps: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351412902-05:00 gap_str = ", ".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351414622-05:00 "{:02d}:00 ({} tx)".format(g['_hour'], g['tx_count']) for g in gaps) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351416162-05:00 gap_lines.append(f"Top gap hours: {gap_str}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351417722-05:00 if zero_tx: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351419272-05:00 gap_lines.append(f"Zero-tx hours (open window): {zero_tx}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351420882-05:00 gap_summary = "\n".join(gap_lines) if gap_lines else "No unusual gaps detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351422582-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351424252-05:00 # Authoritative store hours (issue #128): when Google Places supplies the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351426002-05:00 # store's real hours, prepend them and instruct the model to treat [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351427692-05:00 # off-hours zero activity as expected closure rather than hedging. The [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351429282-05:00 # gap/zero-tx figures above are already computed against this same window. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351431202-05:00 store_hours_val = kwargs.get('store_hours') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351432792-05:00 if store_hours_val and store_hours_val.get('weekday_text'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351434352-05:00 hrs_lines = ["AUTHORITATIVE STORE HOURS (Google Places — treat as ground truth):"] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351436082-05:00 hrs_lines += [f" {ln}" for ln in store_hours_val['weekday_text']] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351440142-05:00 if store_hours_val.get('open_24h'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351441692-05:00 hrs_lines.append(" This store operates 24 hours on at least one day.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351443382-05:00 hrs_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351444942-05:00 "Zero-transaction hours OUTSIDE these posted hours are EXPECTED " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351446512-05:00 "CLOSURE — do NOT flag them as gaps and do NOT hedge about whether " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351448082-05:00 "the store was open. Treat zero/low activity DURING posted open " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351449682-05:00 "hours as a real gap worth investigating.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351451342-05:00 gap_summary = "\n".join(hrs_lines) + "\n\n" + gap_summary [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351452892-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351454662-05:00 # ── Weather ─────────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351456302-05:00 if weather: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351457852-05:00 temp = weather.get('temp', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351459482-05:00 precip = weather.get('precipitation_in', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351461042-05:00 conds = list(weather.get('conditions', {}).keys()) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351462642-05:00 impact = weather.get('weather_impact_assessment', '') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351464292-05:00 yd = weather.get('yesterday', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351465822-05:00 td = weather.get('today_forecast', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351467352-05:00 rain_hrs= weather.get('rain_hours_today', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351469012-05:00 weather_str = ( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351470562-05:00 f"Report day: {yd.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351472152-05:00 f"{yd.get('temp_min','?')}–{yd.get('temp_max','?')}°F | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351473672-05:00 f"{yd.get('precip_in',0)}\" precip\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351475272-05:00 f"Today forecast: {td.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351476932-05:00 f"{td.get('temp_min','?')}–{td.get('temp_max','?')}°F\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351478452-05:00 f"Impact assessment: {impact}\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351480082-05:00 + (f"Rain hours: {rain_hrs}\n" if rain_hrs else "") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351481632-05:00 ) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351483182-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351484702-05:00 weather_str = "No weather data provided." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351486242-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351487812-05:00 # ── Schedule section ────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351489522-05:00 schedule_section = kwargs.get('schedule_section', None) or '' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351491072-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351494682-05:00 # ── Category baselines ──────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351496302-05:00 cat_baselines = kwargs.get('category_baselines', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351497992-05:00 # weekly_category_totals: actual accumulated weekly revenue per category [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351499612-05:00 # passed from Cell 39 _week_cat — USE THESE VERBATIM in the report table [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351503662-05:00 weekly_cat_totals = kwargs.get('weekly_category_totals', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351505262-05:00 _is_weekly = (report_type == 'weekly') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351506872-05:00 # Authoritative per-category revenue comes from the owner-mapped [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351508412-05:00 # category_mix (preprocessor). Post-mapping it is aggregated by taxonomy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351509962-05:00 # name and already reconciles to inside sales, so it — NOT the seeded [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351511512-05:00 # baselines — must drive the category table. Previously this block only [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351513062-05:00 # emitted categories that had a seeded history baseline; when baselines [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351514602-05:00 # were sparse or unseeded the model received no authoritative totals and [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351516142-05:00 # dumped the bulk of inside sales into "Other / unclassified" (issue #81). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351517752-05:00 _cat_mix_rows = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351519412-05:00 c for c in scan.get('category_mix', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351520932-05:00 if isinstance(c.get('category'), str) and str(c.get('category')).strip() [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351522472-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351524002-05:00 _period_lbl = 'This Week' if _is_weekly else 'Today' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351525542-05:00 if _is_weekly and weekly_cat_totals: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351527212-05:00 # Legacy notebook path passes pre-accumulated weekly totals verbatim. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351528772-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351530352-05:00 "WEEKLY Category Totals — USE THESE EXACT NUMBERS in Section 3 table.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351532002-05:00 "DO NOT invent or estimate different values. These are computed from actual data.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351533622-05:00 f" {'Category':<30} {'This Week':>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351535282-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351536792-05:00 for cat, week_rev in sorted(weekly_cat_totals.items(), [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351538362-05:00 key=lambda x: -x[1]): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351539902-05:00 if not isinstance(cat, str) or not cat.strip(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351541492-05:00 continue [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351543042-05:00 avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351544602-05:00 if avg > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351546172-05:00 pct = (week_rev - avg) / avg * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351547692-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351549342-05:00 f" {cat:<30} ${week_rev:>10,.0f} ${avg:>10,.0f}/day {pct:>+7.1f}%") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351550902-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351552442-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351553982-05:00 f" {cat:<30} ${week_rev:>10,.0f} (no baseline)") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351555532-05:00 category_baselines_str = "\n".join(_cb_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351557072-05:00 elif _cat_mix_rows: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351558602-05:00 _classified = sum((c.get('revenue') or 0) for c in _cat_mix_rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351560142-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351561672-05:00 f"Per-category inside revenue ({_period_lbl}) — USE THESE EXACT NUMBERS " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351563212-05:00 f"in the Section 3 category table. They are computed from the owner's " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351567232-05:00 f"merchandise-code taxonomy and already sum to inside sales " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351569212-05:00 f"(${_classified:,.0f}). Only the row explicitly labelled " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351570832-05:00 f"'Other / Unclassified' is unclassified — do NOT move any other " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351572382-05:00 f"category's revenue into 'Other', and do NOT invent different values.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351573922-05:00 f" {'Category':<30} {_period_lbl:>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351575542-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351577462-05:00 for c in sorted(_cat_mix_rows, key=lambda x: -(x.get('revenue') or 0)): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351579042-05:00 cat = str(c['category']) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351580602-05:00 rev = c.get('revenue') or 0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351582181-05:00 > avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351583731-05:00 ^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351585361-05:00 E AttributeError: 'NoneType' object has no attribute 'get' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351586861-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351588381-05:00 src/storeminder/agents/synthesis_agent.py:1426: AttributeError [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351590071-05:00 __________ ERROR at setup of test_weekly_headline_labeled_total_sales __________ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351591571-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351593131-05:00 @pytest.fixture(scope="module") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351594691-05:00 def weekly_report(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351596261-05:00 > return _run("weekly") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351597851-05:00 ^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351599311-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351600831-05:00 tests/test_report_traits_live.py:99: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351602441-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351604001-05:00 tests/test_report_traits_live.py:75: in _run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351605551-05:00 res = fn(store, end_date=d, dry_run=True, deliver=False, clean=True) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351607141-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351610661-05:00 src/storeminder/runner.py:359: in run_weekly [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351612271-05:00 return _run_report(params, clean=clean) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351613851-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351615391-05:00 src/storeminder/runner.py:193: in _run_report [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351617071-05:00 result = run_pipeline( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351618611-05:00 src/storeminder/crew.py:456: in run_pipeline [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351620131-05:00 report_result = _agent.run_weekly( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351621811-05:00 src/storeminder/agents/synthesis_agent.py:1028: in run_weekly [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351623371-05:00 return self.run(report_type="weekly", **kwargs) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351624991-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351626641-05:00 src/storeminder/agents/synthesis_agent.py:1073: in run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351628201-05:00 user = self._build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351629871-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351631361-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351632971-05:00 self = [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351634721-05:00 report_date = datetime.date(2026, 7, 10), store_id = 'Fastime' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351636381-05:00 trans = {'total_transactions': 618, 'open_hour_start': 4, 'open_hour_end': 21, 'report_date_filter': '2026-07-10', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351640641-05:00 scan = {'total_scan_rows': 1940, 'report_date_filter': '2026-07-10', 'total_sales': 6436.97, 'avg_item_price': 3.88, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351643301-05:00 baseline = {'day_of_week': 21, 'total_revenue': 100081.6, 'total_transactions': 3568.94, 'fuel_gallons': 16961.39, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351644931-05:00 flags = [{'metric': 'rank_drop', 'label': 'Rank Drop: COPENHAGEN FINE CUT', 'today': 40, 'baseline': 13, ...}, {'metric': 'ran...': 29, ...}, {'metric': 'rank_drop', 'label': 'Rank Drop: BUD LIGHT BEER 12 O', 'today': 33, 'baseline': 22, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351646521-05:00 fuel_section = '## 2. Fuel \n**2,583 gal | $9,446 | 279 fuel transactions** \nFuel volume came in at 2,583 gallons, down from 2,758...nfirm pump access and functionality during 22:00–23:00 if store is open | Measure: Fuel dispensed in evening gap hours' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351648691-05:00 inside_section = '## 3. Inside Sales\n\n**Inside Sales: $6,437 (+9.5% vs normal Tuesday)** \nTobacco / Nicotine surged on strong Marlb...EGG ↑14, ADD CHEESE ↑8) | Because: Modifier use rising but Plate Lunch down 60% | Measure: Attach rate per food ticket' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351650331-05:00 weather = {'yesterday': {'temp_max': 88.3, 'temp_min': 75.4, 'precip_in': 0.0, 'conditions': 'Clear'}, 'today_forecast': {'temp_...cloudy'}, 'rain_hours_today': [], 'weather_impact_assessment': 'Low — weather was mild, not a significant factor', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351652031-05:00 external_section = '## External Context\n\n**Google Reviews (5 reviews)** \nAverage rating of recent reviews (3.0/5) is below store’s ov...- Verify staff scheduling: confirm Luna’s shift coverage and retrain team on food handling consistency — Do by 3:00 PM' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351653591-05:00 loyalty_section = '## Loyalty & Promotions\n\n**Redemption Summary**\n| Promotion | Item | Today | Period Avg | Delta | Status |\n|-----...te and consider extending top performers like *Red Bull Original* and *Doritos Nacho Cheese* — evaluate by end of week' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351655231-05:00 ops_section = '## 5. Operations\n\n**Transactions & Throughput** \n962 total transactions | Avg ticket: $17.10 (+12% vs normal Frid... daily early fuel/retail volume.* \n\nNo additional hours recommended — optimize existing ownership and shift timing.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351656891-05:00 fuel_pricing_section = '## Fuel Pricing\n\n**Current Market** \n| Station | Regular | Midgrade | Premium | Diesel | Distance | \n|---------...y 8:00 AM tomorrow (2026-07-11). \n- Monitor Murphy USA and FASTIME for updates; both changed prices in last 3 hours.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351658461-05:00 prior_actions = None, report_type = 'weekly' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351660091-05:00 weekly_days = [{'date': '2026-07-04', 'dow_name': 'Saturday', 'total_revenue': 7392.12, 'fuel_gallons': 1142.1, ...}, {'date': '2026...42.4, ...}, {'date': '2026-07-09', 'dow_name': 'Thursday', 'total_revenue': 16227.6, 'fuel_gallons': 2780.6, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351661671-05:00 dow_profile = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351663231-05:00 kwargs = {'category_baselines': None, 'schedule_section': None, 'weekly_category_totals': None, 'ext_fetch_date': '2026-07-12', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351664731-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351666351-05:00 def _build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351667911-05:00 self, report_date, store_id, trans, scan, baseline, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351669471-05:00 flags, fuel_section, inside_section, weather, external_section, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351671031-05:00 loyalty_section, ops_section, fuel_pricing_section, prior_actions, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351672571-05:00 report_type: str = "daily", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351674111-05:00 weekly_days: Optional[List[Dict]] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351678331-05:00 dow_profile: Optional[Dict] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351679941-05:00 **kwargs, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351681511-05:00 ) -> str: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351683081-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351694141-05:00 # ── KPI table ───────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351697921-05:00 kpi_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351700481-05:00 "| Metric | Today | Baseline | Delta | Priority |", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351702711-05:00 "|--------|-------|----------|-------|----------|", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351704761-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351706761-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351708801-05:00 # Keys that live in scan_summary (inside sales) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351710881-05:00 _scan_keys = {'total_sales', 'avg_item_price', 'total_scan_rows'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351716261-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351718431-05:00 def kpi_row(label, key, fmt_str=",.0f", prefix="", priority=None): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351720421-05:00 # Prefer scan for inside-sales metrics, trans for everything else [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351723531-05:00 if key in _scan_keys: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351725621-05:00 t = scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351727631-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351729791-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351731871-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351734041-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351736031-05:00 return [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351738031-05:00 t_str = f"{prefix}{t:{fmt_str}}" if isinstance(t, (int, float)) else str(t) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351740081-05:00 b_str = f"{prefix}{b:{fmt_str}}" if isinstance(b, (int, float)) else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351743751-05:00 delta = f"{((t-b)/b*100):+.1f}%" if isinstance(t,(int,float)) and isinstance(b,(int,float)) and b else "—" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351745861-05:00 # Priority: use flag severity if exists, else spec default [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351747851-05:00 flag = next((f for f in flags if f['metric'] == key), None) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351749851-05:00 pri = flag['severity'] if flag else (priority or "—") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351751841-05:00 kpi_lines.append(f"| {label} | {t_str} | {b_str} | {delta} | {pri} |") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351753801-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351755851-05:00 kpi_row("Total Transactions", "total_transactions", ",.0f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351758001-05:00 kpi_row("Total Sales", "total_revenue", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351760021-05:00 kpi_row("Inside Sales", "total_sales", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351762021-05:00 kpi_row("Avg Inside Ticket", "avg_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351764051-05:00 kpi_row("Avg Fuel Ticket", "avg_fuel_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351766051-05:00 kpi_row("Fuel Gallons", "fuel_gallons", ",.1f", "", "HIGH") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351768111-05:00 kpi_row("Fuel Sales $", "fuel_sales", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351770591-05:00 kpi_row("Fuel Transactions", "fuel_transactions", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351779551-05:00 kpi_row("Avg Gallons/Tx", "avg_gallons_per_tx", ",.2f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351781721-05:00 kpi_row("Forecourt Conv %", "forecourt_conversion_pct", ",.1f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351783761-05:00 kpi_row("Void Count", "void_count", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351785791-05:00 kpi_row("Void Total $", "void_total_amt", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351787851-05:00 kpi_row("Inside/100 Gal (weekly avg)", "inside_per_100_gal", ",.0f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351789881-05:00 kpi_row("Eff. Fuel Price", "effective_fuel_price", ",.3f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351791981-05:00 kpi_row("Inside Share %", "inside_sales_share_pct", ",.1f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351794011-05:00 kpi_summary = "\n".join(kpi_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351795971-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351798251-05:00 # ── Derived metrics for snapshot header ──────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351800391-05:00 def _dm(key, label, prefix="", suffix="", decimals=2): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351802551-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351804551-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351806501-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351808451-05:00 return None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351810491-05:00 t_str = f"{prefix}{float(t):,.{decimals}f}{suffix}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351812541-05:00 if b and float(b) > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351814511-05:00 pct = (float(t) - float(b)) / float(b) * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351817191-05:00 return f"{label}: {t_str} ({pct:+.1f}% vs baseline)" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351819151-05:00 return f"{label}: {t_str}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351821101-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351823111-05:00 dm_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351825171-05:00 v = _dm('inside_per_100_gal', 'Inside/100gal', '$', '', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351827271-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351829321-05:00 v = _dm('effective_fuel_price', 'Effective fuel price', '$', '/gal', 3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351831341-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351833361-05:00 v = _dm('inside_sales_share_pct','Inside share of sales', '', '%', 1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351835321-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351837301-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351839421-05:00 gap = trans.get('fuel_inside_gap_pct') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351841391-05:00 if gap is not None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351843441-05:00 direction = "FUEL OUTPACING INSIDE" if gap > 0 else "INSIDE OUTPACING FUEL" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351845471-05:00 dm_lines.append(f"Fuel-vs-Inside gap: {gap:+.1f}pp ({direction})") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351847451-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351849661-05:00 derived_metrics = "\n".join(dm_lines) if dm_lines else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351851660-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351853910-05:00 # ── Flags summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351856360-05:00 if flags: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351858810-05:00 flags_str = "\n".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351863240-05:00 f"- [{f['severity']}] {f['label']}: {f['note']}" for f in flags) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351870080-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351872170-05:00 flags_str = "No significant anomalies detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351874260-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351876340-05:00 # ── Shift summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351878410-05:00 shifts = trans.get('shift_summary', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351880540-05:00 if shifts: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351882520-05:00 shift_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351884580-05:00 for name, data in shifts.items(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351886540-05:00 rev = data.get('revenue', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351888510-05:00 tx = data.get('transactions', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351890510-05:00 gals = data.get('fuel_gallons', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351892660-05:00 shift_lines.append(f"- {name}: ${rev:,.2f} inside | {tx} tx | {gals:,.1f} gal fuel") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351894630-05:00 shift_summary = "\n".join(shift_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351896650-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351898690-05:00 shift_summary = "Shift data not available." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351900700-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351902710-05:00 # ── Gap / zero-tx hours ─────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351905140-05:00 gaps = trans.get('top_hourly_gaps', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351907650-05:00 zero_tx = trans.get('zero_tx_hours', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351909650-05:00 gap_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351911660-05:00 if gaps: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351913730-05:00 gap_str = ", ".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351915810-05:00 "{:02d}:00 ({} tx)".format(g['_hour'], g['tx_count']) for g in gaps) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351923440-05:00 gap_lines.append(f"Top gap hours: {gap_str}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351927840-05:00 if zero_tx: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351929880-05:00 gap_lines.append(f"Zero-tx hours (open window): {zero_tx}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351931930-05:00 gap_summary = "\n".join(gap_lines) if gap_lines else "No unusual gaps detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351933920-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351935960-05:00 # Authoritative store hours (issue #128): when Google Places supplies the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351937990-05:00 # store's real hours, prepend them and instruct the model to treat [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351940000-05:00 # off-hours zero activity as expected closure rather than hedging. The [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351942330-05:00 # gap/zero-tx figures above are already computed against this same window. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351944360-05:00 store_hours_val = kwargs.get('store_hours') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351946370-05:00 if store_hours_val and store_hours_val.get('weekday_text'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351948410-05:00 hrs_lines = ["AUTHORITATIVE STORE HOURS (Google Places — treat as ground truth):"] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351950430-05:00 hrs_lines += [f" {ln}" for ln in store_hours_val['weekday_text']] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351952430-05:00 if store_hours_val.get('open_24h'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351954370-05:00 hrs_lines.append(" This store operates 24 hours on at least one day.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351956320-05:00 hrs_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351958300-05:00 "Zero-transaction hours OUTSIDE these posted hours are EXPECTED " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351963360-05:00 "CLOSURE — do NOT flag them as gaps and do NOT hedge about whether " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351965310-05:00 "the store was open. Treat zero/low activity DURING posted open " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351967240-05:00 "hours as a real gap worth investigating.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351969150-05:00 gap_summary = "\n".join(hrs_lines) + "\n\n" + gap_summary [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351971090-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351973140-05:00 # ── Weather ─────────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351975110-05:00 if weather: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351977160-05:00 temp = weather.get('temp', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351979170-05:00 precip = weather.get('precipitation_in', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351981130-05:00 conds = list(weather.get('conditions', {}).keys()) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351983650-05:00 impact = weather.get('weather_impact_assessment', '') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351985730-05:00 yd = weather.get('yesterday', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351987760-05:00 td = weather.get('today_forecast', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351989850-05:00 rain_hrs= weather.get('rain_hours_today', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351991830-05:00 weather_str = ( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351993880-05:00 f"Report day: {yd.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351995890-05:00 f"{yd.get('temp_min','?')}–{yd.get('temp_max','?')}°F | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.351997820-05:00 f"{yd.get('precip_in',0)}\" precip\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352001470-05:00 f"Today forecast: {td.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352003600-05:00 f"{td.get('temp_min','?')}–{td.get('temp_max','?')}°F\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352005620-05:00 f"Impact assessment: {impact}\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352008160-05:00 + (f"Rain hours: {rain_hrs}\n" if rain_hrs else "") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352010170-05:00 ) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352012110-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352014070-05:00 weather_str = "No weather data provided." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352016010-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352018080-05:00 # ── Schedule section ────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352022700-05:00 schedule_section = kwargs.get('schedule_section', None) or '' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352024770-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352026820-05:00 # ── Category baselines ──────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352028800-05:00 cat_baselines = kwargs.get('category_baselines', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352030860-05:00 # weekly_category_totals: actual accumulated weekly revenue per category [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352032950-05:00 # passed from Cell 39 _week_cat — USE THESE VERBATIM in the report table [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352034960-05:00 weekly_cat_totals = kwargs.get('weekly_category_totals', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352036930-05:00 _is_weekly = (report_type == 'weekly') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352039370-05:00 # Authoritative per-category revenue comes from the owner-mapped [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352041800-05:00 # category_mix (preprocessor). Post-mapping it is aggregated by taxonomy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352047580-05:00 # name and already reconciles to inside sales, so it — NOT the seeded [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352049680-05:00 # baselines — must drive the category table. Previously this block only [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352051710-05:00 # emitted categories that had a seeded history baseline; when baselines [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352053700-05:00 # were sparse or unseeded the model received no authoritative totals and [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352055710-05:00 # dumped the bulk of inside sales into "Other / unclassified" (issue #81). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352057770-05:00 _cat_mix_rows = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352059780-05:00 c for c in scan.get('category_mix', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352061760-05:00 if isinstance(c.get('category'), str) and str(c.get('category')).strip() [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352063750-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352065770-05:00 _period_lbl = 'This Week' if _is_weekly else 'Today' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352067790-05:00 if _is_weekly and weekly_cat_totals: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352069790-05:00 # Legacy notebook path passes pre-accumulated weekly totals verbatim. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352071760-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352073820-05:00 "WEEKLY Category Totals — USE THESE EXACT NUMBERS in Section 3 table.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352075910-05:00 "DO NOT invent or estimate different values. These are computed from actual data.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352077950-05:00 f" {'Category':<30} {'This Week':>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352079930-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352081950-05:00 for cat, week_rev in sorted(weekly_cat_totals.items(), [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352083990-05:00 key=lambda x: -x[1]): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352086530-05:00 if not isinstance(cat, str) or not cat.strip(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352089500-05:00 continue [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352092530-05:00 avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352095370-05:00 if avg > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352098300-05:00 pct = (week_rev - avg) / avg * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352101210-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352104160-05:00 f" {cat:<30} ${week_rev:>10,.0f} ${avg:>10,.0f}/day {pct:>+7.1f}%") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352107020-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352109700-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352112780-05:00 f" {cat:<30} ${week_rev:>10,.0f} (no baseline)") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352115980-05:00 category_baselines_str = "\n".join(_cb_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352119150-05:00 elif _cat_mix_rows: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352122500-05:00 _classified = sum((c.get('revenue') or 0) for c in _cat_mix_rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352125690-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352128910-05:00 f"Per-category inside revenue ({_period_lbl}) — USE THESE EXACT NUMBERS " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352132589-05:00 f"in the Section 3 category table. They are computed from the owner's " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352135769-05:00 f"merchandise-code taxonomy and already sum to inside sales " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352139579-05:00 f"(${_classified:,.0f}). Only the row explicitly labelled " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352142779-05:00 f"'Other / Unclassified' is unclassified — do NOT move any other " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352144849-05:00 f"category's revenue into 'Other', and do NOT invent different values.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352150679-05:00 f" {'Category':<30} {_period_lbl:>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352152699-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352154829-05:00 for c in sorted(_cat_mix_rows, key=lambda x: -(x.get('revenue') or 0)): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352156969-05:00 cat = str(c['category']) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352158979-05:00 rev = c.get('revenue') or 0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352161109-05:00 > avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352163219-05:00 ^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352165299-05:00 E AttributeError: 'NoneType' object has no attribute 'get' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352167229-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352169439-05:00 src/storeminder/agents/synthesis_agent.py:1426: AttributeError [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352171969-05:00 _______________ ERROR at setup of test_weekly_sms_not_truncated ________________ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352173869-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352175789-05:00 @pytest.fixture(scope="module") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352177969-05:00 def weekly_report(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352179919-05:00 > return _run("weekly") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352181869-05:00 ^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352183729-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352185999-05:00 tests/test_report_traits_live.py:99: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352188119-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352191319-05:00 tests/test_report_traits_live.py:75: in _run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352194429-05:00 res = fn(store, end_date=d, dry_run=True, deliver=False, clean=True) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352201899-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352204949-05:00 src/storeminder/runner.py:359: in run_weekly [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352207649-05:00 return _run_report(params, clean=clean) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352209679-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352211849-05:00 src/storeminder/runner.py:193: in _run_report [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352213869-05:00 result = run_pipeline( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352216639-05:00 src/storeminder/crew.py:456: in run_pipeline [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352218569-05:00 report_result = _agent.run_weekly( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352220599-05:00 src/storeminder/agents/synthesis_agent.py:1028: in run_weekly [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352222749-05:00 return self.run(report_type="weekly", **kwargs) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352224909-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352226939-05:00 src/storeminder/agents/synthesis_agent.py:1073: in run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352230019-05:00 user = self._build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352233079-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352236139-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352239259-05:00 self = [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352243159-05:00 report_date = datetime.date(2026, 7, 10), store_id = 'Fastime' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352246489-05:00 trans = {'total_transactions': 618, 'open_hour_start': 4, 'open_hour_end': 21, 'report_date_filter': '2026-07-10', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352250119-05:00 scan = {'total_scan_rows': 1940, 'report_date_filter': '2026-07-10', 'total_sales': 6436.97, 'avg_item_price': 3.88, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352253299-05:00 baseline = {'day_of_week': 21, 'total_revenue': 100081.6, 'total_transactions': 3568.94, 'fuel_gallons': 16961.39, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352256819-05:00 flags = [{'metric': 'rank_drop', 'label': 'Rank Drop: COPENHAGEN FINE CUT', 'today': 40, 'baseline': 13, ...}, {'metric': 'ran...': 29, ...}, {'metric': 'rank_drop', 'label': 'Rank Drop: BUD LIGHT BEER 12 O', 'today': 33, 'baseline': 22, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352268379-05:00 fuel_section = '## 2. Fuel \n**2,583 gal | $9,446 | 279 fuel transactions** \nFuel volume came in at 2,583 gallons, down from 2,758...nfirm pump access and functionality during 22:00–23:00 if store is open | Measure: Fuel dispensed in evening gap hours' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352270749-05:00 inside_section = '## 3. Inside Sales\n\n**Inside Sales: $6,437 (+9.5% vs normal Tuesday)** \nTobacco / Nicotine surged on strong Marlb...EGG ↑14, ADD CHEESE ↑8) | Because: Modifier use rising but Plate Lunch down 60% | Measure: Attach rate per food ticket' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352273179-05:00 weather = {'yesterday': {'temp_max': 88.3, 'temp_min': 75.4, 'precip_in': 0.0, 'conditions': 'Clear'}, 'today_forecast': {'temp_...cloudy'}, 'rain_hours_today': [], 'weather_impact_assessment': 'Low — weather was mild, not a significant factor', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352275339-05:00 external_section = '## External Context\n\n**Google Reviews (5 reviews)** \nAverage rating of recent reviews (3.0/5) is below store’s ov...- Verify staff scheduling: confirm Luna’s shift coverage and retrain team on food handling consistency — Do by 3:00 PM' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352277509-05:00 loyalty_section = '## Loyalty & Promotions\n\n**Redemption Summary**\n| Promotion | Item | Today | Period Avg | Delta | Status |\n|-----...te and consider extending top performers like *Red Bull Original* and *Doritos Nacho Cheese* — evaluate by end of week' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352279579-05:00 ops_section = '## 5. Operations\n\n**Transactions & Throughput** \n962 total transactions | Avg ticket: $17.10 (+12% vs normal Frid... daily early fuel/retail volume.* \n\nNo additional hours recommended — optimize existing ownership and shift timing.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352282039-05:00 fuel_pricing_section = '## Fuel Pricing\n\n**Current Market** \n| Station | Regular | Midgrade | Premium | Diesel | Distance | \n|---------...y 8:00 AM tomorrow (2026-07-11). \n- Monitor Murphy USA and FASTIME for updates; both changed prices in last 3 hours.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352284039-05:00 prior_actions = None, report_type = 'weekly' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352286379-05:00 weekly_days = [{'date': '2026-07-04', 'dow_name': 'Saturday', 'total_revenue': 7392.12, 'fuel_gallons': 1142.1, ...}, {'date': '2026...42.4, ...}, {'date': '2026-07-09', 'dow_name': 'Thursday', 'total_revenue': 16227.6, 'fuel_gallons': 2780.6, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352288399-05:00 dow_profile = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352291029-05:00 kwargs = {'category_baselines': None, 'schedule_section': None, 'weekly_category_totals': None, 'ext_fetch_date': '2026-07-12', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352292929-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352295289-05:00 def _build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352297519-05:00 self, report_date, store_id, trans, scan, baseline, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352299739-05:00 flags, fuel_section, inside_section, weather, external_section, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352301799-05:00 loyalty_section, ops_section, fuel_pricing_section, prior_actions, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352303859-05:00 report_type: str = "daily", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352306359-05:00 weekly_days: Optional[List[Dict]] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352312259-05:00 dow_profile: Optional[Dict] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352314539-05:00 **kwargs, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352316549-05:00 ) -> str: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352318609-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352320819-05:00 # ── KPI table ───────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352327049-05:00 kpi_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352329819-05:00 "| Metric | Today | Baseline | Delta | Priority |", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352331889-05:00 "|--------|-------|----------|-------|----------|", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352333889-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352335899-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352337989-05:00 # Keys that live in scan_summary (inside sales) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352340069-05:00 _scan_keys = {'total_sales', 'avg_item_price', 'total_scan_rows'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352342089-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352344209-05:00 def kpi_row(label, key, fmt_str=",.0f", prefix="", priority=None): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352346319-05:00 # Prefer scan for inside-sales metrics, trans for everything else [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352348349-05:00 if key in _scan_keys: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352350369-05:00 t = scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352352399-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352354429-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352356489-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352358479-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352360489-05:00 return [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352362979-05:00 t_str = f"{prefix}{t:{fmt_str}}" if isinstance(t, (int, float)) else str(t) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352365029-05:00 b_str = f"{prefix}{b:{fmt_str}}" if isinstance(b, (int, float)) else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352367119-05:00 delta = f"{((t-b)/b*100):+.1f}%" if isinstance(t,(int,float)) and isinstance(b,(int,float)) and b else "—" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352369139-05:00 # Priority: use flag severity if exists, else spec default [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352371159-05:00 flag = next((f for f in flags if f['metric'] == key), None) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352373179-05:00 pri = flag['severity'] if flag else (priority or "—") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352375779-05:00 kpi_lines.append(f"| {label} | {t_str} | {b_str} | {delta} | {pri} |") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352377839-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352379899-05:00 kpi_row("Total Transactions", "total_transactions", ",.0f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352381959-05:00 kpi_row("Total Sales", "total_revenue", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352384419-05:00 kpi_row("Inside Sales", "total_sales", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352386419-05:00 kpi_row("Avg Inside Ticket", "avg_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352388429-05:00 kpi_row("Avg Fuel Ticket", "avg_fuel_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352390489-05:00 kpi_row("Fuel Gallons", "fuel_gallons", ",.1f", "", "HIGH") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352392499-05:00 kpi_row("Fuel Sales $", "fuel_sales", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352394569-05:00 kpi_row("Fuel Transactions", "fuel_transactions", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352396619-05:00 kpi_row("Avg Gallons/Tx", "avg_gallons_per_tx", ",.2f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352398659-05:00 kpi_row("Forecourt Conv %", "forecourt_conversion_pct", ",.1f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352400729-05:00 kpi_row("Void Count", "void_count", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352402809-05:00 kpi_row("Void Total $", "void_total_amt", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352407779-05:00 kpi_row("Inside/100 Gal (weekly avg)", "inside_per_100_gal", ",.0f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352409839-05:00 kpi_row("Eff. Fuel Price", "effective_fuel_price", ",.3f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352411839-05:00 kpi_row("Inside Share %", "inside_sales_share_pct", ",.1f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352413919-05:00 kpi_summary = "\n".join(kpi_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352415899-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352418068-05:00 # ── Derived metrics for snapshot header ──────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352420158-05:00 def _dm(key, label, prefix="", suffix="", decimals=2): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352422228-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352424288-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352426358-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352428338-05:00 return None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352430448-05:00 t_str = f"{prefix}{float(t):,.{decimals}f}{suffix}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352432428-05:00 if b and float(b) > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352434458-05:00 pct = (float(t) - float(b)) / float(b) * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352437058-05:00 return f"{label}: {t_str} ({pct:+.1f}% vs baseline)" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352439038-05:00 return f"{label}: {t_str}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352441068-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352443198-05:00 dm_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352445708-05:00 v = _dm('inside_per_100_gal', 'Inside/100gal', '$', '', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352447828-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352449898-05:00 v = _dm('effective_fuel_price', 'Effective fuel price', '$', '/gal', 3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352451898-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352454778-05:00 v = _dm('inside_sales_share_pct','Inside share of sales', '', '%', 1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352456768-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352458798-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352463208-05:00 gap = trans.get('fuel_inside_gap_pct') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352465268-05:00 if gap is not None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352468188-05:00 direction = "FUEL OUTPACING INSIDE" if gap > 0 else "INSIDE OUTPACING FUEL" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352470288-05:00 dm_lines.append(f"Fuel-vs-Inside gap: {gap:+.1f}pp ({direction})") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352472288-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352474398-05:00 derived_metrics = "\n".join(dm_lines) if dm_lines else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352476378-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352478518-05:00 # ── Flags summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352480508-05:00 if flags: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352482508-05:00 flags_str = "\n".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352484538-05:00 f"- [{f['severity']}] {f['label']}: {f['note']}" for f in flags) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352486508-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352488568-05:00 flags_str = "No significant anomalies detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352490648-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352492708-05:00 # ── Shift summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352497788-05:00 shifts = trans.get('shift_summary', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352499828-05:00 if shifts: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352501908-05:00 shift_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352503968-05:00 for name, data in shifts.items(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352505948-05:00 rev = data.get('revenue', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352507998-05:00 tx = data.get('transactions', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352510048-05:00 gals = data.get('fuel_gallons', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352512098-05:00 shift_lines.append(f"- {name}: ${rev:,.2f} inside | {tx} tx | {gals:,.1f} gal fuel") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352514168-05:00 shift_summary = "\n".join(shift_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352516168-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352518848-05:00 shift_summary = "Shift data not available." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352520888-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352522978-05:00 # ── Gap / zero-tx hours ─────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352525058-05:00 gaps = trans.get('top_hourly_gaps', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352527108-05:00 zero_tx = trans.get('zero_tx_hours', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352529098-05:00 gap_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352531158-05:00 if gaps: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352533168-05:00 gap_str = ", ".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352535188-05:00 "{:02d}:00 ({} tx)".format(g['_hour'], g['tx_count']) for g in gaps) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352537298-05:00 gap_lines.append(f"Top gap hours: {gap_str}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352539258-05:00 if zero_tx: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352541308-05:00 gap_lines.append(f"Zero-tx hours (open window): {zero_tx}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352543418-05:00 gap_summary = "\n".join(gap_lines) if gap_lines else "No unusual gaps detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352545398-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352548088-05:00 # Authoritative store hours (issue #128): when Google Places supplies the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352550198-05:00 # store's real hours, prepend them and instruct the model to treat [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352552278-05:00 # off-hours zero activity as expected closure rather than hedging. The [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352554898-05:00 # gap/zero-tx figures above are already computed against this same window. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352556868-05:00 store_hours_val = kwargs.get('store_hours') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352558918-05:00 if store_hours_val and store_hours_val.get('weekday_text'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352561788-05:00 hrs_lines = ["AUTHORITATIVE STORE HOURS (Google Places — treat as ground truth):"] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352563808-05:00 hrs_lines += [f" {ln}" for ln in store_hours_val['weekday_text']] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352565888-05:00 if store_hours_val.get('open_24h'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352567898-05:00 hrs_lines.append(" This store operates 24 hours on at least one day.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352569948-05:00 hrs_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352572068-05:00 "Zero-transaction hours OUTSIDE these posted hours are EXPECTED " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352574078-05:00 "CLOSURE — do NOT flag them as gaps and do NOT hedge about whether " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352576078-05:00 "the store was open. Treat zero/low activity DURING posted open " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352578128-05:00 "hours as a real gap worth investigating.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352580138-05:00 gap_summary = "\n".join(hrs_lines) + "\n\n" + gap_summary [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352587858-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352589988-05:00 # ── Weather ─────────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352592018-05:00 if weather: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352594128-05:00 temp = weather.get('temp', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352596148-05:00 precip = weather.get('precipitation_in', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352598178-05:00 conds = list(weather.get('conditions', {}).keys()) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352600278-05:00 impact = weather.get('weather_impact_assessment', '') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352602328-05:00 yd = weather.get('yesterday', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352604318-05:00 td = weather.get('today_forecast', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352606348-05:00 rain_hrs= weather.get('rain_hours_today', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352608368-05:00 weather_str = ( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352610418-05:00 f"Report day: {yd.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352612468-05:00 f"{yd.get('temp_min','?')}–{yd.get('temp_max','?')}°F | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352616718-05:00 f"{yd.get('precip_in',0)}\" precip\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352618898-05:00 f"Today forecast: {td.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352621098-05:00 f"{td.get('temp_min','?')}–{td.get('temp_max','?')}°F\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352623148-05:00 f"Impact assessment: {impact}\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352628088-05:00 + (f"Rain hours: {rain_hrs}\n" if rain_hrs else "") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352630118-05:00 ) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352632098-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352634118-05:00 weather_str = "No weather data provided." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352636168-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352638418-05:00 # ── Schedule section ────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352640528-05:00 schedule_section = kwargs.get('schedule_section', None) or '' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352642648-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352644748-05:00 # ── Category baselines ──────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352646768-05:00 cat_baselines = kwargs.get('category_baselines', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352649548-05:00 # weekly_category_totals: actual accumulated weekly revenue per category [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352651678-05:00 # passed from Cell 39 _week_cat — USE THESE VERBATIM in the report table [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352653788-05:00 weekly_cat_totals = kwargs.get('weekly_category_totals', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352655788-05:00 _is_weekly = (report_type == 'weekly') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352657938-05:00 # Authoritative per-category revenue comes from the owner-mapped [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352660058-05:00 # category_mix (preprocessor). Post-mapping it is aggregated by taxonomy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352662128-05:00 # name and already reconciles to inside sales, so it — NOT the seeded [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352664168-05:00 # baselines — must drive the category table. Previously this block only [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352666198-05:00 # emitted categories that had a seeded history baseline; when baselines [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352671668-05:00 # were sparse or unseeded the model received no authoritative totals and [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352674518-05:00 # dumped the bulk of inside sales into "Other / unclassified" (issue #81). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352677618-05:00 _cat_mix_rows = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352681078-05:00 c for c in scan.get('category_mix', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352684378-05:00 if isinstance(c.get('category'), str) and str(c.get('category')).strip() [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352687788-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352690858-05:00 _period_lbl = 'This Week' if _is_weekly else 'Today' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352693848-05:00 if _is_weekly and weekly_cat_totals: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352696938-05:00 # Legacy notebook path passes pre-accumulated weekly totals verbatim. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352699858-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352702868-05:00 "WEEKLY Category Totals — USE THESE EXACT NUMBERS in Section 3 table.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352706767-05:00 "DO NOT invent or estimate different values. These are computed from actual data.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352709777-05:00 f" {'Category':<30} {'This Week':>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352712707-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352716457-05:00 for cat, week_rev in sorted(weekly_cat_totals.items(), [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352719367-05:00 key=lambda x: -x[1]): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352722417-05:00 if not isinstance(cat, str) or not cat.strip(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352725317-05:00 continue [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352728557-05:00 avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352731857-05:00 if avg > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352735857-05:00 pct = (week_rev - avg) / avg * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352738867-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352742057-05:00 f" {cat:<30} ${week_rev:>10,.0f} ${avg:>10,.0f}/day {pct:>+7.1f}%") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352745067-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352747847-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352750627-05:00 f" {cat:<30} ${week_rev:>10,.0f} (no baseline)") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352753757-05:00 category_baselines_str = "\n".join(_cb_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352756417-05:00 elif _cat_mix_rows: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352759127-05:00 _classified = sum((c.get('revenue') or 0) for c in _cat_mix_rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352762227-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352765297-05:00 f"Per-category inside revenue ({_period_lbl}) — USE THESE EXACT NUMBERS " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352768327-05:00 f"in the Section 3 category table. They are computed from the owner's " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352771537-05:00 f"merchandise-code taxonomy and already sum to inside sales " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352774327-05:00 f"(${_classified:,.0f}). Only the row explicitly labelled " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352777587-05:00 f"'Other / Unclassified' is unclassified — do NOT move any other " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352780617-05:00 f"category's revenue into 'Other', and do NOT invent different values.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352783967-05:00 f" {'Category':<30} {_period_lbl:>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352787127-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352790237-05:00 for c in sorted(_cat_mix_rows, key=lambda x: -(x.get('revenue') or 0)): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352798967-05:00 cat = str(c['category']) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352802437-05:00 rev = c.get('revenue') or 0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352805707-05:00 > avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352814647-05:00 ^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352818017-05:00 E AttributeError: 'NoneType' object has no attribute 'get' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352821207-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352824427-05:00 src/storeminder/agents/synthesis_agent.py:1426: AttributeError [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352828107-05:00 ____________ ERROR at setup of test_weekly_artifact_bundle_written _____________ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352830897-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352833737-05:00 @pytest.fixture(scope="module") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352836587-05:00 def weekly_report(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352840587-05:00 > return _run("weekly") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352844117-05:00 ^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352846047-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352848077-05:00 tests/test_report_traits_live.py:99: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352850147-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352852187-05:00 tests/test_report_traits_live.py:75: in _run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352854407-05:00 res = fn(store, end_date=d, dry_run=True, deliver=False, clean=True) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352856587-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352858577-05:00 src/storeminder/runner.py:359: in run_weekly [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352860607-05:00 return _run_report(params, clean=clean) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352862637-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352864627-05:00 src/storeminder/runner.py:193: in _run_report [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352866697-05:00 result = run_pipeline( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352868787-05:00 src/storeminder/crew.py:456: in run_pipeline [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352870867-05:00 report_result = _agent.run_weekly( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352872897-05:00 src/storeminder/agents/synthesis_agent.py:1028: in run_weekly [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352874987-05:00 return self.run(report_type="weekly", **kwargs) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352877067-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352879137-05:00 src/storeminder/agents/synthesis_agent.py:1073: in run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352881707-05:00 user = self._build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352884267-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352886287-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352888367-05:00 self = [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352890487-05:00 report_date = datetime.date(2026, 7, 10), store_id = 'Fastime' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352892717-05:00 trans = {'total_transactions': 618, 'open_hour_start': 4, 'open_hour_end': 21, 'report_date_filter': '2026-07-10', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352894837-05:00 scan = {'total_scan_rows': 1940, 'report_date_filter': '2026-07-10', 'total_sales': 6436.97, 'avg_item_price': 3.88, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352896937-05:00 baseline = {'day_of_week': 21, 'total_revenue': 100081.6, 'total_transactions': 3568.94, 'fuel_gallons': 16961.39, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352899107-05:00 flags = [{'metric': 'rank_drop', 'label': 'Rank Drop: COPENHAGEN FINE CUT', 'today': 40, 'baseline': 13, ...}, {'metric': 'ran...': 29, ...}, {'metric': 'rank_drop', 'label': 'Rank Drop: BUD LIGHT BEER 12 O', 'today': 33, 'baseline': 22, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352901717-05:00 fuel_section = '## 2. Fuel \n**2,583 gal | $9,446 | 279 fuel transactions** \nFuel volume came in at 2,583 gallons, down from 2,758...nfirm pump access and functionality during 22:00–23:00 if store is open | Measure: Fuel dispensed in evening gap hours' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352908327-05:00 inside_section = '## 3. Inside Sales\n\n**Inside Sales: $6,437 (+9.5% vs normal Tuesday)** \nTobacco / Nicotine surged on strong Marlb...EGG ↑14, ADD CHEESE ↑8) | Because: Modifier use rising but Plate Lunch down 60% | Measure: Attach rate per food ticket' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352910497-05:00 weather = {'yesterday': {'temp_max': 88.3, 'temp_min': 75.4, 'precip_in': 0.0, 'conditions': 'Clear'}, 'today_forecast': {'temp_...cloudy'}, 'rain_hours_today': [], 'weather_impact_assessment': 'Low — weather was mild, not a significant factor', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352912767-05:00 external_section = '## External Context\n\n**Google Reviews (5 reviews)** \nAverage rating of recent reviews (3.0/5) is below store’s ov...- Verify staff scheduling: confirm Luna’s shift coverage and retrain team on food handling consistency — Do by 3:00 PM' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352915667-05:00 loyalty_section = '## Loyalty & Promotions\n\n**Redemption Summary**\n| Promotion | Item | Today | Period Avg | Delta | Status |\n|-----...te and consider extending top performers like *Red Bull Original* and *Doritos Nacho Cheese* — evaluate by end of week' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352939397-05:00 ops_section = '## 5. Operations\n\n**Transactions & Throughput** \n962 total transactions | Avg ticket: $17.10 (+12% vs normal Frid... daily early fuel/retail volume.* \n\nNo additional hours recommended — optimize existing ownership and shift timing.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352945317-05:00 fuel_pricing_section = '## Fuel Pricing\n\n**Current Market** \n| Station | Regular | Midgrade | Premium | Diesel | Distance | \n|---------...y 8:00 AM tomorrow (2026-07-11). \n- Monitor Murphy USA and FASTIME for updates; both changed prices in last 3 hours.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352947327-05:00 prior_actions = None, report_type = 'weekly' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352963567-05:00 weekly_days = [{'date': '2026-07-04', 'dow_name': 'Saturday', 'total_revenue': 7392.12, 'fuel_gallons': 1142.1, ...}, {'date': '2026...42.4, ...}, {'date': '2026-07-09', 'dow_name': 'Thursday', 'total_revenue': 16227.6, 'fuel_gallons': 2780.6, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352969257-05:00 dow_profile = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352978067-05:00 kwargs = {'category_baselines': None, 'schedule_section': None, 'weekly_category_totals': None, 'ext_fetch_date': '2026-07-12', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352980517-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352983167-05:00 def _build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352985787-05:00 self, report_date, store_id, trans, scan, baseline, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352988806-05:00 flags, fuel_section, inside_section, weather, external_section, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352991476-05:00 loyalty_section, ops_section, fuel_pricing_section, prior_actions, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352994026-05:00 report_type: str = "daily", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352996326-05:00 weekly_days: Optional[List[Dict]] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.352998636-05:00 dow_profile: Optional[Dict] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353000916-05:00 **kwargs, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353003596-05:00 ) -> str: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353006356-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353008756-05:00 # ── KPI table ───────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353012546-05:00 kpi_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353015116-05:00 "| Metric | Today | Baseline | Delta | Priority |", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353017456-05:00 "|--------|-------|----------|-------|----------|", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353019716-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353031046-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353033626-05:00 # Keys that live in scan_summary (inside sales) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353036156-05:00 _scan_keys = {'total_sales', 'avg_item_price', 'total_scan_rows'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353038566-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353041236-05:00 def kpi_row(label, key, fmt_str=",.0f", prefix="", priority=None): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353043816-05:00 # Prefer scan for inside-sales metrics, trans for everything else [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353046326-05:00 if key in _scan_keys: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353048686-05:00 t = scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353050986-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353053496-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353055976-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353058516-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353060906-05:00 return [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353063386-05:00 t_str = f"{prefix}{t:{fmt_str}}" if isinstance(t, (int, float)) else str(t) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353066406-05:00 b_str = f"{prefix}{b:{fmt_str}}" if isinstance(b, (int, float)) else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353069476-05:00 delta = f"{((t-b)/b*100):+.1f}%" if isinstance(t,(int,float)) and isinstance(b,(int,float)) and b else "—" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353071826-05:00 # Priority: use flag severity if exists, else spec default [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353074326-05:00 flag = next((f for f in flags if f['metric'] == key), None) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353076596-05:00 pri = flag['severity'] if flag else (priority or "—") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353079936-05:00 kpi_lines.append(f"| {label} | {t_str} | {b_str} | {delta} | {pri} |") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353082356-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353085006-05:00 kpi_row("Total Transactions", "total_transactions", ",.0f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353087616-05:00 kpi_row("Total Sales", "total_revenue", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353090216-05:00 kpi_row("Inside Sales", "total_sales", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353092706-05:00 kpi_row("Avg Inside Ticket", "avg_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353095056-05:00 kpi_row("Avg Fuel Ticket", "avg_fuel_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353097476-05:00 kpi_row("Fuel Gallons", "fuel_gallons", ",.1f", "", "HIGH") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353099846-05:00 kpi_row("Fuel Sales $", "fuel_sales", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353102156-05:00 kpi_row("Fuel Transactions", "fuel_transactions", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353104626-05:00 kpi_row("Avg Gallons/Tx", "avg_gallons_per_tx", ",.2f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353107686-05:00 kpi_row("Forecourt Conv %", "forecourt_conversion_pct", ",.1f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353110026-05:00 kpi_row("Void Count", "void_count", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353112376-05:00 kpi_row("Void Total $", "void_total_amt", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353115336-05:00 kpi_row("Inside/100 Gal (weekly avg)", "inside_per_100_gal", ",.0f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353117896-05:00 kpi_row("Eff. Fuel Price", "effective_fuel_price", ",.3f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353120366-05:00 kpi_row("Inside Share %", "inside_sales_share_pct", ",.1f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353129736-05:00 kpi_summary = "\n".join(kpi_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353132216-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353134856-05:00 # ── Derived metrics for snapshot header ──────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353137246-05:00 def _dm(key, label, prefix="", suffix="", decimals=2): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353139606-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353141986-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353144366-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353146656-05:00 return None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353148976-05:00 t_str = f"{prefix}{float(t):,.{decimals}f}{suffix}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353151436-05:00 if b and float(b) > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353154406-05:00 pct = (float(t) - float(b)) / float(b) * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353159016-05:00 return f"{label}: {t_str} ({pct:+.1f}% vs baseline)" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353161606-05:00 return f"{label}: {t_str}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353164446-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353166926-05:00 dm_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353170026-05:00 v = _dm('inside_per_100_gal', 'Inside/100gal', '$', '', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353172426-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353174796-05:00 v = _dm('effective_fuel_price', 'Effective fuel price', '$', '/gal', 3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353177806-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353180196-05:00 v = _dm('inside_sales_share_pct','Inside share of sales', '', '%', 1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353182436-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353185736-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353188086-05:00 gap = trans.get('fuel_inside_gap_pct') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353191706-05:00 if gap is not None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353194206-05:00 direction = "FUEL OUTPACING INSIDE" if gap > 0 else "INSIDE OUTPACING FUEL" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353196606-05:00 dm_lines.append(f"Fuel-vs-Inside gap: {gap:+.1f}pp ({direction})") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353199136-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353201466-05:00 derived_metrics = "\n".join(dm_lines) if dm_lines else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353203936-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353207196-05:00 # ── Flags summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353209646-05:00 if flags: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353212136-05:00 flags_str = "\n".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353214546-05:00 f"- [{f['severity']}] {f['label']}: {f['note']}" for f in flags) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353216856-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353219316-05:00 flags_str = "No significant anomalies detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353221396-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353224556-05:00 # ── Shift summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353226826-05:00 shifts = trans.get('shift_summary', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353229066-05:00 if shifts: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353232426-05:00 shift_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353234786-05:00 for name, data in shifts.items(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353241696-05:00 rev = data.get('revenue', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353244346-05:00 tx = data.get('transactions', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353246726-05:00 gals = data.get('fuel_gallons', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353249286-05:00 shift_lines.append(f"- {name}: ${rev:,.2f} inside | {tx} tx | {gals:,.1f} gal fuel") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353251836-05:00 shift_summary = "\n".join(shift_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353254246-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353256456-05:00 shift_summary = "Shift data not available." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353259006-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353261366-05:00 # ── Gap / zero-tx hours ─────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353264226-05:00 gaps = trans.get('top_hourly_gaps', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353267416-05:00 zero_tx = trans.get('zero_tx_hours', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353269796-05:00 gap_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353272336-05:00 if gaps: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353274875-05:00 gap_str = ", ".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353277215-05:00 "{:02d}:00 ({} tx)".format(g['_hour'], g['tx_count']) for g in gaps) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353279735-05:00 gap_lines.append(f"Top gap hours: {gap_str}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353282095-05:00 if zero_tx: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353284535-05:00 gap_lines.append(f"Zero-tx hours (open window): {zero_tx}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353287315-05:00 gap_summary = "\n".join(gap_lines) if gap_lines else "No unusual gaps detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353289945-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353292625-05:00 # Authoritative store hours (issue #128): when Google Places supplies the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353295725-05:00 # store's real hours, prepend them and instruct the model to treat [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353298225-05:00 # off-hours zero activity as expected closure rather than hedging. The [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353300765-05:00 # gap/zero-tx figures above are already computed against this same window. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353303355-05:00 store_hours_val = kwargs.get('store_hours') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353305855-05:00 if store_hours_val and store_hours_val.get('weekday_text'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353308485-05:00 hrs_lines = ["AUTHORITATIVE STORE HOURS (Google Places — treat as ground truth):"] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353311565-05:00 hrs_lines += [f" {ln}" for ln in store_hours_val['weekday_text']] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353314235-05:00 if store_hours_val.get('open_24h'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353317285-05:00 hrs_lines.append(" This store operates 24 hours on at least one day.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353319665-05:00 hrs_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353322275-05:00 "Zero-transaction hours OUTSIDE these posted hours are EXPECTED " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353324595-05:00 "CLOSURE — do NOT flag them as gaps and do NOT hedge about whether " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353326925-05:00 "the store was open. Treat zero/low activity DURING posted open " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353329295-05:00 "hours as a real gap worth investigating.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353331715-05:00 gap_summary = "\n".join(hrs_lines) + "\n\n" + gap_summary [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353334115-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353336585-05:00 # ── Weather ─────────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353342955-05:00 if weather: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353345635-05:00 temp = weather.get('temp', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353348105-05:00 precip = weather.get('precipitation_in', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353354725-05:00 conds = list(weather.get('conditions', {}).keys()) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353357285-05:00 impact = weather.get('weather_impact_assessment', '') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353360665-05:00 yd = weather.get('yesterday', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353363055-05:00 td = weather.get('today_forecast', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353365655-05:00 rain_hrs= weather.get('rain_hours_today', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353368045-05:00 weather_str = ( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353370335-05:00 f"Report day: {yd.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353373025-05:00 f"{yd.get('temp_min','?')}–{yd.get('temp_max','?')}°F | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353375535-05:00 f"{yd.get('precip_in',0)}\" precip\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353377935-05:00 f"Today forecast: {td.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353380575-05:00 f"{td.get('temp_min','?')}–{td.get('temp_max','?')}°F\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353383325-05:00 f"Impact assessment: {impact}\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353385585-05:00 + (f"Rain hours: {rain_hrs}\n" if rain_hrs else "") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353388485-05:00 ) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353391035-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353393385-05:00 weather_str = "No weather data provided." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353395835-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353398465-05:00 # ── Schedule section ────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353401125-05:00 schedule_section = kwargs.get('schedule_section', None) or '' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353403435-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353406235-05:00 # ── Category baselines ──────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353408475-05:00 cat_baselines = kwargs.get('category_baselines', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353411015-05:00 # weekly_category_totals: actual accumulated weekly revenue per category [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353414245-05:00 # passed from Cell 39 _week_cat — USE THESE VERBATIM in the report table [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353416715-05:00 weekly_cat_totals = kwargs.get('weekly_category_totals', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353419775-05:00 _is_weekly = (report_type == 'weekly') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353422185-05:00 # Authoritative per-category revenue comes from the owner-mapped [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353424645-05:00 # category_mix (preprocessor). Post-mapping it is aggregated by taxonomy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353427745-05:00 # name and already reconciles to inside sales, so it — NOT the seeded [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353430245-05:00 # baselines — must drive the category table. Previously this block only [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353432715-05:00 # emitted categories that had a seeded history baseline; when baselines [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353435315-05:00 # were sparse or unseeded the model received no authoritative totals and [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353437915-05:00 # dumped the bulk of inside sales into "Other / unclassified" (issue #81). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353440215-05:00 _cat_mix_rows = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353442855-05:00 c for c in scan.get('category_mix', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353448695-05:00 if isinstance(c.get('category'), str) and str(c.get('category')).strip() [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353451195-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353453715-05:00 _period_lbl = 'This Week' if _is_weekly else 'Today' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353456695-05:00 if _is_weekly and weekly_cat_totals: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353459035-05:00 # Legacy notebook path passes pre-accumulated weekly totals verbatim. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353461475-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353464055-05:00 "WEEKLY Category Totals — USE THESE EXACT NUMBERS in Section 3 table.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353466485-05:00 "DO NOT invent or estimate different values. These are computed from actual data.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353468945-05:00 f" {'Category':<30} {'This Week':>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353471285-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353473585-05:00 for cat, week_rev in sorted(weekly_cat_totals.items(), [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353475965-05:00 key=lambda x: -x[1]): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353478675-05:00 if not isinstance(cat, str) or not cat.strip(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353481125-05:00 continue [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353483665-05:00 avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353485945-05:00 if avg > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353488435-05:00 pct = (week_rev - avg) / avg * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353490735-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353493065-05:00 f" {cat:<30} ${week_rev:>10,.0f} ${avg:>10,.0f}/day {pct:>+7.1f}%") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353495335-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353498355-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353500765-05:00 f" {cat:<30} ${week_rev:>10,.0f} (no baseline)") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353503325-05:00 category_baselines_str = "\n".join(_cb_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353505565-05:00 elif _cat_mix_rows: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353507935-05:00 _classified = sum((c.get('revenue') or 0) for c in _cat_mix_rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353510235-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353512595-05:00 f"Per-category inside revenue ({_period_lbl}) — USE THESE EXACT NUMBERS " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353515685-05:00 f"in the Section 3 category table. They are computed from the owner's " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353518155-05:00 f"merchandise-code taxonomy and already sum to inside sales " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353520885-05:00 f"(${_classified:,.0f}). Only the row explicitly labelled " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353525025-05:00 f"'Other / Unclassified' is unclassified — do NOT move any other " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353527645-05:00 f"category's revenue into 'Other', and do NOT invent different values.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353530025-05:00 f" {'Category':<30} {_period_lbl:>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353532365-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353534745-05:00 for c in sorted(_cat_mix_rows, key=lambda x: -(x.get('revenue') or 0)): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353537965-05:00 cat = str(c['category']) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353540155-05:00 rev = c.get('revenue') or 0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353542535-05:00 > avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353544725-05:00 ^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353551955-05:00 E AttributeError: 'NoneType' object has no attribute 'get' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353554235-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353556655-05:00 src/storeminder/agents/synthesis_agent.py:1426: AttributeError [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353559035-05:00 --------------------- generated xml file: /tmp/traits.xml ---------------------- [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353561464-05:00 =========================== short test summary info ============================ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353563904-05:00 ERROR tests/test_report_traits_live.py::test_daily_builds_and_renders - Attri... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353566464-05:00 ERROR tests/test_report_traits_live.py::test_daily_is_sendable_and_no_weekly_leak [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353568804-05:00 ERROR tests/test_report_traits_live.py::test_daily_headline_labeled_total_sales [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353571254-05:00 ERROR tests/test_report_traits_live.py::test_daily_sms_not_truncated - Attrib... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353573904-05:00 ERROR tests/test_report_traits_live.py::test_daily_artifact_bundle_written - ... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353576274-05:00 ERROR tests/test_report_traits_live.py::test_weekly_builds_and_renders - Attr... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353578614-05:00 ERROR tests/test_report_traits_live.py::test_weekly_is_sendable - AttributeEr... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353581054-05:00 ERROR tests/test_report_traits_live.py::test_weekly_headline_labeled_total_sales [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353583584-05:00 ERROR tests/test_report_traits_live.py::test_weekly_sms_not_truncated - Attri... [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353586044-05:00 ERROR tests/test_report_traits_live.py::test_weekly_artifact_bundle_written [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353588534-05:00 ======================== 10 errors in 140.21s (0:02:20) ======================== [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353590834-05:00 ===JUNIT_BEGIN=== [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353593534-05:00 @pytest.fixture(scope="module") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353595894-05:00 def daily_report(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353598094-05:00 > return _run("daily") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353600484-05:00 ^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353602654-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353605034-05:00 tests/test_report_traits_live.py:94: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353607454-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353609664-05:00 tests/test_report_traits_live.py:75: in _run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353612124-05:00 res = fn(store, end_date=d, dry_run=True, deliver=False, clean=True) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353614494-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353616984-05:00 src/storeminder/runner.py:340: in run_daily [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353619464-05:00 return _run_report(params, clean=clean) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353622454-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353625024-05:00 src/storeminder/runner.py:193: in _run_report [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353627294-05:00 result = run_pipeline( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353629944-05:00 src/storeminder/crew.py:463: in run_pipeline [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353632614-05:00 report_result = _agent.run_daily( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353634854-05:00 src/storeminder/agents/synthesis_agent.py:1023: in run_daily [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353637144-05:00 return self.run(report_type="daily", **kwargs) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353643984-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353646524-05:00 src/storeminder/agents/synthesis_agent.py:1073: in run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353648834-05:00 user = self._build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353651224-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353653444-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353655824-05:00 self = <storeminder.agents.synthesis_agent.SynthesisAgent object at 0x749a81486b90> [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353658204-05:00 report_date = datetime.date(2026, 7, 10), store_id = 'Fastime' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353661434-05:00 trans = {'total_transactions': 618, 'open_hour_start': 4, 'open_hour_end': 21, 'report_date_filter': '2026-07-10', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353663864-05:00 scan = {'total_scan_rows': 1940, 'report_date_filter': '2026-07-10', 'total_sales': 6436.97, 'avg_item_price': 3.88, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353666334-05:00 baseline = {'sample_count': 15, 'day_of_week': 4, 'total_revenue': 16270.22, 'total_transactions': 590.87, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353669494-05:00 flags = [{'metric': 'rank_drop', 'label': 'Rank Drop: COPENHAGEN FINE CUT', 'today': 40, 'baseline': 13, ...}, {'metric': 'ran...': 29, ...}, {'metric': 'rank_drop', 'label': 'Rank Drop: BUD LIGHT BEER 12 O', 'today': 33, 'baseline': 22, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353675194-05:00 fuel_section = '## 2. Fuel \n**2,583 gal | $9,446 | 279 fuel transactions** \nFuel volume came in at 2,583 gallons, down from 2,758...w | Confirm pump access and functionality during 21:00–23:00 window | Measure: Fuel volume in last two operating hours' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353677654-05:00 inside_section = '## 3. Inside Sales\n\n**Inside Sales: $6,436.97 (+9.5% vs normal Monday)** \nTobacco / Nicotine surged, but Foodserv...e (1 void, $25.64) and override logs | Because: Void rate 100% above baseline | Measure: Closeout discipline confirmed' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353680194-05:00 weather = {'yesterday': {'temp_max': 90.1, 'temp_min': 76.8, 'precip_in': 0.0, 'conditions': 'Partly cloudy'}, 'today_forecast':...rtly cloudy'}, 'rain_hours_today': [], 'weather_impact_assessment': 'Low — weather was mild, not a significant factor'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353682694-05:00 external_section = '## External Context\n\n**Google Reviews (5 reviews)** \nAverage rating of recent reviews (3.0/5) is below overall st...y 12:00 PM \n- Review staff scheduling: confirm if high-performing employee (Luna) is on shift today — Do by 10:00 AM' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353685094-05:00 loyalty_section = '## Loyalty & Promotions\n\n**Redemption Summary**\n| Promotion | Item | Today | Period Avg | Delta | Status |\n|-----...y EOD Friday \n- Confirm in-store visibility for underperforming tobacco and energy drink promos — Do by EOD Thursday' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353687654-05:00 ops_section = '## 5. Operations\n\n**Transactions & Throughput** \n962 total transactions | Avg ticket: $17.15 (+2% vs normal Frida...on void authorization protocol | Because: elevated voids in high-volume period | Measure: Reduce voids to ≤2 per shift' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353690124-05:00 fuel_pricing_section = '## Fuel Pricing\n\n**Current Market** \n| Station | Regular | Midgrade | Premium | Diesel | Distance | \n|---------...1mi and priced at $3.400. \n- Update competitor price tracking—several stations have outdated or missing diesel data.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353693034-05:00 prior_actions = None, report_type = 'daily', weekly_days = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353695364-05:00 dow_profile = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353697804-05:00 kwargs = {'category_baselines': None, 'schedule_section': None, 'weekly_category_totals': None, 'ext_fetch_date': '2026-07-12', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353700004-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353702364-05:00 def _build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353709124-05:00 self, report_date, store_id, trans, scan, baseline, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353711614-05:00 flags, fuel_section, inside_section, weather, external_section, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353714074-05:00 loyalty_section, ops_section, fuel_pricing_section, prior_actions, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353716264-05:00 report_type: str = "daily", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353718874-05:00 weekly_days: Optional[List[Dict]] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353721084-05:00 dow_profile: Optional[Dict] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353723284-05:00 **kwargs, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353725744-05:00 ) -> str: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353728104-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353730684-05:00 # ── KPI table ───────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353733414-05:00 kpi_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353735744-05:00 "| Metric | Today | Baseline | Delta | Priority |", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353738764-05:00 "|--------|-------|----------|-------|----------|", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353741734-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353744184-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353746904-05:00 # Keys that live in scan_summary (inside sales) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353749394-05:00 _scan_keys = {'total_sales', 'avg_item_price', 'total_scan_rows'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353751794-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353754274-05:00 def kpi_row(label, key, fmt_str=",.0f", prefix="", priority=None): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353756534-05:00 # Prefer scan for inside-sales metrics, trans for everything else [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353759074-05:00 if key in _scan_keys: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353761544-05:00 t = scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353763794-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353766424-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353768744-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353771114-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353773844-05:00 return [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353776154-05:00 t_str = f"{prefix}{t:{fmt_str}}" if isinstance(t, (int, float)) else str(t) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353778624-05:00 b_str = f"{prefix}{b:{fmt_str}}" if isinstance(b, (int, float)) else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353781204-05:00 delta = f"{((t-b)/b*100):+.1f}%" if isinstance(t,(int,float)) and isinstance(b,(int,float)) and b else "—" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353783614-05:00 # Priority: use flag severity if exists, else spec default [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353786004-05:00 flag = next((f for f in flags if f['metric'] == key), None) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353788424-05:00 pri = flag['severity'] if flag else (priority or "—") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353790874-05:00 kpi_lines.append(f"| {label} | {t_str} | {b_str} | {delta} | {pri} |") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353793604-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353796024-05:00 kpi_row("Total Transactions", "total_transactions", ",.0f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353802004-05:00 kpi_row("Total Sales", "total_revenue", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353804714-05:00 kpi_row("Inside Sales", "total_sales", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353807334-05:00 kpi_row("Avg Inside Ticket", "avg_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353810214-05:00 kpi_row("Avg Fuel Ticket", "avg_fuel_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353817424-05:00 kpi_row("Fuel Gallons", "fuel_gallons", ",.1f", "", "HIGH") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353820004-05:00 kpi_row("Fuel Sales $", "fuel_sales", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353822544-05:00 kpi_row("Fuel Transactions", "fuel_transactions", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353824934-05:00 kpi_row("Avg Gallons/Tx", "avg_gallons_per_tx", ",.2f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353828154-05:00 kpi_row("Forecourt Conv %", "forecourt_conversion_pct", ",.1f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353830524-05:00 kpi_row("Void Count", "void_count", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353833064-05:00 kpi_row("Void Total $", "void_total_amt", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353835484-05:00 kpi_row("Inside/100 Gal (weekly avg)", "inside_per_100_gal", ",.0f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353838014-05:00 kpi_row("Eff. Fuel Price", "effective_fuel_price", ",.3f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353840564-05:00 kpi_row("Inside Share %", "inside_sales_share_pct", ",.1f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353843084-05:00 kpi_summary = "\n".join(kpi_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353845363-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353847793-05:00 # ── Derived metrics for snapshot header ──────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353850293-05:00 def _dm(key, label, prefix="", suffix="", decimals=2): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353852573-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353855163-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353857553-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353859823-05:00 return None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353862183-05:00 t_str = f"{prefix}{float(t):,.{decimals}f}{suffix}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353864613-05:00 if b and float(b) > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353866813-05:00 pct = (float(t) - float(b)) / float(b) * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353869383-05:00 return f"{label}: {t_str} ({pct:+.1f}% vs baseline)" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353872363-05:00 return f"{label}: {t_str}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353874693-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353877023-05:00 dm_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353880533-05:00 v = _dm('inside_per_100_gal', 'Inside/100gal', '$', '', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353882893-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353885273-05:00 v = _dm('effective_fuel_price', 'Effective fuel price', '$', '/gal', 3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353887533-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353890063-05:00 v = _dm('inside_sales_share_pct','Inside share of sales', '', '%', 1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353892343-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353894643-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353897123-05:00 gap = trans.get('fuel_inside_gap_pct') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353899693-05:00 if gap is not None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353902353-05:00 direction = "FUEL OUTPACING INSIDE" if gap > 0 else "INSIDE OUTPACING FUEL" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353904813-05:00 dm_lines.append(f"Fuel-vs-Inside gap: {gap:+.1f}pp ({direction})") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353907143-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353909503-05:00 derived_metrics = "\n".join(dm_lines) if dm_lines else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353915723-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353925183-05:00 # ── Flags summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353933683-05:00 if flags: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353936153-05:00 flags_str = "\n".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353938933-05:00 f"- [{f['severity']}] {f['label']}: {f['note']}" for f in flags) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353941333-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353943643-05:00 flags_str = "No significant anomalies detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353946003-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353948523-05:00 # ── Shift summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353950983-05:00 shifts = trans.get('shift_summary', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353953253-05:00 if shifts: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353956053-05:00 shift_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353958533-05:00 for name, data in shifts.items(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353961613-05:00 rev = data.get('revenue', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353963863-05:00 tx = data.get('transactions', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353966293-05:00 gals = data.get('fuel_gallons', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353969413-05:00 shift_lines.append(f"- {name}: ${rev:,.2f} inside | {tx} tx | {gals:,.1f} gal fuel") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353971743-05:00 shift_summary = "\n".join(shift_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353974153-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353976673-05:00 shift_summary = "Shift data not available." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353979003-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353981563-05:00 # ── Gap / zero-tx hours ─────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353983963-05:00 gaps = trans.get('top_hourly_gaps', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353986373-05:00 zero_tx = trans.get('zero_tx_hours', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353992083-05:00 gap_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353994513-05:00 if gaps: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.353998253-05:00 gap_str = ", ".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354000773-05:00 "{:02d}:00 ({} tx)".format(g['_hour'], g['tx_count']) for g in gaps) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354003213-05:00 gap_lines.append(f"Top gap hours: {gap_str}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354005713-05:00 if zero_tx: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354008223-05:00 gap_lines.append(f"Zero-tx hours (open window): {zero_tx}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354010533-05:00 gap_summary = "\n".join(gap_lines) if gap_lines else "No unusual gaps detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354012813-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354015283-05:00 # Authoritative store hours (issue #128): when Google Places supplies the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354017523-05:00 # store's real hours, prepend them and instruct the model to treat [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354020063-05:00 # off-hours zero activity as expected closure rather than hedging. The [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354022343-05:00 # gap/zero-tx figures above are already computed against this same window. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354024733-05:00 store_hours_val = kwargs.get('store_hours') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354027473-05:00 if store_hours_val and store_hours_val.get('weekday_text'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354029983-05:00 hrs_lines = ["AUTHORITATIVE STORE HOURS (Google Places — treat as ground truth):"] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354037043-05:00 hrs_lines += [f" {ln}" for ln in store_hours_val['weekday_text']] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354040003-05:00 if store_hours_val.get('open_24h'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354042533-05:00 hrs_lines.append(" This store operates 24 hours on at least one day.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354044793-05:00 hrs_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354047133-05:00 "Zero-transaction hours OUTSIDE these posted hours are EXPECTED " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354049683-05:00 "CLOSURE — do NOT flag them as gaps and do NOT hedge about whether " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354052133-05:00 "the store was open. Treat zero/low activity DURING posted open " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354054583-05:00 "hours as a real gap worth investigating.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354056953-05:00 gap_summary = "\n".join(hrs_lines) + "\n\n" + gap_summary [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354059433-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354062073-05:00 # ── Weather ─────────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354064413-05:00 if weather: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354067083-05:00 temp = weather.get('temp', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354069633-05:00 precip = weather.get('precipitation_in', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354072093-05:00 conds = list(weather.get('conditions', {}).keys()) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354074603-05:00 impact = weather.get('weather_impact_assessment', '') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354076953-05:00 yd = weather.get('yesterday', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354079363-05:00 td = weather.get('today_forecast', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354081693-05:00 rain_hrs= weather.get('rain_hours_today', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354084173-05:00 weather_str = ( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354086493-05:00 f"Report day: {yd.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354090103-05:00 f"{yd.get('temp_min','?')}–{yd.get('temp_max','?')}°F | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354092383-05:00 f"{yd.get('precip_in',0)}\" precip\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354094723-05:00 f"Today forecast: {td.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354098043-05:00 f"{td.get('temp_min','?')}–{td.get('temp_max','?')}°F\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354100463-05:00 f"Impact assessment: {impact}\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354103143-05:00 + (f"Rain hours: {rain_hrs}\n" if rain_hrs else "") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354105383-05:00 ) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354107963-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354110273-05:00 weather_str = "No weather data provided." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354112783-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354115513-05:00 # ── Schedule section ────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354117923-05:00 schedule_section = kwargs.get('schedule_section', None) or '' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354120242-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354123322-05:00 # ── Category baselines ──────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354125752-05:00 cat_baselines = kwargs.get('category_baselines', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354128302-05:00 # weekly_category_totals: actual accumulated weekly revenue per category [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354135802-05:00 # passed from Cell 39 _week_cat — USE THESE VERBATIM in the report table [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354138472-05:00 weekly_cat_totals = kwargs.get('weekly_category_totals', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354140732-05:00 _is_weekly = (report_type == 'weekly') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354143202-05:00 # Authoritative per-category revenue comes from the owner-mapped [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354145532-05:00 # category_mix (preprocessor). Post-mapping it is aggregated by taxonomy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354147882-05:00 # name and already reconciles to inside sales, so it — NOT the seeded [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354150352-05:00 # baselines — must drive the category table. Previously this block only [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354152812-05:00 # emitted categories that had a seeded history baseline; when baselines [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354155152-05:00 # were sparse or unseeded the model received no authoritative totals and [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354161022-05:00 # dumped the bulk of inside sales into "Other / unclassified" (issue #81). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354163622-05:00 _cat_mix_rows = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354166022-05:00 c for c in scan.get('category_mix', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354168412-05:00 if isinstance(c.get('category'), str) and str(c.get('category')).strip() [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354170832-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354173042-05:00 _period_lbl = 'This Week' if _is_weekly else 'Today' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354175782-05:00 if _is_weekly and weekly_cat_totals: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354178122-05:00 # Legacy notebook path passes pre-accumulated weekly totals verbatim. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354180812-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354183312-05:00 "WEEKLY Category Totals — USE THESE EXACT NUMBERS in Section 3 table.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354185672-05:00 "DO NOT invent or estimate different values. These are computed from actual data.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354188172-05:00 f" {'Category':<30} {'This Week':>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354190632-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354192932-05:00 for cat, week_rev in sorted(weekly_cat_totals.items(), [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354196112-05:00 key=lambda x: -x[1]): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354198522-05:00 if not isinstance(cat, str) or not cat.strip(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354200902-05:00 continue [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354203322-05:00 avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354205762-05:00 if avg > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354208282-05:00 pct = (week_rev - avg) / avg * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354210632-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354213032-05:00 f" {cat:<30} ${week_rev:>10,.0f} ${avg:>10,.0f}/day {pct:>+7.1f}%") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354216242-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354218672-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354221852-05:00 f" {cat:<30} ${week_rev:>10,.0f} (no baseline)") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354224492-05:00 category_baselines_str = "\n".join(_cb_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354226922-05:00 elif _cat_mix_rows: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354229522-05:00 _classified = sum((c.get('revenue') or 0) for c in _cat_mix_rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354232612-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354235542-05:00 f"Per-category inside revenue ({_period_lbl}) — USE THESE EXACT NUMBERS " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354243392-05:00 f"in the Section 3 category table. They are computed from the owner's " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354245872-05:00 f"merchandise-code taxonomy and already sum to inside sales " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354247962-05:00 f"(${_classified:,.0f}). Only the row explicitly labelled " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354250302-05:00 f"'Other / Unclassified' is unclassified — do NOT move any other " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354252872-05:00 f"category's revenue into 'Other', and do NOT invent different values.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354255132-05:00 f" {'Category':<30} {_period_lbl:>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354257682-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354260302-05:00 for c in sorted(_cat_mix_rows, key=lambda x: -(x.get('revenue') or 0)): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354262622-05:00 cat = str(c['category']) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354264922-05:00 rev = c.get('revenue') or 0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354267262-05:00 > avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354269772-05:00 ^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354272112-05:00 E AttributeError: 'NoneType' object has no attribute 'get' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354274332-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354277382-05:00 src/storeminder/agents/synthesis_agent.py:1426: AttributeError@pytest.fixture(scope="module") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354279662-05:00 def daily_report(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354282102-05:00 > return _run("daily") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354284332-05:00 ^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354286562-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354288982-05:00 tests/test_report_traits_live.py:94: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354292702-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354295172-05:00 tests/test_report_traits_live.py:75: in _run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354297532-05:00 res = fn(store, end_date=d, dry_run=True, deliver=False, clean=True) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354299822-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354302252-05:00 src/storeminder/runner.py:340: in run_daily [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354304472-05:00 return _run_report(params, clean=clean) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354306432-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354308472-05:00 src/storeminder/runner.py:193: in _run_report [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354310702-05:00 result = run_pipeline( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354312722-05:00 src/storeminder/crew.py:463: in run_pipeline [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354314762-05:00 report_result = _agent.run_daily( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354316912-05:00 src/storeminder/agents/synthesis_agent.py:1023: in run_daily [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354320102-05:00 return self.run(report_type="daily", **kwargs) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354322832-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354325142-05:00 src/storeminder/agents/synthesis_agent.py:1073: in run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354327792-05:00 user = self._build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354330262-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354332512-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354334892-05:00 self = <storeminder.agents.synthesis_agent.SynthesisAgent object at 0x749a81486b90> [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354341792-05:00 report_date = datetime.date(2026, 7, 10), store_id = 'Fastime' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354352922-05:00 trans = {'total_transactions': 618, 'open_hour_start': 4, 'open_hour_end': 21, 'report_date_filter': '2026-07-10', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354357292-05:00 scan = {'total_scan_rows': 1940, 'report_date_filter': '2026-07-10', 'total_sales': 6436.97, 'avg_item_price': 3.88, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354360072-05:00 baseline = {'sample_count': 15, 'day_of_week': 4, 'total_revenue': 16270.22, 'total_transactions': 590.87, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354364652-05:00 flags = [{'metric': 'rank_drop', 'label': 'Rank Drop: COPENHAGEN FINE CUT', 'today': 40, 'baseline': 13, ...}, {'metric': 'ran...': 29, ...}, {'metric': 'rank_drop', 'label': 'Rank Drop: BUD LIGHT BEER 12 O', 'today': 33, 'baseline': 22, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354367562-05:00 fuel_section = '## 2. Fuel \n**2,583 gal | $9,446 | 279 fuel transactions** \nFuel volume came in at 2,583 gallons, down from 2,758...w | Confirm pump access and functionality during 21:00–23:00 window | Measure: Fuel volume in last two operating hours' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354370762-05:00 inside_section = '## 3. Inside Sales\n\n**Inside Sales: $6,436.97 (+9.5% vs normal Monday)** \nTobacco / Nicotine surged, but Foodserv...e (1 void, $25.64) and override logs | Because: Void rate 100% above baseline | Measure: Closeout discipline confirmed' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354373212-05:00 weather = {'yesterday': {'temp_max': 90.1, 'temp_min': 76.8, 'precip_in': 0.0, 'conditions': 'Partly cloudy'}, 'today_forecast':...rtly cloudy'}, 'rain_hours_today': [], 'weather_impact_assessment': 'Low — weather was mild, not a significant factor'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354375492-05:00 external_section = '## External Context\n\n**Google Reviews (5 reviews)** \nAverage rating of recent reviews (3.0/5) is below overall st...y 12:00 PM \n- Review staff scheduling: confirm if high-performing employee (Luna) is on shift today — Do by 10:00 AM' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354378492-05:00 loyalty_section = '## Loyalty & Promotions\n\n**Redemption Summary**\n| Promotion | Item | Today | Period Avg | Delta | Status |\n|-----...y EOD Friday \n- Confirm in-store visibility for underperforming tobacco and energy drink promos — Do by EOD Thursday' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354381222-05:00 ops_section = '## 5. Operations\n\n**Transactions & Throughput** \n962 total transactions | Avg ticket: $17.15 (+2% vs normal Frida...on void authorization protocol | Because: elevated voids in high-volume period | Measure: Reduce voids to ≤2 per shift' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354383692-05:00 fuel_pricing_section = '## Fuel Pricing\n\n**Current Market** \n| Station | Regular | Midgrade | Premium | Diesel | Distance | \n|---------...1mi and priced at $3.400. \n- Update competitor price tracking—several stations have outdated or missing diesel data.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354386252-05:00 prior_actions = None, report_type = 'daily', weekly_days = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354388711-05:00 dow_profile = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354391161-05:00 kwargs = {'category_baselines': None, 'schedule_section': None, 'weekly_category_totals': None, 'ext_fetch_date': '2026-07-12', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354393261-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354395621-05:00 def _build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354398161-05:00 self, report_date, store_id, trans, scan, baseline, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354400571-05:00 flags, fuel_section, inside_section, weather, external_section, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354403211-05:00 loyalty_section, ops_section, fuel_pricing_section, prior_actions, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354405521-05:00 report_type: str = "daily", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354408261-05:00 weekly_days: Optional[List[Dict]] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354410791-05:00 dow_profile: Optional[Dict] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354418291-05:00 **kwargs, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354420821-05:00 ) -> str: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354423321-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354425641-05:00 # ── KPI table ───────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354428231-05:00 kpi_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354430601-05:00 "| Metric | Today | Baseline | Delta | Priority |", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354432901-05:00 "|--------|-------|----------|-------|----------|", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354435631-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354437951-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354440411-05:00 # Keys that live in scan_summary (inside sales) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354442831-05:00 _scan_keys = {'total_sales', 'avg_item_price', 'total_scan_rows'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354445261-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354448641-05:00 def kpi_row(label, key, fmt_str=",.0f", prefix="", priority=None): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354451071-05:00 # Prefer scan for inside-sales metrics, trans for everything else [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354453541-05:00 if key in _scan_keys: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354456481-05:00 t = scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354458881-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354461431-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354464261-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354466551-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354468971-05:00 return [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354471341-05:00 t_str = f"{prefix}{t:{fmt_str}}" if isinstance(t, (int, float)) else str(t) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354473801-05:00 b_str = f"{prefix}{b:{fmt_str}}" if isinstance(b, (int, float)) else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354476301-05:00 delta = f"{((t-b)/b*100):+.1f}%" if isinstance(t,(int,float)) and isinstance(b,(int,float)) and b else "—" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354478711-05:00 # Priority: use flag severity if exists, else spec default [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354481151-05:00 flag = next((f for f in flags if f['metric'] == key), None) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354487351-05:00 pri = flag['severity'] if flag else (priority or "—") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354489851-05:00 kpi_lines.append(f"| {label} | {t_str} | {b_str} | {delta} | {pri} |") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354492301-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354494841-05:00 kpi_row("Total Transactions", "total_transactions", ",.0f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354497291-05:00 kpi_row("Total Sales", "total_revenue", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354499871-05:00 kpi_row("Inside Sales", "total_sales", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354502321-05:00 kpi_row("Avg Inside Ticket", "avg_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354504701-05:00 kpi_row("Avg Fuel Ticket", "avg_fuel_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354507361-05:00 kpi_row("Fuel Gallons", "fuel_gallons", ",.1f", "", "HIGH") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354509671-05:00 kpi_row("Fuel Sales $", "fuel_sales", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354512051-05:00 kpi_row("Fuel Transactions", "fuel_transactions", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354514651-05:00 kpi_row("Avg Gallons/Tx", "avg_gallons_per_tx", ",.2f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354520741-05:00 kpi_row("Forecourt Conv %", "forecourt_conversion_pct", ",.1f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354523301-05:00 kpi_row("Void Count", "void_count", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354525831-05:00 kpi_row("Void Total $", "void_total_amt", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354528121-05:00 kpi_row("Inside/100 Gal (weekly avg)", "inside_per_100_gal", ",.0f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354530681-05:00 kpi_row("Eff. Fuel Price", "effective_fuel_price", ",.3f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354533081-05:00 kpi_row("Inside Share %", "inside_sales_share_pct", ",.1f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354535421-05:00 kpi_summary = "\n".join(kpi_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354537841-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354540701-05:00 # ── Derived metrics for snapshot header ──────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354543171-05:00 def _dm(key, label, prefix="", suffix="", decimals=2): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354546311-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354548791-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354551181-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354553461-05:00 return None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354555931-05:00 t_str = f"{prefix}{float(t):,.{decimals}f}{suffix}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354558201-05:00 if b and float(b) > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354560751-05:00 pct = (float(t) - float(b)) / float(b) * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354563121-05:00 return f"{label}: {t_str} ({pct:+.1f}% vs baseline)" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354565501-05:00 return f"{label}: {t_str}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354567791-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354570181-05:00 dm_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354572711-05:00 v = _dm('inside_per_100_gal', 'Inside/100gal', '$', '', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354575321-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354577691-05:00 v = _dm('effective_fuel_price', 'Effective fuel price', '$', '/gal', 3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354580391-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354582921-05:00 v = _dm('inside_sales_share_pct','Inside share of sales', '', '%', 1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354585011-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354587371-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354589981-05:00 gap = trans.get('fuel_inside_gap_pct') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354592941-05:00 if gap is not None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354595661-05:00 direction = "FUEL OUTPACING INSIDE" if gap > 0 else "INSIDE OUTPACING FUEL" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354598021-05:00 dm_lines.append(f"Fuel-vs-Inside gap: {gap:+.1f}pp ({direction})") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354600371-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354603011-05:00 derived_metrics = "\n".join(dm_lines) if dm_lines else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354605251-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354607751-05:00 # ── Flags summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354610281-05:00 if flags: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354612731-05:00 flags_str = "\n".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354615151-05:00 f"- [{f['severity']}] {f['label']}: {f['note']}" for f in flags) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354617681-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354627141-05:00 flags_str = "No significant anomalies detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354629741-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354632121-05:00 # ── Shift summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354634581-05:00 shifts = trans.get('shift_summary', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354637061-05:00 if shifts: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354640261-05:00 shift_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354642871-05:00 for name, data in shifts.items(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354645101-05:00 rev = data.get('revenue', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354648041-05:00 tx = data.get('transactions', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354650591-05:00 gals = data.get('fuel_gallons', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354652931-05:00 shift_lines.append(f"- {name}: ${rev:,.2f} inside | {tx} tx | {gals:,.1f} gal fuel") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354655901-05:00 shift_summary = "\n".join(shift_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354659010-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354661460-05:00 shift_summary = "Shift data not available." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354663880-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354669470-05:00 # ── Gap / zero-tx hours ─────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354672060-05:00 gaps = trans.get('top_hourly_gaps', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354674480-05:00 zero_tx = trans.get('zero_tx_hours', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354676810-05:00 gap_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354679240-05:00 if gaps: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354681630-05:00 gap_str = ", ".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354684000-05:00 "{:02d}:00 ({} tx)".format(g['_hour'], g['tx_count']) for g in gaps) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354686310-05:00 gap_lines.append(f"Top gap hours: {gap_str}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354688610-05:00 if zero_tx: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354691050-05:00 gap_lines.append(f"Zero-tx hours (open window): {zero_tx}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354694390-05:00 gap_summary = "\n".join(gap_lines) if gap_lines else "No unusual gaps detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354696560-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354698920-05:00 # Authoritative store hours (issue #128): when Google Places supplies the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354701250-05:00 # store's real hours, prepend them and instruct the model to treat [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354703590-05:00 # off-hours zero activity as expected closure rather than hedging. The [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354706040-05:00 # gap/zero-tx figures above are already computed against this same window. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354708360-05:00 store_hours_val = kwargs.get('store_hours') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354710870-05:00 if store_hours_val and store_hours_val.get('weekday_text'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354713380-05:00 hrs_lines = ["AUTHORITATIVE STORE HOURS (Google Places — treat as ground truth):"] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354715570-05:00 hrs_lines += [f" {ln}" for ln in store_hours_val['weekday_text']] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354718050-05:00 if store_hours_val.get('open_24h'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354720430-05:00 hrs_lines.append(" This store operates 24 hours on at least one day.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354723060-05:00 hrs_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354725370-05:00 "Zero-transaction hours OUTSIDE these posted hours are EXPECTED " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354731460-05:00 "CLOSURE — do NOT flag them as gaps and do NOT hedge about whether " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354733980-05:00 "the store was open. Treat zero/low activity DURING posted open " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354736520-05:00 "hours as a real gap worth investigating.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354738790-05:00 gap_summary = "\n".join(hrs_lines) + "\n\n" + gap_summary [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354741180-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354743700-05:00 # ── Weather ─────────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354746050-05:00 if weather: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354748420-05:00 temp = weather.get('temp', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354750770-05:00 precip = weather.get('precipitation_in', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354753170-05:00 conds = list(weather.get('conditions', {}).keys()) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354756180-05:00 impact = weather.get('weather_impact_assessment', '') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354758610-05:00 yd = weather.get('yesterday', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354761000-05:00 td = weather.get('today_forecast', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354763300-05:00 rain_hrs= weather.get('rain_hours_today', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354765690-05:00 weather_str = ( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354768020-05:00 f"Report day: {yd.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354770320-05:00 f"{yd.get('temp_min','?')}–{yd.get('temp_max','?')}°F | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354772710-05:00 f"{yd.get('precip_in',0)}\" precip\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354775050-05:00 f"Today forecast: {td.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354777480-05:00 f"{td.get('temp_min','?')}–{td.get('temp_max','?')}°F\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354779900-05:00 f"Impact assessment: {impact}\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354782200-05:00 + (f"Rain hours: {rain_hrs}\n" if rain_hrs else "") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354784660-05:00 ) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354786960-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354789190-05:00 weather_str = "No weather data provided." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354791470-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354793860-05:00 # ── Schedule section ────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354796150-05:00 schedule_section = kwargs.get('schedule_section', None) or '' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354798450-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354800830-05:00 # ── Category baselines ──────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354803200-05:00 cat_baselines = kwargs.get('category_baselines', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354805540-05:00 # weekly_category_totals: actual accumulated weekly revenue per category [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354807880-05:00 # passed from Cell 39 _week_cat — USE THESE VERBATIM in the report table [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354810350-05:00 weekly_cat_totals = kwargs.get('weekly_category_totals', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354812620-05:00 _is_weekly = (report_type == 'weekly') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354814820-05:00 # Authoritative per-category revenue comes from the owner-mapped [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354817220-05:00 # category_mix (preprocessor). Post-mapping it is aggregated by taxonomy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354819410-05:00 # name and already reconciles to inside sales, so it — NOT the seeded [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354829130-05:00 # baselines — must drive the category table. Previously this block only [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354831730-05:00 # emitted categories that had a seeded history baseline; when baselines [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354834020-05:00 # were sparse or unseeded the model received no authoritative totals and [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354836390-05:00 # dumped the bulk of inside sales into "Other / unclassified" (issue #81). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354838670-05:00 _cat_mix_rows = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354841080-05:00 c for c in scan.get('category_mix', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354843420-05:00 if isinstance(c.get('category'), str) and str(c.get('category')).strip() [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354845840-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354848000-05:00 _period_lbl = 'This Week' if _is_weekly else 'Today' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354850500-05:00 if _is_weekly and weekly_cat_totals: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354852780-05:00 # Legacy notebook path passes pre-accumulated weekly totals verbatim. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354855140-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354858100-05:00 "WEEKLY Category Totals — USE THESE EXACT NUMBERS in Section 3 table.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354860600-05:00 "DO NOT invent or estimate different values. These are computed from actual data.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354863860-05:00 f" {'Category':<30} {'This Week':>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354866160-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354868480-05:00 for cat, week_rev in sorted(weekly_cat_totals.items(), [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354870810-05:00 key=lambda x: -x[1]): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354873190-05:00 if not isinstance(cat, str) or not cat.strip(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354875500-05:00 continue [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354877950-05:00 avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354880550-05:00 if avg > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354882910-05:00 pct = (week_rev - avg) / avg * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354885160-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354887590-05:00 f" {cat:<30} ${week_rev:>10,.0f} ${avg:>10,.0f}/day {pct:>+7.1f}%") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354889930-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354892190-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354894870-05:00 f" {cat:<30} ${week_rev:>10,.0f} (no baseline)") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354897150-05:00 category_baselines_str = "\n".join(_cb_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354899440-05:00 elif _cat_mix_rows: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354903050-05:00 _classified = sum((c.get('revenue') or 0) for c in _cat_mix_rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354905440-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354908130-05:00 f"Per-category inside revenue ({_period_lbl}) — USE THESE EXACT NUMBERS " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354910940-05:00 f"in the Section 3 category table. They are computed from the owner's " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354913570-05:00 f"merchandise-code taxonomy and already sum to inside sales " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354916030-05:00 f"(${_classified:,.0f}). Only the row explicitly labelled " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354923580-05:00 f"'Other / Unclassified' is unclassified — do NOT move any other " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354928289-05:00 f"category's revenue into 'Other', and do NOT invent different values.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354935989-05:00 f" {'Category':<30} {_period_lbl:>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354938439-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354941089-05:00 for c in sorted(_cat_mix_rows, key=lambda x: -(x.get('revenue') or 0)): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354943609-05:00 cat = str(c['category']) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354945909-05:00 rev = c.get('revenue') or 0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354948249-05:00 > avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354950499-05:00 ^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354953109-05:00 E AttributeError: 'NoneType' object has no attribute 'get' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354955219-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354957729-05:00 src/storeminder/agents/synthesis_agent.py:1426: AttributeError@pytest.fixture(scope="module") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354960109-05:00 def daily_report(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354962349-05:00 > return _run("daily") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354964839-05:00 ^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354966909-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354969219-05:00 tests/test_report_traits_live.py:94: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354971669-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354973899-05:00 tests/test_report_traits_live.py:75: in _run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354976239-05:00 res = fn(store, end_date=d, dry_run=True, deliver=False, clean=True) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354978449-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354980749-05:00 src/storeminder/runner.py:340: in run_daily [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354983059-05:00 return _run_report(params, clean=clean) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354985429-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354987709-05:00 src/storeminder/runner.py:193: in _run_report [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354990029-05:00 result = run_pipeline( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354992579-05:00 src/storeminder/crew.py:463: in run_pipeline [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354994859-05:00 report_result = _agent.run_daily( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354997009-05:00 src/storeminder/agents/synthesis_agent.py:1023: in run_daily [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.354999499-05:00 return self.run(report_type="daily", **kwargs) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355001809-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355004099-05:00 src/storeminder/agents/synthesis_agent.py:1073: in run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355006439-05:00 user = self._build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355012739-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355015059-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355017609-05:00 self = <storeminder.agents.synthesis_agent.SynthesisAgent object at 0x749a81486b90> [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355019979-05:00 report_date = datetime.date(2026, 7, 10), store_id = 'Fastime' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355022409-05:00 trans = {'total_transactions': 618, 'open_hour_start': 4, 'open_hour_end': 21, 'report_date_filter': '2026-07-10', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355024949-05:00 scan = {'total_scan_rows': 1940, 'report_date_filter': '2026-07-10', 'total_sales': 6436.97, 'avg_item_price': 3.88, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355028039-05:00 baseline = {'sample_count': 15, 'day_of_week': 4, 'total_revenue': 16270.22, 'total_transactions': 590.87, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355030649-05:00 flags = [{'metric': 'rank_drop', 'label': 'Rank Drop: COPENHAGEN FINE CUT', 'today': 40, 'baseline': 13, ...}, {'metric': 'ran...': 29, ...}, {'metric': 'rank_drop', 'label': 'Rank Drop: BUD LIGHT BEER 12 O', 'today': 33, 'baseline': 22, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355036739-05:00 fuel_section = '## 2. Fuel \n**2,583 gal | $9,446 | 279 fuel transactions** \nFuel volume came in at 2,583 gallons, down from 2,758...w | Confirm pump access and functionality during 21:00–23:00 window | Measure: Fuel volume in last two operating hours' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355039299-05:00 inside_section = '## 3. Inside Sales\n\n**Inside Sales: $6,436.97 (+9.5% vs normal Monday)** \nTobacco / Nicotine surged, but Foodserv...e (1 void, $25.64) and override logs | Because: Void rate 100% above baseline | Measure: Closeout discipline confirmed' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355041769-05:00 weather = {'yesterday': {'temp_max': 90.1, 'temp_min': 76.8, 'precip_in': 0.0, 'conditions': 'Partly cloudy'}, 'today_forecast':...rtly cloudy'}, 'rain_hours_today': [], 'weather_impact_assessment': 'Low — weather was mild, not a significant factor'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355044319-05:00 external_section = '## External Context\n\n**Google Reviews (5 reviews)** \nAverage rating of recent reviews (3.0/5) is below overall st...y 12:00 PM \n- Review staff scheduling: confirm if high-performing employee (Luna) is on shift today — Do by 10:00 AM' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355046849-05:00 loyalty_section = '## Loyalty & Promotions\n\n**Redemption Summary**\n| Promotion | Item | Today | Period Avg | Delta | Status |\n|-----...y EOD Friday \n- Confirm in-store visibility for underperforming tobacco and energy drink promos — Do by EOD Thursday' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355049119-05:00 ops_section = '## 5. Operations\n\n**Transactions & Throughput** \n962 total transactions | Avg ticket: $17.15 (+2% vs normal Frida...on void authorization protocol | Because: elevated voids in high-volume period | Measure: Reduce voids to ≤2 per shift' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355052699-05:00 fuel_pricing_section = '## Fuel Pricing\n\n**Current Market** \n| Station | Regular | Midgrade | Premium | Diesel | Distance | \n|---------...1mi and priced at $3.400. \n- Update competitor price tracking—several stations have outdated or missing diesel data.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355054979-05:00 prior_actions = None, report_type = 'daily', weekly_days = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355057019-05:00 dow_profile = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355059599-05:00 kwargs = {'category_baselines': None, 'schedule_section': None, 'weekly_category_totals': None, 'ext_fetch_date': '2026-07-12', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355061689-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355063899-05:00 def _build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355066249-05:00 self, report_date, store_id, trans, scan, baseline, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355068799-05:00 flags, fuel_section, inside_section, weather, external_section, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355071249-05:00 loyalty_section, ops_section, fuel_pricing_section, prior_actions, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355073709-05:00 report_type: str = "daily", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355076039-05:00 weekly_days: Optional[List[Dict]] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355078899-05:00 dow_profile: Optional[Dict] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355081249-05:00 **kwargs, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355083589-05:00 ) -> str: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355086199-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355088529-05:00 # ── KPI table ───────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355091019-05:00 kpi_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355093419-05:00 "| Metric | Today | Baseline | Delta | Priority |", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355096139-05:00 "|--------|-------|----------|-------|----------|", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355101929-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355104359-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355106579-05:00 # Keys that live in scan_summary (inside sales) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355109049-05:00 _scan_keys = {'total_sales', 'avg_item_price', 'total_scan_rows'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355111409-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355113739-05:00 def kpi_row(label, key, fmt_str=",.0f", prefix="", priority=None): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355116249-05:00 # Prefer scan for inside-sales metrics, trans for everything else [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355118579-05:00 if key in _scan_keys: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355120939-05:00 t = scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355123269-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355128669-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355131469-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355133739-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355135899-05:00 return [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355138779-05:00 t_str = f"{prefix}{t:{fmt_str}}" if isinstance(t, (int, float)) else str(t) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355141119-05:00 b_str = f"{prefix}{b:{fmt_str}}" if isinstance(b, (int, float)) else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355143519-05:00 delta = f"{((t-b)/b*100):+.1f}%" if isinstance(t,(int,float)) and isinstance(b,(int,float)) and b else "—" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355146629-05:00 # Priority: use flag severity if exists, else spec default [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355149689-05:00 flag = next((f for f in flags if f['metric'] == key), None) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355152839-05:00 pri = flag['severity'] if flag else (priority or "—") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355155359-05:00 kpi_lines.append(f"| {label} | {t_str} | {b_str} | {delta} | {pri} |") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355157679-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355160319-05:00 kpi_row("Total Transactions", "total_transactions", ",.0f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355162569-05:00 kpi_row("Total Sales", "total_revenue", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355165169-05:00 kpi_row("Inside Sales", "total_sales", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355167519-05:00 kpi_row("Avg Inside Ticket", "avg_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355170029-05:00 kpi_row("Avg Fuel Ticket", "avg_fuel_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355172439-05:00 kpi_row("Fuel Gallons", "fuel_gallons", ",.1f", "", "HIGH") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355174779-05:00 kpi_row("Fuel Sales $", "fuel_sales", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355178009-05:00 kpi_row("Fuel Transactions", "fuel_transactions", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355180459-05:00 kpi_row("Avg Gallons/Tx", "avg_gallons_per_tx", ",.2f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355184549-05:00 kpi_row("Forecourt Conv %", "forecourt_conversion_pct", ",.1f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355187009-05:00 kpi_row("Void Count", "void_count", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355189259-05:00 kpi_row("Void Total $", "void_total_amt", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355191649-05:00 kpi_row("Inside/100 Gal (weekly avg)", "inside_per_100_gal", ",.0f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355194089-05:00 kpi_row("Eff. Fuel Price", "effective_fuel_price", ",.3f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355200289-05:00 kpi_row("Inside Share %", "inside_sales_share_pct", ",.1f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355202689-05:00 kpi_summary = "\n".join(kpi_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355205119-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355207639-05:00 # ── Derived metrics for snapshot header ──────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355209949-05:00 def _dm(key, label, prefix="", suffix="", decimals=2): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355212569-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355214878-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355217208-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355219758-05:00 return None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355222038-05:00 t_str = f"{prefix}{float(t):,.{decimals}f}{suffix}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355224468-05:00 if b and float(b) > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355226908-05:00 pct = (float(t) - float(b)) / float(b) * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355229338-05:00 return f"{label}: {t_str} ({pct:+.1f}% vs baseline)" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355231808-05:00 return f"{label}: {t_str}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355234208-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355236548-05:00 dm_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355238978-05:00 v = _dm('inside_per_100_gal', 'Inside/100gal', '$', '', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355241298-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355243418-05:00 v = _dm('effective_fuel_price', 'Effective fuel price', '$', '/gal', 3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355245768-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355248158-05:00 v = _dm('inside_sales_share_pct','Inside share of sales', '', '%', 1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355250478-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355252868-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355255328-05:00 gap = trans.get('fuel_inside_gap_pct') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355257608-05:00 if gap is not None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355260068-05:00 direction = "FUEL OUTPACING INSIDE" if gap > 0 else "INSIDE OUTPACING FUEL" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355262478-05:00 dm_lines.append(f"Fuel-vs-Inside gap: {gap:+.1f}pp ({direction})") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355264718-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355267258-05:00 derived_metrics = "\n".join(dm_lines) if dm_lines else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355269498-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355271968-05:00 # ── Flags summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355274308-05:00 if flags: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355276758-05:00 flags_str = "\n".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355279208-05:00 f"- [{f['severity']}] {f['label']}: {f['note']}" for f in flags) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355281508-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355283848-05:00 flags_str = "No significant anomalies detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355286848-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355292978-05:00 # ── Shift summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355295538-05:00 shifts = trans.get('shift_summary', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355297878-05:00 if shifts: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355300088-05:00 shift_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355302548-05:00 for name, data in shifts.items(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355311338-05:00 rev = data.get('revenue', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355313758-05:00 tx = data.get('transactions', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355317218-05:00 gals = data.get('fuel_gallons', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355319598-05:00 shift_lines.append(f"- {name}: ${rev:,.2f} inside | {tx} tx | {gals:,.1f} gal fuel") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355322118-05:00 shift_summary = "\n".join(shift_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355324448-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355326858-05:00 shift_summary = "Shift data not available." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355329438-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355331948-05:00 # ── Gap / zero-tx hours ─────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355334228-05:00 gaps = trans.get('top_hourly_gaps', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355336628-05:00 zero_tx = trans.get('zero_tx_hours', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355338878-05:00 gap_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355341368-05:00 if gaps: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355343708-05:00 gap_str = ", ".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355346158-05:00 "{:02d}:00 ({} tx)".format(g['_hour'], g['tx_count']) for g in gaps) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355348658-05:00 gap_lines.append(f"Top gap hours: {gap_str}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355351008-05:00 if zero_tx: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355353538-05:00 gap_lines.append(f"Zero-tx hours (open window): {zero_tx}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355356098-05:00 gap_summary = "\n".join(gap_lines) if gap_lines else "No unusual gaps detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355358458-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355361298-05:00 # Authoritative store hours (issue #128): when Google Places supplies the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355364238-05:00 # store's real hours, prepend them and instruct the model to treat [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355366908-05:00 # off-hours zero activity as expected closure rather than hedging. The [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355369478-05:00 # gap/zero-tx figures above are already computed against this same window. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355372718-05:00 store_hours_val = kwargs.get('store_hours') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355375248-05:00 if store_hours_val and store_hours_val.get('weekday_text'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355377728-05:00 hrs_lines = ["AUTHORITATIVE STORE HOURS (Google Places — treat as ground truth):"] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355380148-05:00 hrs_lines += [f" {ln}" for ln in store_hours_val['weekday_text']] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355382408-05:00 if store_hours_val.get('open_24h'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355385138-05:00 hrs_lines.append(" This store operates 24 hours on at least one day.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355387448-05:00 hrs_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355389878-05:00 "Zero-transaction hours OUTSIDE these posted hours are EXPECTED " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355392348-05:00 "CLOSURE — do NOT flag them as gaps and do NOT hedge about whether " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355394888-05:00 "the store was open. Treat zero/low activity DURING posted open " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355397278-05:00 "hours as a real gap worth investigating.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355399648-05:00 gap_summary = "\n".join(hrs_lines) + "\n\n" + gap_summary [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355402008-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355404678-05:00 # ── Weather ─────────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355412098-05:00 if weather: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355414558-05:00 temp = weather.get('temp', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355416898-05:00 precip = weather.get('precipitation_in', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355419308-05:00 conds = list(weather.get('conditions', {}).keys()) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355421848-05:00 impact = weather.get('weather_impact_assessment', '') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355424378-05:00 yd = weather.get('yesterday', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355427458-05:00 td = weather.get('today_forecast', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355430208-05:00 rain_hrs= weather.get('rain_hours_today', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355432328-05:00 weather_str = ( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355435368-05:00 f"Report day: {yd.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355437768-05:00 f"{yd.get('temp_min','?')}–{yd.get('temp_max','?')}°F | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355440408-05:00 f"{yd.get('precip_in',0)}\" precip\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355442968-05:00 f"Today forecast: {td.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355445298-05:00 f"{td.get('temp_min','?')}–{td.get('temp_max','?')}°F\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355447738-05:00 f"Impact assessment: {impact}\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355450158-05:00 + (f"Rain hours: {rain_hrs}\n" if rain_hrs else "") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355452538-05:00 ) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355455018-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355457548-05:00 weather_str = "No weather data provided." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355460068-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355462468-05:00 # ── Schedule section ────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355464808-05:00 schedule_section = kwargs.get('schedule_section', None) or '' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355467108-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355472718-05:00 # ── Category baselines ──────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355475248-05:00 cat_baselines = kwargs.get('category_baselines', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355477678-05:00 # weekly_category_totals: actual accumulated weekly revenue per category [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355480158-05:00 # passed from Cell 39 _week_cat — USE THESE VERBATIM in the report table [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355482648-05:00 weekly_cat_totals = kwargs.get('weekly_category_totals', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355484908-05:00 _is_weekly = (report_type == 'weekly') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355487278-05:00 # Authoritative per-category revenue comes from the owner-mapped [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355489908-05:00 # category_mix (preprocessor). Post-mapping it is aggregated by taxonomy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355492258-05:00 # name and already reconciles to inside sales, so it — NOT the seeded [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355494998-05:00 # baselines — must drive the category table. Previously this block only [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355497428-05:00 # emitted categories that had a seeded history baseline; when baselines [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355499837-05:00 # were sparse or unseeded the model received no authoritative totals and [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355502227-05:00 # dumped the bulk of inside sales into "Other / unclassified" (issue #81). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355504517-05:00 _cat_mix_rows = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355511297-05:00 c for c in scan.get('category_mix', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355513747-05:00 if isinstance(c.get('category'), str) and str(c.get('category')).strip() [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355516087-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355519727-05:00 _period_lbl = 'This Week' if _is_weekly else 'Today' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355522157-05:00 if _is_weekly and weekly_cat_totals: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355524607-05:00 # Legacy notebook path passes pre-accumulated weekly totals verbatim. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355526967-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355529347-05:00 "WEEKLY Category Totals — USE THESE EXACT NUMBERS in Section 3 table.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355531737-05:00 "DO NOT invent or estimate different values. These are computed from actual data.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355534797-05:00 f" {'Category':<30} {'This Week':>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355537107-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355539367-05:00 for cat, week_rev in sorted(weekly_cat_totals.items(), [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355541887-05:00 key=lambda x: -x[1]): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355544247-05:00 if not isinstance(cat, str) or not cat.strip(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355547047-05:00 continue [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355549397-05:00 avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355551807-05:00 if avg > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355553847-05:00 pct = (week_rev - avg) / avg * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355556277-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355558657-05:00 f" {cat:<30} ${week_rev:>10,.0f} ${avg:>10,.0f}/day {pct:>+7.1f}%") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355561017-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355563337-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355565937-05:00 f" {cat:<30} ${week_rev:>10,.0f} (no baseline)") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355568317-05:00 category_baselines_str = "\n".join(_cb_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355570677-05:00 elif _cat_mix_rows: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355573047-05:00 _classified = sum((c.get('revenue') or 0) for c in _cat_mix_rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355575637-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355577957-05:00 f"Per-category inside revenue ({_period_lbl}) — USE THESE EXACT NUMBERS " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355580237-05:00 f"in the Section 3 category table. They are computed from the owner's " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355583157-05:00 f"merchandise-code taxonomy and already sum to inside sales " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355585627-05:00 f"(${_classified:,.0f}). Only the row explicitly labelled " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355588237-05:00 f"'Other / Unclassified' is unclassified — do NOT move any other " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355591017-05:00 f"category's revenue into 'Other', and do NOT invent different values.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355593197-05:00 f" {'Category':<30} {_period_lbl:>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355595617-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355598817-05:00 for c in sorted(_cat_mix_rows, key=lambda x: -(x.get('revenue') or 0)): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355601247-05:00 cat = str(c['category']) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355604577-05:00 rev = c.get('revenue') or 0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355607027-05:00 > avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355612947-05:00 ^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355615347-05:00 E AttributeError: 'NoneType' object has no attribute 'get' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355618297-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355621107-05:00 src/storeminder/agents/synthesis_agent.py:1426: AttributeError@pytest.fixture(scope="module") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355623467-05:00 def daily_report(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355625687-05:00 > return _run("daily") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355628057-05:00 ^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355630197-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355632787-05:00 tests/test_report_traits_live.py:94: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355635307-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355648427-05:00 tests/test_report_traits_live.py:75: in _run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355654117-05:00 res = fn(store, end_date=d, dry_run=True, deliver=False, clean=True) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355658497-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355661937-05:00 src/storeminder/runner.py:340: in run_daily [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355665237-05:00 return _run_report(params, clean=clean) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355668287-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355670597-05:00 src/storeminder/runner.py:193: in _run_report [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355673277-05:00 result = run_pipeline( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355675517-05:00 src/storeminder/crew.py:463: in run_pipeline [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355681267-05:00 report_result = _agent.run_daily( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355683707-05:00 src/storeminder/agents/synthesis_agent.py:1023: in run_daily [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355686577-05:00 return self.run(report_type="daily", **kwargs) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355688627-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355690647-05:00 src/storeminder/agents/synthesis_agent.py:1073: in run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355692597-05:00 user = self._build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355694667-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355696567-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355699247-05:00 self = <storeminder.agents.synthesis_agent.SynthesisAgent object at 0x749a81486b90> [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355701397-05:00 report_date = datetime.date(2026, 7, 10), store_id = 'Fastime' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355704467-05:00 trans = {'total_transactions': 618, 'open_hour_start': 4, 'open_hour_end': 21, 'report_date_filter': '2026-07-10', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355706687-05:00 scan = {'total_scan_rows': 1940, 'report_date_filter': '2026-07-10', 'total_sales': 6436.97, 'avg_item_price': 3.88, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355708807-05:00 baseline = {'sample_count': 15, 'day_of_week': 4, 'total_revenue': 16270.22, 'total_transactions': 590.87, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355711177-05:00 flags = [{'metric': 'rank_drop', 'label': 'Rank Drop: COPENHAGEN FINE CUT', 'today': 40, 'baseline': 13, ...}, {'metric': 'ran...': 29, ...}, {'metric': 'rank_drop', 'label': 'Rank Drop: BUD LIGHT BEER 12 O', 'today': 33, 'baseline': 22, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355714367-05:00 fuel_section = '## 2. Fuel \n**2,583 gal | $9,446 | 279 fuel transactions** \nFuel volume came in at 2,583 gallons, down from 2,758...w | Confirm pump access and functionality during 21:00–23:00 window | Measure: Fuel volume in last two operating hours' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355716707-05:00 inside_section = '## 3. Inside Sales\n\n**Inside Sales: $6,436.97 (+9.5% vs normal Monday)** \nTobacco / Nicotine surged, but Foodserv...e (1 void, $25.64) and override logs | Because: Void rate 100% above baseline | Measure: Closeout discipline confirmed' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355730517-05:00 weather = {'yesterday': {'temp_max': 90.1, 'temp_min': 76.8, 'precip_in': 0.0, 'conditions': 'Partly cloudy'}, 'today_forecast':...rtly cloudy'}, 'rain_hours_today': [], 'weather_impact_assessment': 'Low — weather was mild, not a significant factor'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355733967-05:00 external_section = '## External Context\n\n**Google Reviews (5 reviews)** \nAverage rating of recent reviews (3.0/5) is below overall st...y 12:00 PM \n- Review staff scheduling: confirm if high-performing employee (Luna) is on shift today — Do by 10:00 AM' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355737567-05:00 loyalty_section = '## Loyalty & Promotions\n\n**Redemption Summary**\n| Promotion | Item | Today | Period Avg | Delta | Status |\n|-----...y EOD Friday \n- Confirm in-store visibility for underperforming tobacco and energy drink promos — Do by EOD Thursday' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355741397-05:00 ops_section = '## 5. Operations\n\n**Transactions & Throughput** \n962 total transactions | Avg ticket: $17.15 (+2% vs normal Frida...on void authorization protocol | Because: elevated voids in high-volume period | Measure: Reduce voids to ≤2 per shift' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355744717-05:00 fuel_pricing_section = '## Fuel Pricing\n\n**Current Market** \n| Station | Regular | Midgrade | Premium | Diesel | Distance | \n|---------...1mi and priced at $3.400. \n- Update competitor price tracking—several stations have outdated or missing diesel data.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355747897-05:00 prior_actions = None, report_type = 'daily', weekly_days = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355750377-05:00 dow_profile = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355753627-05:00 kwargs = {'category_baselines': None, 'schedule_section': None, 'weekly_category_totals': None, 'ext_fetch_date': '2026-07-12', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355755597-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355757677-05:00 def _build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355760197-05:00 self, report_date, store_id, trans, scan, baseline, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355762327-05:00 flags, fuel_section, inside_section, weather, external_section, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355764457-05:00 loyalty_section, ops_section, fuel_pricing_section, prior_actions, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355766747-05:00 report_type: str = "daily", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355768847-05:00 weekly_days: Optional[List[Dict]] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355770977-05:00 dow_profile: Optional[Dict] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355773127-05:00 **kwargs, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355775127-05:00 ) -> str: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355778067-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355780677-05:00 # ── KPI table ───────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355782757-05:00 kpi_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355784876-05:00 "| Metric | Today | Baseline | Delta | Priority |", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355787036-05:00 "|--------|-------|----------|-------|----------|", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355789206-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355791246-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355793416-05:00 # Keys that live in scan_summary (inside sales) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355795486-05:00 _scan_keys = {'total_sales', 'avg_item_price', 'total_scan_rows'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355797516-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355802126-05:00 def kpi_row(label, key, fmt_str=",.0f", prefix="", priority=None): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355804356-05:00 # Prefer scan for inside-sales metrics, trans for everything else [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355810456-05:00 if key in _scan_keys: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355812766-05:00 t = scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355814886-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355817096-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355819216-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355821226-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355823466-05:00 return [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355825916-05:00 t_str = f"{prefix}{t:{fmt_str}}" if isinstance(t, (int, float)) else str(t) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355828036-05:00 b_str = f"{prefix}{b:{fmt_str}}" if isinstance(b, (int, float)) else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355830176-05:00 delta = f"{((t-b)/b*100):+.1f}%" if isinstance(t,(int,float)) and isinstance(b,(int,float)) and b else "—" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355832366-05:00 # Priority: use flag severity if exists, else spec default [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355834716-05:00 flag = next((f for f in flags if f['metric'] == key), None) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355836776-05:00 pri = flag['severity'] if flag else (priority or "—") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355838856-05:00 kpi_lines.append(f"| {label} | {t_str} | {b_str} | {delta} | {pri} |") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355841056-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355843166-05:00 kpi_row("Total Transactions", "total_transactions", ",.0f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355845216-05:00 kpi_row("Total Sales", "total_revenue", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355847286-05:00 kpi_row("Inside Sales", "total_sales", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355849316-05:00 kpi_row("Avg Inside Ticket", "avg_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355851396-05:00 kpi_row("Avg Fuel Ticket", "avg_fuel_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355853416-05:00 kpi_row("Fuel Gallons", "fuel_gallons", ",.1f", "", "HIGH") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355856146-05:00 kpi_row("Fuel Sales $", "fuel_sales", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355858176-05:00 kpi_row("Fuel Transactions", "fuel_transactions", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355860216-05:00 kpi_row("Avg Gallons/Tx", "avg_gallons_per_tx", ",.2f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355862166-05:00 kpi_row("Forecourt Conv %", "forecourt_conversion_pct", ",.1f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355864346-05:00 kpi_row("Void Count", "void_count", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355866376-05:00 kpi_row("Void Total $", "void_total_amt", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355868446-05:00 kpi_row("Inside/100 Gal (weekly avg)", "inside_per_100_gal", ",.0f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355870436-05:00 kpi_row("Eff. Fuel Price", "effective_fuel_price", ",.3f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355872586-05:00 kpi_row("Inside Share %", "inside_sales_share_pct", ",.1f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355874666-05:00 kpi_summary = "\n".join(kpi_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355876746-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355879176-05:00 # ── Derived metrics for snapshot header ──────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355881376-05:00 def _dm(key, label, prefix="", suffix="", decimals=2): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355887216-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355889266-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355891316-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355893406-05:00 return None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355895686-05:00 t_str = f"{prefix}{float(t):,.{decimals}f}{suffix}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355897976-05:00 if b and float(b) > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355900086-05:00 pct = (float(t) - float(b)) / float(b) * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355902326-05:00 return f"{label}: {t_str} ({pct:+.1f}% vs baseline)" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355904466-05:00 return f"{label}: {t_str}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355906596-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355908696-05:00 dm_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355910896-05:00 v = _dm('inside_per_100_gal', 'Inside/100gal', '$', '', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355913016-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355915236-05:00 v = _dm('effective_fuel_price', 'Effective fuel price', '$', '/gal', 3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355925246-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355934036-05:00 v = _dm('inside_sales_share_pct','Inside share of sales', '', '%', 1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355937106-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355939146-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355941216-05:00 gap = trans.get('fuel_inside_gap_pct') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355943406-05:00 if gap is not None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355945576-05:00 direction = "FUEL OUTPACING INSIDE" if gap > 0 else "INSIDE OUTPACING FUEL" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355947806-05:00 dm_lines.append(f"Fuel-vs-Inside gap: {gap:+.1f}pp ({direction})") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355949806-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355951866-05:00 derived_metrics = "\n".join(dm_lines) if dm_lines else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355953926-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355956236-05:00 # ── Flags summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355958286-05:00 if flags: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355960356-05:00 flags_str = "\n".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355965936-05:00 f"- [{f['severity']}] {f['label']}: {f['note']}" for f in flags) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355968136-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355970196-05:00 flags_str = "No significant anomalies detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355972286-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355974956-05:00 # ── Shift summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355977126-05:00 shifts = trans.get('shift_summary', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355979206-05:00 if shifts: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355981436-05:00 shift_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355983486-05:00 for name, data in shifts.items(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355985536-05:00 rev = data.get('revenue', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355987556-05:00 tx = data.get('transactions', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355989586-05:00 gals = data.get('fuel_gallons', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355991726-05:00 shift_lines.append(f"- {name}: ${rev:,.2f} inside | {tx} tx | {gals:,.1f} gal fuel") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355993776-05:00 shift_summary = "\n".join(shift_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.355999226-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356001526-05:00 shift_summary = "Shift data not available." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356004376-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356006526-05:00 # ── Gap / zero-tx hours ─────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356008576-05:00 gaps = trans.get('top_hourly_gaps', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356010566-05:00 zero_tx = trans.get('zero_tx_hours', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356012746-05:00 gap_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356014886-05:00 if gaps: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356017106-05:00 gap_str = ", ".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356019106-05:00 "{:02d}:00 ({} tx)".format(g['_hour'], g['tx_count']) for g in gaps) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356021246-05:00 gap_lines.append(f"Top gap hours: {gap_str}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356023376-05:00 if zero_tx: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356025476-05:00 gap_lines.append(f"Zero-tx hours (open window): {zero_tx}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356027886-05:00 gap_summary = "\n".join(gap_lines) if gap_lines else "No unusual gaps detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356029946-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356032616-05:00 # Authoritative store hours (issue #128): when Google Places supplies the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356035446-05:00 # store's real hours, prepend them and instruct the model to treat [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356037536-05:00 # off-hours zero activity as expected closure rather than hedging. The [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356039556-05:00 # gap/zero-tx figures above are already computed against this same window. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356041886-05:00 store_hours_val = kwargs.get('store_hours') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356044016-05:00 if store_hours_val and store_hours_val.get('weekday_text'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356046466-05:00 hrs_lines = ["AUTHORITATIVE STORE HOURS (Google Places — treat as ground truth):"] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356048526-05:00 hrs_lines += [f" {ln}" for ln in store_hours_val['weekday_text']] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356050596-05:00 if store_hours_val.get('open_24h'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356053026-05:00 hrs_lines.append(" This store operates 24 hours on at least one day.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356055166-05:00 hrs_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356057326-05:00 "Zero-transaction hours OUTSIDE these posted hours are EXPECTED " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356059506-05:00 "CLOSURE — do NOT flag them as gaps and do NOT hedge about whether " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356061665-05:00 "the store was open. Treat zero/low activity DURING posted open " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356063805-05:00 "hours as a real gap worth investigating.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356065935-05:00 gap_summary = "\n".join(hrs_lines) + "\n\n" + gap_summary [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356068085-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356070435-05:00 # ── Weather ─────────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356072625-05:00 if weather: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356074745-05:00 temp = weather.get('temp', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356076985-05:00 precip = weather.get('precipitation_in', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356079155-05:00 conds = list(weather.get('conditions', {}).keys()) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356082145-05:00 impact = weather.get('weather_impact_assessment', '') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356087575-05:00 yd = weather.get('yesterday', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356089765-05:00 td = weather.get('today_forecast', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356091945-05:00 rain_hrs= weather.get('rain_hours_today', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356094065-05:00 weather_str = ( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356096285-05:00 f"Report day: {yd.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356098465-05:00 f"{yd.get('temp_min','?')}–{yd.get('temp_max','?')}°F | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356100685-05:00 f"{yd.get('precip_in',0)}\" precip\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356102855-05:00 f"Today forecast: {td.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356105015-05:00 f"{td.get('temp_min','?')}–{td.get('temp_max','?')}°F\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356107145-05:00 f"Impact assessment: {impact}\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356109675-05:00 + (f"Rain hours: {rain_hrs}\n" if rain_hrs else "") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356111885-05:00 ) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356113975-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356116185-05:00 weather_str = "No weather data provided." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356118315-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356121325-05:00 # ── Schedule section ────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356126065-05:00 schedule_section = kwargs.get('schedule_section', None) or '' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356128265-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356131155-05:00 # ── Category baselines ──────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356133365-05:00 cat_baselines = kwargs.get('category_baselines', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356135645-05:00 # weekly_category_totals: actual accumulated weekly revenue per category [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356139035-05:00 # passed from Cell 39 _week_cat — USE THESE VERBATIM in the report table [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356141245-05:00 weekly_cat_totals = kwargs.get('weekly_category_totals', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356143335-05:00 _is_weekly = (report_type == 'weekly') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356145485-05:00 # Authoritative per-category revenue comes from the owner-mapped [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356147625-05:00 # category_mix (preprocessor). Post-mapping it is aggregated by taxonomy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356150375-05:00 # name and already reconciles to inside sales, so it — NOT the seeded [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356152495-05:00 # baselines — must drive the category table. Previously this block only [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356154715-05:00 # emitted categories that had a seeded history baseline; when baselines [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356156925-05:00 # were sparse or unseeded the model received no authoritative totals and [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356159245-05:00 # dumped the bulk of inside sales into "Other / unclassified" (issue #81). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356161465-05:00 _cat_mix_rows = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356163535-05:00 c for c in scan.get('category_mix', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356165685-05:00 if isinstance(c.get('category'), str) and str(c.get('category')).strip() [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356167865-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356170675-05:00 _period_lbl = 'This Week' if _is_weekly else 'Today' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356172865-05:00 if _is_weekly and weekly_cat_totals: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356175495-05:00 # Legacy notebook path passes pre-accumulated weekly totals verbatim. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356180975-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356183045-05:00 "WEEKLY Category Totals — USE THESE EXACT NUMBERS in Section 3 table.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356185285-05:00 "DO NOT invent or estimate different values. These are computed from actual data.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356187295-05:00 f" {'Category':<30} {'This Week':>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356189365-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356191345-05:00 for cat, week_rev in sorted(weekly_cat_totals.items(), [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356193345-05:00 key=lambda x: -x[1]): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356195405-05:00 if not isinstance(cat, str) or not cat.strip(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356197495-05:00 continue [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356199825-05:00 avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356201855-05:00 if avg > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356203955-05:00 pct = (week_rev - avg) / avg * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356205935-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356207995-05:00 f" {cat:<30} ${week_rev:>10,.0f} ${avg:>10,.0f}/day {pct:>+7.1f}%") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356210065-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356212255-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356214995-05:00 f" {cat:<30} ${week_rev:>10,.0f} (no baseline)") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356217075-05:00 category_baselines_str = "\n".join(_cb_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356219115-05:00 elif _cat_mix_rows: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356221335-05:00 _classified = sum((c.get('revenue') or 0) for c in _cat_mix_rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356223325-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356225385-05:00 f"Per-category inside revenue ({_period_lbl}) — USE THESE EXACT NUMBERS " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356227475-05:00 f"in the Section 3 category table. They are computed from the owner's " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356229585-05:00 f"merchandise-code taxonomy and already sum to inside sales " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356231605-05:00 f"(${_classified:,.0f}). Only the row explicitly labelled " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356233685-05:00 f"'Other / Unclassified' is unclassified — do NOT move any other " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356235805-05:00 f"category's revenue into 'Other', and do NOT invent different values.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356238025-05:00 f" {'Category':<30} {_period_lbl:>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356240155-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356242265-05:00 for c in sorted(_cat_mix_rows, key=lambda x: -(x.get('revenue') or 0)): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356244755-05:00 cat = str(c['category']) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356246865-05:00 rev = c.get('revenue') or 0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356248985-05:00 > avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356251745-05:00 ^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356254065-05:00 E AttributeError: 'NoneType' object has no attribute 'get' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356256055-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356258515-05:00 src/storeminder/agents/synthesis_agent.py:1426: AttributeError@pytest.fixture(scope="module") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356264005-05:00 def daily_report(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356266665-05:00 > return _run("daily") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356268725-05:00 ^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356270615-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356272685-05:00 tests/test_report_traits_live.py:94: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356277085-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356280095-05:00 tests/test_report_traits_live.py:75: in _run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356282145-05:00 res = fn(store, end_date=d, dry_run=True, deliver=False, clean=True) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356284745-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356287095-05:00 src/storeminder/runner.py:340: in run_daily [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356289235-05:00 return _run_report(params, clean=clean) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356291535-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356293725-05:00 src/storeminder/runner.py:193: in _run_report [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356295845-05:00 result = run_pipeline( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356298005-05:00 src/storeminder/crew.py:463: in run_pipeline [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356300195-05:00 report_result = _agent.run_daily( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356302375-05:00 src/storeminder/agents/synthesis_agent.py:1023: in run_daily [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356304455-05:00 return self.run(report_type="daily", **kwargs) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356306575-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356308785-05:00 src/storeminder/agents/synthesis_agent.py:1073: in run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356310955-05:00 user = self._build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356313115-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356315105-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356317255-05:00 self = <storeminder.agents.synthesis_agent.SynthesisAgent object at 0x749a81486b90> [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356319535-05:00 report_date = datetime.date(2026, 7, 10), store_id = 'Fastime' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356322405-05:00 trans = {'total_transactions': 618, 'open_hour_start': 4, 'open_hour_end': 21, 'report_date_filter': '2026-07-10', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356324685-05:00 scan = {'total_scan_rows': 1940, 'report_date_filter': '2026-07-10', 'total_sales': 6436.97, 'avg_item_price': 3.88, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356326915-05:00 baseline = {'sample_count': 15, 'day_of_week': 4, 'total_revenue': 16270.22, 'total_transactions': 590.87, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356329205-05:00 flags = [{'metric': 'rank_drop', 'label': 'Rank Drop: COPENHAGEN FINE CUT', 'today': 40, 'baseline': 13, ...}, {'metric': 'ran...': 29, ...}, {'metric': 'rank_drop', 'label': 'Rank Drop: BUD LIGHT BEER 12 O', 'today': 33, 'baseline': 22, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356331744-05:00 fuel_section = '## 2. Fuel \n**2,583 gal | $9,446 | 279 fuel transactions** \nFuel volume came in at 2,583 gallons, down from 2,758...w | Confirm pump access and functionality during 21:00–23:00 window | Measure: Fuel volume in last two operating hours' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356333974-05:00 inside_section = '## 3. Inside Sales\n\n**Inside Sales: $6,436.97 (+9.5% vs normal Monday)** \nTobacco / Nicotine surged, but Foodserv...e (1 void, $25.64) and override logs | Because: Void rate 100% above baseline | Measure: Closeout discipline confirmed' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356336144-05:00 weather = {'yesterday': {'temp_max': 90.1, 'temp_min': 76.8, 'precip_in': 0.0, 'conditions': 'Partly cloudy'}, 'today_forecast':...rtly cloudy'}, 'rain_hours_today': [], 'weather_impact_assessment': 'Low — weather was mild, not a significant factor'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356339094-05:00 external_section = '## External Context\n\n**Google Reviews (5 reviews)** \nAverage rating of recent reviews (3.0/5) is below overall st...y 12:00 PM \n- Review staff scheduling: confirm if high-performing employee (Luna) is on shift today — Do by 10:00 AM' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356344454-05:00 loyalty_section = '## Loyalty & Promotions\n\n**Redemption Summary**\n| Promotion | Item | Today | Period Avg | Delta | Status |\n|-----...y EOD Friday \n- Confirm in-store visibility for underperforming tobacco and energy drink promos — Do by EOD Thursday' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356347254-05:00 ops_section = '## 5. Operations\n\n**Transactions & Throughput** \n962 total transactions | Avg ticket: $17.15 (+2% vs normal Frida...on void authorization protocol | Because: elevated voids in high-volume period | Measure: Reduce voids to ≤2 per shift' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356349524-05:00 fuel_pricing_section = '## Fuel Pricing\n\n**Current Market** \n| Station | Regular | Midgrade | Premium | Diesel | Distance | \n|---------...1mi and priced at $3.400. \n- Update competitor price tracking—several stations have outdated or missing diesel data.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356351494-05:00 prior_actions = None, report_type = 'daily', weekly_days = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356353574-05:00 dow_profile = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356355634-05:00 kwargs = {'category_baselines': None, 'schedule_section': None, 'weekly_category_totals': None, 'ext_fetch_date': '2026-07-12', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356357514-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356359514-05:00 def _build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356361624-05:00 self, report_date, store_id, trans, scan, baseline, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356363704-05:00 flags, fuel_section, inside_section, weather, external_section, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356365774-05:00 loyalty_section, ops_section, fuel_pricing_section, prior_actions, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356367834-05:00 report_type: str = "daily", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356369884-05:00 weekly_days: Optional[List[Dict]] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356372414-05:00 dow_profile: Optional[Dict] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356374464-05:00 **kwargs, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356376504-05:00 ) -> str: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356380784-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356383074-05:00 # ── KPI table ───────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356385234-05:00 kpi_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356387404-05:00 "| Metric | Today | Baseline | Delta | Priority |", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356390184-05:00 "|--------|-------|----------|-------|----------|", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356392384-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356394504-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356396754-05:00 # Keys that live in scan_summary (inside sales) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356398914-05:00 _scan_keys = {'total_sales', 'avg_item_price', 'total_scan_rows'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356401024-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356403204-05:00 def kpi_row(label, key, fmt_str=",.0f", prefix="", priority=None): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356405414-05:00 # Prefer scan for inside-sales metrics, trans for everything else [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356407554-05:00 if key in _scan_keys: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356409804-05:00 t = scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356412004-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356414174-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356416324-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356418404-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356423774-05:00 return [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356425964-05:00 t_str = f"{prefix}{t:{fmt_str}}" if isinstance(t, (int, float)) else str(t) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356427974-05:00 b_str = f"{prefix}{b:{fmt_str}}" if isinstance(b, (int, float)) else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356430044-05:00 delta = f"{((t-b)/b*100):+.1f}%" if isinstance(t,(int,float)) and isinstance(b,(int,float)) and b else "—" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356432064-05:00 # Priority: use flag severity if exists, else spec default [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356434174-05:00 flag = next((f for f in flags if f['metric'] == key), None) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356436264-05:00 pri = flag['severity'] if flag else (priority or "—") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356438294-05:00 kpi_lines.append(f"| {label} | {t_str} | {b_str} | {delta} | {pri} |") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356440284-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356442864-05:00 kpi_row("Total Transactions", "total_transactions", ",.0f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356444824-05:00 kpi_row("Total Sales", "total_revenue", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356446874-05:00 kpi_row("Inside Sales", "total_sales", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356448844-05:00 kpi_row("Avg Inside Ticket", "avg_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356450824-05:00 kpi_row("Avg Fuel Ticket", "avg_fuel_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356452894-05:00 kpi_row("Fuel Gallons", "fuel_gallons", ",.1f", "", "HIGH") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356454874-05:00 kpi_row("Fuel Sales $", "fuel_sales", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356456844-05:00 kpi_row("Fuel Transactions", "fuel_transactions", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356458794-05:00 kpi_row("Avg Gallons/Tx", "avg_gallons_per_tx", ",.2f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356460784-05:00 kpi_row("Forecourt Conv %", "forecourt_conversion_pct", ",.1f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356462754-05:00 kpi_row("Void Count", "void_count", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356464704-05:00 kpi_row("Void Total $", "void_total_amt", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356466694-05:00 kpi_row("Inside/100 Gal (weekly avg)", "inside_per_100_gal", ",.0f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356468724-05:00 kpi_row("Eff. Fuel Price", "effective_fuel_price", ",.3f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356470674-05:00 kpi_row("Inside Share %", "inside_sales_share_pct", ",.1f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356472694-05:00 kpi_summary = "\n".join(kpi_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356474674-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356476814-05:00 # ── Derived metrics for snapshot header ──────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356478884-05:00 def _dm(key, label, prefix="", suffix="", decimals=2): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356480914-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356483004-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356485154-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356487244-05:00 return None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356489364-05:00 t_str = f"{prefix}{float(t):,.{decimals}f}{suffix}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356491504-05:00 if b and float(b) > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356497314-05:00 pct = (float(t) - float(b)) / float(b) * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356499604-05:00 return f"{label}: {t_str} ({pct:+.1f}% vs baseline)" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356501644-05:00 return f"{label}: {t_str}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356503794-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356505804-05:00 dm_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356507894-05:00 v = _dm('inside_per_100_gal', 'Inside/100gal', '$', '', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356510044-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356512034-05:00 v = _dm('effective_fuel_price', 'Effective fuel price', '$', '/gal', 3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356514094-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356516124-05:00 v = _dm('inside_sales_share_pct','Inside share of sales', '', '%', 1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356518094-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356520824-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356522794-05:00 gap = trans.get('fuel_inside_gap_pct') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356524784-05:00 if gap is not None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356529254-05:00 direction = "FUEL OUTPACING INSIDE" if gap > 0 else "INSIDE OUTPACING FUEL" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356531544-05:00 dm_lines.append(f"Fuel-vs-Inside gap: {gap:+.1f}pp ({direction})") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356533684-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356535734-05:00 derived_metrics = "\n".join(dm_lines) if dm_lines else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356537744-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356539934-05:00 # ── Flags summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356541934-05:00 if flags: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356543914-05:00 flags_str = "\n".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356546064-05:00 f"- [{f['severity']}] {f['label']}: {f['note']}" for f in flags) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356548054-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356550104-05:00 flags_str = "No significant anomalies detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356552064-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356554384-05:00 # ── Shift summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356556624-05:00 shifts = trans.get('shift_summary', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356558734-05:00 if shifts: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356560824-05:00 shift_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356563014-05:00 for name, data in shifts.items(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356565074-05:00 rev = data.get('revenue', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356568004-05:00 tx = data.get('transactions', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356570104-05:00 gals = data.get('fuel_gallons', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356572314-05:00 shift_lines.append(f"- {name}: ${rev:,.2f} inside | {tx} tx | {gals:,.1f} gal fuel") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356574544-05:00 shift_summary = "\n".join(shift_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356576654-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356579704-05:00 shift_summary = "Shift data not available." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356582104-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356584504-05:00 # ── Gap / zero-tx hours ─────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356586884-05:00 gaps = trans.get('top_hourly_gaps', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356588994-05:00 zero_tx = trans.get('zero_tx_hours', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356595224-05:00 gap_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356597724-05:00 if gaps: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356599704-05:00 gap_str = ", ".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356601673-05:00 "{:02d}:00 ({} tx)".format(g['_hour'], g['tx_count']) for g in gaps) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356603793-05:00 gap_lines.append(f"Top gap hours: {gap_str}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356605763-05:00 if zero_tx: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356608103-05:00 gap_lines.append(f"Zero-tx hours (open window): {zero_tx}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356610173-05:00 gap_summary = "\n".join(gap_lines) if gap_lines else "No unusual gaps detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356612683-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356614783-05:00 # Authoritative store hours (issue #128): when Google Places supplies the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356617073-05:00 # store's real hours, prepend them and instruct the model to treat [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356619123-05:00 # off-hours zero activity as expected closure rather than hedging. The [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356621223-05:00 # gap/zero-tx figures above are already computed against this same window. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356623203-05:00 store_hours_val = kwargs.get('store_hours') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356625213-05:00 if store_hours_val and store_hours_val.get('weekday_text'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356627183-05:00 hrs_lines = ["AUTHORITATIVE STORE HOURS (Google Places — treat as ground truth):"] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356629413-05:00 hrs_lines += [f" {ln}" for ln in store_hours_val['weekday_text']] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356631673-05:00 if store_hours_val.get('open_24h'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356633923-05:00 hrs_lines.append(" This store operates 24 hours on at least one day.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356636013-05:00 hrs_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356638233-05:00 "Zero-transaction hours OUTSIDE these posted hours are EXPECTED " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356640383-05:00 "CLOSURE — do NOT flag them as gaps and do NOT hedge about whether " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356642713-05:00 "the store was open. Treat zero/low activity DURING posted open " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356644773-05:00 "hours as a real gap worth investigating.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356646993-05:00 gap_summary = "\n".join(hrs_lines) + "\n\n" + gap_summary [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356649323-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356651513-05:00 # ── Weather ─────────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356653663-05:00 if weather: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356655833-05:00 temp = weather.get('temp', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356657903-05:00 precip = weather.get('precipitation_in', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356660053-05:00 conds = list(weather.get('conditions', {}).keys()) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356662223-05:00 impact = weather.get('weather_impact_assessment', '') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356664323-05:00 yd = weather.get('yesterday', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356666563-05:00 td = weather.get('today_forecast', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356668633-05:00 rain_hrs= weather.get('rain_hours_today', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356670763-05:00 weather_str = ( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356672923-05:00 f"Report day: {yd.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356675643-05:00 f"{yd.get('temp_min','?')}–{yd.get('temp_max','?')}°F | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356682413-05:00 f"{yd.get('precip_in',0)}\" precip\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356686813-05:00 f"Today forecast: {td.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356689053-05:00 f"{td.get('temp_min','?')}–{td.get('temp_max','?')}°F\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356691083-05:00 f"Impact assessment: {impact}\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356693173-05:00 + (f"Rain hours: {rain_hrs}\n" if rain_hrs else "") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356695253-05:00 ) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356697713-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356699733-05:00 weather_str = "No weather data provided." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356701923-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356704563-05:00 # ── Schedule section ────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356706743-05:00 schedule_section = kwargs.get('schedule_section', None) or '' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356708883-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356711673-05:00 # ── Category baselines ──────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356714103-05:00 cat_baselines = kwargs.get('category_baselines', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356716263-05:00 # weekly_category_totals: actual accumulated weekly revenue per category [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356718343-05:00 # passed from Cell 39 _week_cat — USE THESE VERBATIM in the report table [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356720393-05:00 weekly_cat_totals = kwargs.get('weekly_category_totals', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356722373-05:00 _is_weekly = (report_type == 'weekly') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356724583-05:00 # Authoritative per-category revenue comes from the owner-mapped [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356726843-05:00 # category_mix (preprocessor). Post-mapping it is aggregated by taxonomy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356728973-05:00 # name and already reconciles to inside sales, so it — NOT the seeded [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356731483-05:00 # baselines — must drive the category table. Previously this block only [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356733873-05:00 # emitted categories that had a seeded history baseline; when baselines [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356736133-05:00 # were sparse or unseeded the model received no authoritative totals and [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356738453-05:00 # dumped the bulk of inside sales into "Other / unclassified" (issue #81). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356740553-05:00 _cat_mix_rows = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356742683-05:00 c for c in scan.get('category_mix', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356744873-05:00 if isinstance(c.get('category'), str) and str(c.get('category')).strip() [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356746963-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356749163-05:00 _period_lbl = 'This Week' if _is_weekly else 'Today' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356751293-05:00 if _is_weekly and weekly_cat_totals: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356753463-05:00 # Legacy notebook path passes pre-accumulated weekly totals verbatim. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356755723-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356757983-05:00 "WEEKLY Category Totals — USE THESE EXACT NUMBERS in Section 3 table.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356760143-05:00 "DO NOT invent or estimate different values. These are computed from actual data.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356762773-05:00 f" {'Category':<30} {'This Week':>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356764993-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356770693-05:00 for cat, week_rev in sorted(weekly_cat_totals.items(), [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356772863-05:00 key=lambda x: -x[1]): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356774893-05:00 if not isinstance(cat, str) or not cat.strip(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356776993-05:00 continue [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356779153-05:00 avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356781123-05:00 if avg > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356783193-05:00 pct = (week_rev - avg) / avg * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356785173-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356787253-05:00 f" {cat:<30} ${week_rev:>10,.0f} ${avg:>10,.0f}/day {pct:>+7.1f}%") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356789253-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356791343-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356793673-05:00 f" {cat:<30} ${week_rev:>10,.0f} (no baseline)") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356795823-05:00 category_baselines_str = "\n".join(_cb_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356797933-05:00 elif _cat_mix_rows: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356800073-05:00 _classified = sum((c.get('revenue') or 0) for c in _cat_mix_rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356802163-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356804313-05:00 f"Per-category inside revenue ({_period_lbl}) — USE THESE EXACT NUMBERS " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356806493-05:00 f"in the Section 3 category table. They are computed from the owner's " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356808663-05:00 f"merchandise-code taxonomy and already sum to inside sales " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356810773-05:00 f"(${_classified:,.0f}). Only the row explicitly labelled " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356812943-05:00 f"'Other / Unclassified' is unclassified — do NOT move any other " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356815063-05:00 f"category's revenue into 'Other', and do NOT invent different values.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356817563-05:00 f" {'Category':<30} {_period_lbl:>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356819633-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356821773-05:00 for c in sorted(_cat_mix_rows, key=lambda x: -(x.get('revenue') or 0)): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356823843-05:00 cat = str(c['category']) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356825983-05:00 rev = c.get('revenue') or 0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356828183-05:00 > avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356830283-05:00 ^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356846683-05:00 E AttributeError: 'NoneType' object has no attribute 'get' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356853163-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356860513-05:00 src/storeminder/agents/synthesis_agent.py:1426: AttributeError@pytest.fixture(scope="module") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356864163-05:00 def weekly_report(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356867053-05:00 > return _run("weekly") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356869593-05:00 ^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356871712-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356873922-05:00 tests/test_report_traits_live.py:99: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356876262-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356878742-05:00 tests/test_report_traits_live.py:75: in _run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356893902-05:00 res = fn(store, end_date=d, dry_run=True, deliver=False, clean=True) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356896542-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356899862-05:00 src/storeminder/runner.py:359: in run_weekly [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356902102-05:00 return _run_report(params, clean=clean) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356905502-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356907692-05:00 src/storeminder/runner.py:193: in _run_report [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356910062-05:00 result = run_pipeline( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356912662-05:00 src/storeminder/crew.py:456: in run_pipeline [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356914762-05:00 report_result = _agent.run_weekly( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356932782-05:00 src/storeminder/agents/synthesis_agent.py:1028: in run_weekly [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356940982-05:00 return self.run(report_type="weekly", **kwargs) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356944022-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356946162-05:00 src/storeminder/agents/synthesis_agent.py:1073: in run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356949022-05:00 user = self._build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356951372-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356953442-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356956392-05:00 self = <storeminder.agents.synthesis_agent.SynthesisAgent object at 0x749b3d120d50> [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356958692-05:00 report_date = datetime.date(2026, 7, 10), store_id = 'Fastime' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356962312-05:00 trans = {'total_transactions': 618, 'open_hour_start': 4, 'open_hour_end': 21, 'report_date_filter': '2026-07-10', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356964962-05:00 scan = {'total_scan_rows': 1940, 'report_date_filter': '2026-07-10', 'total_sales': 6436.97, 'avg_item_price': 3.88, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356967952-05:00 baseline = {'day_of_week': 21, 'total_revenue': 100081.6, 'total_transactions': 3568.94, 'fuel_gallons': 16961.39, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356970752-05:00 flags = [{'metric': 'rank_drop', 'label': 'Rank Drop: COPENHAGEN FINE CUT', 'today': 40, 'baseline': 13, ...}, {'metric': 'ran...': 29, ...}, {'metric': 'rank_drop', 'label': 'Rank Drop: BUD LIGHT BEER 12 O', 'today': 33, 'baseline': 22, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356973492-05:00 fuel_section = '## 2. Fuel \n**2,583 gal | $9,446 | 279 fuel transactions** \nFuel volume came in at 2,583 gallons, down from 2,758...nfirm pump access and functionality during 22:00–23:00 if store is open | Measure: Fuel dispensed in evening gap hours' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356975642-05:00 inside_section = '## 3. Inside Sales\n\n**Inside Sales: $6,437 (+9.5% vs normal Tuesday)** \nTobacco / Nicotine surged on strong Marlb...EGG ↑14, ADD CHEESE ↑8) | Because: Modifier use rising but Plate Lunch down 60% | Measure: Attach rate per food ticket' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356978012-05:00 weather = {'yesterday': {'temp_max': 88.3, 'temp_min': 75.4, 'precip_in': 0.0, 'conditions': 'Clear'}, 'today_forecast': {'temp_...cloudy'}, 'rain_hours_today': [], 'weather_impact_assessment': 'Low — weather was mild, not a significant factor', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356980262-05:00 external_section = '## External Context\n\n**Google Reviews (5 reviews)** \nAverage rating of recent reviews (3.0/5) is below store’s ov...- Verify staff scheduling: confirm Luna’s shift coverage and retrain team on food handling consistency — Do by 3:00 PM' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356982392-05:00 loyalty_section = '## Loyalty & Promotions\n\n**Redemption Summary**\n| Promotion | Item | Today | Period Avg | Delta | Status |\n|-----...te and consider extending top performers like *Red Bull Original* and *Doritos Nacho Cheese* — evaluate by end of week' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356992022-05:00 ops_section = '## 5. Operations\n\n**Transactions & Throughput** \n962 total transactions | Avg ticket: $17.10 (+12% vs normal Frid... daily early fuel/retail volume.* \n\nNo additional hours recommended — optimize existing ownership and shift timing.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356994762-05:00 fuel_pricing_section = '## Fuel Pricing\n\n**Current Market** \n| Station | Regular | Midgrade | Premium | Diesel | Distance | \n|---------...y 8:00 AM tomorrow (2026-07-11). \n- Monitor Murphy USA and FASTIME for updates; both changed prices in last 3 hours.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.356997162-05:00 prior_actions = None, report_type = 'weekly' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357002942-05:00 weekly_days = [{'date': '2026-07-04', 'dow_name': 'Saturday', 'total_revenue': 7392.12, 'fuel_gallons': 1142.1, ...}, {'date': '2026...42.4, ...}, {'date': '2026-07-09', 'dow_name': 'Thursday', 'total_revenue': 16227.6, 'fuel_gallons': 2780.6, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357005242-05:00 dow_profile = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357007912-05:00 kwargs = {'category_baselines': None, 'schedule_section': None, 'weekly_category_totals': None, 'ext_fetch_date': '2026-07-12', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357009822-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357011912-05:00 def _build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357013992-05:00 self, report_date, store_id, trans, scan, baseline, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357016542-05:00 flags, fuel_section, inside_section, weather, external_section, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357018702-05:00 loyalty_section, ops_section, fuel_pricing_section, prior_actions, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357020862-05:00 report_type: str = "daily", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357022932-05:00 weekly_days: Optional[List[Dict]] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357025052-05:00 dow_profile: Optional[Dict] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357027852-05:00 **kwargs, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357029902-05:00 ) -> str: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357032032-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357034452-05:00 # ── KPI table ───────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357036432-05:00 kpi_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357039562-05:00 "| Metric | Today | Baseline | Delta | Priority |", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357041652-05:00 "|--------|-------|----------|-------|----------|", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357043672-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357045992-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357047942-05:00 # Keys that live in scan_summary (inside sales) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357049922-05:00 _scan_keys = {'total_sales', 'avg_item_price', 'total_scan_rows'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357052022-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357054072-05:00 def kpi_row(label, key, fmt_str=",.0f", prefix="", priority=None): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357056212-05:00 # Prefer scan for inside-sales metrics, trans for everything else [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357059072-05:00 if key in _scan_keys: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357061862-05:00 t = scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357064032-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357066202-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357068792-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357071012-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357073102-05:00 return [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357075412-05:00 t_str = f"{prefix}{t:{fmt_str}}" if isinstance(t, (int, float)) else str(t) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357077512-05:00 b_str = f"{prefix}{b:{fmt_str}}" if isinstance(b, (int, float)) else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357083102-05:00 delta = f"{((t-b)/b*100):+.1f}%" if isinstance(t,(int,float)) and isinstance(b,(int,float)) and b else "—" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357085352-05:00 # Priority: use flag severity if exists, else spec default [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357087452-05:00 flag = next((f for f in flags if f['metric'] == key), None) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357090332-05:00 pri = flag['severity'] if flag else (priority or "—") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357093242-05:00 kpi_lines.append(f"| {label} | {t_str} | {b_str} | {delta} | {pri} |") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357095312-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357097472-05:00 kpi_row("Total Transactions", "total_transactions", ",.0f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357099632-05:00 kpi_row("Total Sales", "total_revenue", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357101942-05:00 kpi_row("Inside Sales", "total_sales", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357104092-05:00 kpi_row("Avg Inside Ticket", "avg_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357106332-05:00 kpi_row("Avg Fuel Ticket", "avg_fuel_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357108392-05:00 kpi_row("Fuel Gallons", "fuel_gallons", ",.1f", "", "HIGH") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357110522-05:00 kpi_row("Fuel Sales $", "fuel_sales", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357112572-05:00 kpi_row("Fuel Transactions", "fuel_transactions", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357114642-05:00 kpi_row("Avg Gallons/Tx", "avg_gallons_per_tx", ",.2f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357116802-05:00 kpi_row("Forecourt Conv %", "forecourt_conversion_pct", ",.1f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357118932-05:00 kpi_row("Void Count", "void_count", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357121962-05:00 kpi_row("Void Total $", "void_total_amt", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357124062-05:00 kpi_row("Inside/100 Gal (weekly avg)", "inside_per_100_gal", ",.0f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357126132-05:00 kpi_row("Eff. Fuel Price", "effective_fuel_price", ",.3f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357128562-05:00 kpi_row("Inside Share %", "inside_sales_share_pct", ",.1f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357130912-05:00 kpi_summary = "\n".join(kpi_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357133242-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357136712-05:00 # ── Derived metrics for snapshot header ──────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357139252-05:00 def _dm(key, label, prefix="", suffix="", decimals=2): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357141692-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357147412-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357149992-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357152522-05:00 return None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357154751-05:00 t_str = f"{prefix}{float(t):,.{decimals}f}{suffix}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357158031-05:00 if b and float(b) > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357160351-05:00 pct = (float(t) - float(b)) / float(b) * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357162851-05:00 return f"{label}: {t_str} ({pct:+.1f}% vs baseline)" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357165511-05:00 return f"{label}: {t_str}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357171721-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357174311-05:00 dm_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357176661-05:00 v = _dm('inside_per_100_gal', 'Inside/100gal', '$', '', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357178801-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357180991-05:00 v = _dm('effective_fuel_price', 'Effective fuel price', '$', '/gal', 3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357183181-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357185471-05:00 v = _dm('inside_sales_share_pct','Inside share of sales', '', '%', 1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357187911-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357190931-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357193221-05:00 gap = trans.get('fuel_inside_gap_pct') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357195521-05:00 if gap is not None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357197771-05:00 direction = "FUEL OUTPACING INSIDE" if gap > 0 else "INSIDE OUTPACING FUEL" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357200421-05:00 dm_lines.append(f"Fuel-vs-Inside gap: {gap:+.1f}pp ({direction})") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357202611-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357204831-05:00 derived_metrics = "\n".join(dm_lines) if dm_lines else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357207281-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357209931-05:00 # ── Flags summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357212021-05:00 if flags: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357214421-05:00 flags_str = "\n".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357216661-05:00 f"- [{f['severity']}] {f['label']}: {f['note']}" for f in flags) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357218651-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357221211-05:00 flags_str = "No significant anomalies detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357223271-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357225731-05:00 # ── Shift summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357227931-05:00 shifts = trans.get('shift_summary', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357230561-05:00 if shifts: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357232661-05:00 shift_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357234791-05:00 for name, data in shifts.items(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357237591-05:00 rev = data.get('revenue', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357240291-05:00 tx = data.get('transactions', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357242491-05:00 gals = data.get('fuel_gallons', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357245331-05:00 shift_lines.append(f"- {name}: ${rev:,.2f} inside | {tx} tx | {gals:,.1f} gal fuel") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357247551-05:00 shift_summary = "\n".join(shift_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357249481-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357251571-05:00 shift_summary = "Shift data not available." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357253711-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357256311-05:00 # ── Gap / zero-tx hours ─────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357258931-05:00 gaps = trans.get('top_hourly_gaps', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357261851-05:00 zero_tx = trans.get('zero_tx_hours', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357264251-05:00 gap_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357266441-05:00 if gaps: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357268571-05:00 gap_str = ", ".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357270881-05:00 "{:02d}:00 ({} tx)".format(g['_hour'], g['tx_count']) for g in gaps) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357277841-05:00 gap_lines.append(f"Top gap hours: {gap_str}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357279841-05:00 if zero_tx: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357281951-05:00 gap_lines.append(f"Zero-tx hours (open window): {zero_tx}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357284071-05:00 gap_summary = "\n".join(gap_lines) if gap_lines else "No unusual gaps detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357286141-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357288191-05:00 # Authoritative store hours (issue #128): when Google Places supplies the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357290761-05:00 # store's real hours, prepend them and instruct the model to treat [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357293121-05:00 # off-hours zero activity as expected closure rather than hedging. The [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357295471-05:00 # gap/zero-tx figures above are already computed against this same window. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357297731-05:00 store_hours_val = kwargs.get('store_hours') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357300491-05:00 if store_hours_val and store_hours_val.get('weekday_text'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357302641-05:00 hrs_lines = ["AUTHORITATIVE STORE HOURS (Google Places — treat as ground truth):"] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357305161-05:00 hrs_lines += [f" {ln}" for ln in store_hours_val['weekday_text']] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357307801-05:00 if store_hours_val.get('open_24h'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357310981-05:00 hrs_lines.append(" This store operates 24 hours on at least one day.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357313291-05:00 hrs_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357315561-05:00 "Zero-transaction hours OUTSIDE these posted hours are EXPECTED " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357318041-05:00 "CLOSURE — do NOT flag them as gaps and do NOT hedge about whether " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357320351-05:00 "the store was open. Treat zero/low activity DURING posted open " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357322711-05:00 "hours as a real gap worth investigating.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357324981-05:00 gap_summary = "\n".join(hrs_lines) + "\n\n" + gap_summary [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357327221-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357332811-05:00 # ── Weather ─────────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357335171-05:00 if weather: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357337381-05:00 temp = weather.get('temp', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357339601-05:00 precip = weather.get('precipitation_in', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357341931-05:00 conds = list(weather.get('conditions', {}).keys()) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357344311-05:00 impact = weather.get('weather_impact_assessment', '') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357347301-05:00 yd = weather.get('yesterday', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357349561-05:00 td = weather.get('today_forecast', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357351691-05:00 rain_hrs= weather.get('rain_hours_today', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357354001-05:00 weather_str = ( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357356181-05:00 f"Report day: {yd.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357358301-05:00 f"{yd.get('temp_min','?')}–{yd.get('temp_max','?')}°F | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357360721-05:00 f"{yd.get('precip_in',0)}\" precip\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357363011-05:00 f"Today forecast: {td.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357365311-05:00 f"{td.get('temp_min','?')}–{td.get('temp_max','?')}°F\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357370951-05:00 f"Impact assessment: {impact}\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357373381-05:00 + (f"Rain hours: {rain_hrs}\n" if rain_hrs else "") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357375541-05:00 ) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357377621-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357379801-05:00 weather_str = "No weather data provided." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357381921-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357384301-05:00 # ── Schedule section ────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357386931-05:00 schedule_section = kwargs.get('schedule_section', None) or '' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357389001-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357391211-05:00 # ── Category baselines ──────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357394201-05:00 cat_baselines = kwargs.get('category_baselines', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357396561-05:00 # weekly_category_totals: actual accumulated weekly revenue per category [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357398761-05:00 # passed from Cell 39 _week_cat — USE THESE VERBATIM in the report table [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357401071-05:00 weekly_cat_totals = kwargs.get('weekly_category_totals', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357403361-05:00 _is_weekly = (report_type == 'weekly') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357406071-05:00 # Authoritative per-category revenue comes from the owner-mapped [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357408511-05:00 # category_mix (preprocessor). Post-mapping it is aggregated by taxonomy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357410741-05:00 # name and already reconciles to inside sales, so it — NOT the seeded [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357412811-05:00 # baselines — must drive the category table. Previously this block only [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357415021-05:00 # emitted categories that had a seeded history baseline; when baselines [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357417891-05:00 # were sparse or unseeded the model received no authoritative totals and [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357420091-05:00 # dumped the bulk of inside sales into "Other / unclassified" (issue #81). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357422221-05:00 _cat_mix_rows = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357424481-05:00 c for c in scan.get('category_mix', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357426641-05:00 if isinstance(c.get('category'), str) and str(c.get('category')).strip() [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357428781-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357431041-05:00 _period_lbl = 'This Week' if _is_weekly else 'Today' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357433291-05:00 if _is_weekly and weekly_cat_totals: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357435671-05:00 # Legacy notebook path passes pre-accumulated weekly totals verbatim. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357437931-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357440120-05:00 "WEEKLY Category Totals — USE THESE EXACT NUMBERS in Section 3 table.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357443340-05:00 "DO NOT invent or estimate different values. These are computed from actual data.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357445480-05:00 f" {'Category':<30} {'This Week':>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357447630-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357449790-05:00 for cat, week_rev in sorted(weekly_cat_totals.items(), [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357452000-05:00 key=lambda x: -x[1]): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357454490-05:00 if not isinstance(cat, str) or not cat.strip(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357459970-05:00 continue [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357462510-05:00 avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357464750-05:00 if avg > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357466990-05:00 pct = (week_rev - avg) / avg * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357469120-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357471450-05:00 f" {cat:<30} ${week_rev:>10,.0f} ${avg:>10,.0f}/day {pct:>+7.1f}%") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357473970-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357476110-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357478490-05:00 f" {cat:<30} ${week_rev:>10,.0f} (no baseline)") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357480810-05:00 category_baselines_str = "\n".join(_cb_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357483020-05:00 elif _cat_mix_rows: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357485340-05:00 _classified = sum((c.get('revenue') or 0) for c in _cat_mix_rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357487380-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357492350-05:00 f"Per-category inside revenue ({_period_lbl}) — USE THESE EXACT NUMBERS " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357495200-05:00 f"in the Section 3 category table. They are computed from the owner's " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357497620-05:00 f"merchandise-code taxonomy and already sum to inside sales " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357499880-05:00 f"(${_classified:,.0f}). Only the row explicitly labelled " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357502130-05:00 f"'Other / Unclassified' is unclassified — do NOT move any other " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357504250-05:00 f"category's revenue into 'Other', and do NOT invent different values.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357506720-05:00 f" {'Category':<30} {_period_lbl:>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357509050-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357511300-05:00 for c in sorted(_cat_mix_rows, key=lambda x: -(x.get('revenue') or 0)): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357514930-05:00 cat = str(c['category']) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357517390-05:00 rev = c.get('revenue') or 0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357519650-05:00 > avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357521880-05:00 ^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357524060-05:00 E AttributeError: 'NoneType' object has no attribute 'get' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357526000-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357528480-05:00 src/storeminder/agents/synthesis_agent.py:1426: AttributeError@pytest.fixture(scope="module") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357530690-05:00 def weekly_report(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357532990-05:00 > return _run("weekly") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357535360-05:00 ^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357537410-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357539610-05:00 tests/test_report_traits_live.py:99: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357542620-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357544870-05:00 tests/test_report_traits_live.py:75: in _run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357547010-05:00 res = fn(store, end_date=d, dry_run=True, deliver=False, clean=True) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357549320-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357551470-05:00 src/storeminder/runner.py:359: in run_weekly [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357557520-05:00 return _run_report(params, clean=clean) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357560040-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357562170-05:00 src/storeminder/runner.py:193: in _run_report [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357564720-05:00 result = run_pipeline( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357567080-05:00 src/storeminder/crew.py:456: in run_pipeline [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357569270-05:00 report_result = _agent.run_weekly( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357571610-05:00 src/storeminder/agents/synthesis_agent.py:1028: in run_weekly [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357574270-05:00 return self.run(report_type="weekly", **kwargs) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357576360-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357578780-05:00 src/storeminder/agents/synthesis_agent.py:1073: in run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357580910-05:00 user = self._build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357583460-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357585390-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357587570-05:00 self = <storeminder.agents.synthesis_agent.SynthesisAgent object at 0x749b3d120d50> [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357589990-05:00 report_date = datetime.date(2026, 7, 10), store_id = 'Fastime' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357592420-05:00 trans = {'total_transactions': 618, 'open_hour_start': 4, 'open_hour_end': 21, 'report_date_filter': '2026-07-10', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357594890-05:00 scan = {'total_scan_rows': 1940, 'report_date_filter': '2026-07-10', 'total_sales': 6436.97, 'avg_item_price': 3.88, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357597510-05:00 baseline = {'day_of_week': 21, 'total_revenue': 100081.6, 'total_transactions': 3568.94, 'fuel_gallons': 16961.39, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357600330-05:00 flags = [{'metric': 'rank_drop', 'label': 'Rank Drop: COPENHAGEN FINE CUT', 'today': 40, 'baseline': 13, ...}, {'metric': 'ran...': 29, ...}, {'metric': 'rank_drop', 'label': 'Rank Drop: BUD LIGHT BEER 12 O', 'today': 33, 'baseline': 22, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357602770-05:00 fuel_section = '## 2. Fuel \n**2,583 gal | $9,446 | 279 fuel transactions** \nFuel volume came in at 2,583 gallons, down from 2,758...nfirm pump access and functionality during 22:00–23:00 if store is open | Measure: Fuel dispensed in evening gap hours' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357605720-05:00 inside_section = '## 3. Inside Sales\n\n**Inside Sales: $6,437 (+9.5% vs normal Tuesday)** \nTobacco / Nicotine surged on strong Marlb...EGG ↑14, ADD CHEESE ↑8) | Because: Modifier use rising but Plate Lunch down 60% | Measure: Attach rate per food ticket' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357608070-05:00 weather = {'yesterday': {'temp_max': 88.3, 'temp_min': 75.4, 'precip_in': 0.0, 'conditions': 'Clear'}, 'today_forecast': {'temp_...cloudy'}, 'rain_hours_today': [], 'weather_impact_assessment': 'Low — weather was mild, not a significant factor', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357612100-05:00 external_section = '## External Context\n\n**Google Reviews (5 reviews)** \nAverage rating of recent reviews (3.0/5) is below store’s ov...- Verify staff scheduling: confirm Luna’s shift coverage and retrain team on food handling consistency — Do by 3:00 PM' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357615150-05:00 loyalty_section = '## Loyalty & Promotions\n\n**Redemption Summary**\n| Promotion | Item | Today | Period Avg | Delta | Status |\n|-----...te and consider extending top performers like *Red Bull Original* and *Doritos Nacho Cheese* — evaluate by end of week' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357617430-05:00 ops_section = '## 5. Operations\n\n**Transactions & Throughput** \n962 total transactions | Avg ticket: $17.10 (+12% vs normal Frid... daily early fuel/retail volume.* \n\nNo additional hours recommended — optimize existing ownership and shift timing.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357619920-05:00 fuel_pricing_section = '## Fuel Pricing\n\n**Current Market** \n| Station | Regular | Midgrade | Premium | Diesel | Distance | \n|---------...y 8:00 AM tomorrow (2026-07-11). \n- Monitor Murphy USA and FASTIME for updates; both changed prices in last 3 hours.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357625290-05:00 prior_actions = None, report_type = 'weekly' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357627940-05:00 weekly_days = [{'date': '2026-07-04', 'dow_name': 'Saturday', 'total_revenue': 7392.12, 'fuel_gallons': 1142.1, ...}, {'date': '2026...42.4, ...}, {'date': '2026-07-09', 'dow_name': 'Thursday', 'total_revenue': 16227.6, 'fuel_gallons': 2780.6, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357630240-05:00 dow_profile = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357632640-05:00 kwargs = {'category_baselines': None, 'schedule_section': None, 'weekly_category_totals': None, 'ext_fetch_date': '2026-07-12', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357634640-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357636820-05:00 def _build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357639020-05:00 self, report_date, store_id, trans, scan, baseline, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357641240-05:00 flags, fuel_section, inside_section, weather, external_section, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357643340-05:00 loyalty_section, ops_section, fuel_pricing_section, prior_actions, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357645490-05:00 report_type: str = "daily", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357647810-05:00 weekly_days: Optional[List[Dict]] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357650170-05:00 dow_profile: Optional[Dict] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357652260-05:00 **kwargs, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357654620-05:00 ) -> str: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357656960-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357659240-05:00 # ── KPI table ───────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357662000-05:00 kpi_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357664280-05:00 "| Metric | Today | Baseline | Delta | Priority |", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357666960-05:00 "|--------|-------|----------|-------|----------|", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357669470-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357671550-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357673760-05:00 # Keys that live in scan_summary (inside sales) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357676750-05:00 _scan_keys = {'total_sales', 'avg_item_price', 'total_scan_rows'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357679060-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357681450-05:00 def kpi_row(label, key, fmt_str=",.0f", prefix="", priority=None): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357683660-05:00 # Prefer scan for inside-sales metrics, trans for everything else [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357685780-05:00 if key in _scan_keys: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357688140-05:00 t = scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357690420-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357692640-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357695050-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357697150-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357700070-05:00 return [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357702370-05:00 t_str = f"{prefix}{t:{fmt_str}}" if isinstance(t, (int, float)) else str(t) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357704750-05:00 b_str = f"{prefix}{b:{fmt_str}}" if isinstance(b, (int, float)) else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357708130-05:00 delta = f"{((t-b)/b*100):+.1f}%" if isinstance(t,(int,float)) and isinstance(b,(int,float)) and b else "—" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357710400-05:00 # Priority: use flag severity if exists, else spec default [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357712610-05:00 flag = next((f for f in flags if f['metric'] == key), None) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357718710-05:00 pri = flag['severity'] if flag else (priority or "—") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357721160-05:00 kpi_lines.append(f"| {label} | {t_str} | {b_str} | {delta} | {pri} |") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357724189-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357726389-05:00 kpi_row("Total Transactions", "total_transactions", ",.0f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357729349-05:00 kpi_row("Total Sales", "total_revenue", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357731949-05:00 kpi_row("Inside Sales", "total_sales", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357734059-05:00 kpi_row("Avg Inside Ticket", "avg_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357736369-05:00 kpi_row("Avg Fuel Ticket", "avg_fuel_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357739319-05:00 kpi_row("Fuel Gallons", "fuel_gallons", ",.1f", "", "HIGH") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357741569-05:00 kpi_row("Fuel Sales $", "fuel_sales", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357747049-05:00 kpi_row("Fuel Transactions", "fuel_transactions", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357749409-05:00 kpi_row("Avg Gallons/Tx", "avg_gallons_per_tx", ",.2f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357751749-05:00 kpi_row("Forecourt Conv %", "forecourt_conversion_pct", ",.1f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357754389-05:00 kpi_row("Void Count", "void_count", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357756869-05:00 kpi_row("Void Total $", "void_total_amt", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357759269-05:00 kpi_row("Inside/100 Gal (weekly avg)", "inside_per_100_gal", ",.0f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357761449-05:00 kpi_row("Eff. Fuel Price", "effective_fuel_price", ",.3f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357763999-05:00 kpi_row("Inside Share %", "inside_sales_share_pct", ",.1f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357766569-05:00 kpi_summary = "\n".join(kpi_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357768749-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357771549-05:00 # ── Derived metrics for snapshot header ──────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357773779-05:00 def _dm(key, label, prefix="", suffix="", decimals=2): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357776429-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357779259-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357781699-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357783879-05:00 return None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357786059-05:00 t_str = f"{prefix}{float(t):,.{decimals}f}{suffix}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357788299-05:00 if b and float(b) > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357790539-05:00 pct = (float(t) - float(b)) / float(b) * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357792799-05:00 return f"{label}: {t_str} ({pct:+.1f}% vs baseline)" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357794879-05:00 return f"{label}: {t_str}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357797179-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357799509-05:00 dm_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357801729-05:00 v = _dm('inside_per_100_gal', 'Inside/100gal', '$', '', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357803959-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357806209-05:00 v = _dm('effective_fuel_price', 'Effective fuel price', '$', '/gal', 3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357811809-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357814389-05:00 v = _dm('inside_sales_share_pct','Inside share of sales', '', '%', 1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357816639-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357818749-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357820989-05:00 gap = trans.get('fuel_inside_gap_pct') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357823159-05:00 if gap is not None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357825369-05:00 direction = "FUEL OUTPACING INSIDE" if gap > 0 else "INSIDE OUTPACING FUEL" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357827689-05:00 dm_lines.append(f"Fuel-vs-Inside gap: {gap:+.1f}pp ({direction})") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357829919-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357832389-05:00 derived_metrics = "\n".join(dm_lines) if dm_lines else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357834539-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357836729-05:00 # ── Flags summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357839049-05:00 if flags: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357841309-05:00 flags_str = "\n".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357843559-05:00 f"- [{f['severity']}] {f['label']}: {f['note']}" for f in flags) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357845769-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357848029-05:00 flags_str = "No significant anomalies detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357850269-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357852669-05:00 # ── Shift summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357854989-05:00 shifts = trans.get('shift_summary', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357857259-05:00 if shifts: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357859399-05:00 shift_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357861669-05:00 for name, data in shifts.items(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357864069-05:00 rev = data.get('revenue', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357866279-05:00 tx = data.get('transactions', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357868419-05:00 gals = data.get('fuel_gallons', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357870689-05:00 shift_lines.append(f"- {name}: ${rev:,.2f} inside | {tx} tx | {gals:,.1f} gal fuel") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357874269-05:00 shift_summary = "\n".join(shift_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357876609-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357878719-05:00 shift_summary = "Shift data not available." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357880829-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357883139-05:00 # ── Gap / zero-tx hours ─────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357885439-05:00 gaps = trans.get('top_hourly_gaps', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357887809-05:00 zero_tx = trans.get('zero_tx_hours', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357890389-05:00 gap_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357892959-05:00 if gaps: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357895069-05:00 gap_str = ", ".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357897299-05:00 "{:02d}:00 ({} tx)".format(g['_hour'], g['tx_count']) for g in gaps) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357899559-05:00 gap_lines.append(f"Top gap hours: {gap_str}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357901669-05:00 if zero_tx: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357903849-05:00 gap_lines.append(f"Zero-tx hours (open window): {zero_tx}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357906079-05:00 gap_summary = "\n".join(gap_lines) if gap_lines else "No unusual gaps detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357910969-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357913479-05:00 # Authoritative store hours (issue #128): when Google Places supplies the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357915799-05:00 # store's real hours, prepend them and instruct the model to treat [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357929449-05:00 # off-hours zero activity as expected closure rather than hedging. The [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357934949-05:00 # gap/zero-tx figures above are already computed against this same window. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357937519-05:00 store_hours_val = kwargs.get('store_hours') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357939969-05:00 if store_hours_val and store_hours_val.get('weekday_text'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357942109-05:00 hrs_lines = ["AUTHORITATIVE STORE HOURS (Google Places — treat as ground truth):"] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357944369-05:00 hrs_lines += [f" {ln}" for ln in store_hours_val['weekday_text']] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357946729-05:00 if store_hours_val.get('open_24h'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357948899-05:00 hrs_lines.append(" This store operates 24 hours on at least one day.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357951039-05:00 hrs_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357953229-05:00 "Zero-transaction hours OUTSIDE these posted hours are EXPECTED " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357955829-05:00 "CLOSURE — do NOT flag them as gaps and do NOT hedge about whether " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357958259-05:00 "the store was open. Treat zero/low activity DURING posted open " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357960399-05:00 "hours as a real gap worth investigating.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357962689-05:00 gap_summary = "\n".join(hrs_lines) + "\n\n" + gap_summary [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357965509-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357968909-05:00 # ── Weather ─────────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357971219-05:00 if weather: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357974279-05:00 temp = weather.get('temp', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357976479-05:00 precip = weather.get('precipitation_in', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357978739-05:00 conds = list(weather.get('conditions', {}).keys()) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357980989-05:00 impact = weather.get('weather_impact_assessment', '') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357983389-05:00 yd = weather.get('yesterday', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357985559-05:00 td = weather.get('today_forecast', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357987959-05:00 rain_hrs= weather.get('rain_hours_today', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357990029-05:00 weather_str = ( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357992219-05:00 f"Report day: {yd.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357994429-05:00 f"{yd.get('temp_min','?')}–{yd.get('temp_max','?')}°F | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357996659-05:00 f"{yd.get('precip_in',0)}\" precip\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.357998809-05:00 f"Today forecast: {td.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358000999-05:00 f"{td.get('temp_min','?')}–{td.get('temp_max','?')}°F\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358003179-05:00 f"Impact assessment: {impact}\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358005619-05:00 + (f"Rain hours: {rain_hrs}\n" if rain_hrs else "") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358007779-05:00 ) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358009998-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358016588-05:00 weather_str = "No weather data provided." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358019798-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358021988-05:00 # ── Schedule section ────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358024138-05:00 schedule_section = kwargs.get('schedule_section', None) or '' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358026238-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358028408-05:00 # ── Category baselines ──────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358030548-05:00 cat_baselines = kwargs.get('category_baselines', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358032748-05:00 # weekly_category_totals: actual accumulated weekly revenue per category [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358034858-05:00 # passed from Cell 39 _week_cat — USE THESE VERBATIM in the report table [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358037068-05:00 weekly_cat_totals = kwargs.get('weekly_category_totals', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358039298-05:00 _is_weekly = (report_type == 'weekly') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358041828-05:00 # Authoritative per-category revenue comes from the owner-mapped [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358044448-05:00 # category_mix (preprocessor). Post-mapping it is aggregated by taxonomy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358047798-05:00 # name and already reconciles to inside sales, so it — NOT the seeded [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358050028-05:00 # baselines — must drive the category table. Previously this block only [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358052388-05:00 # emitted categories that had a seeded history baseline; when baselines [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358054568-05:00 # were sparse or unseeded the model received no authoritative totals and [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358056978-05:00 # dumped the bulk of inside sales into "Other / unclassified" (issue #81). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358059038-05:00 _cat_mix_rows = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358061298-05:00 c for c in scan.get('category_mix', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358063498-05:00 if isinstance(c.get('category'), str) and str(c.get('category')).strip() [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358065758-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358068668-05:00 _period_lbl = 'This Week' if _is_weekly else 'Today' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358070918-05:00 if _is_weekly and weekly_cat_totals: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358073168-05:00 # Legacy notebook path passes pre-accumulated weekly totals verbatim. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358075478-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358077718-05:00 "WEEKLY Category Totals — USE THESE EXACT NUMBERS in Section 3 table.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358089808-05:00 "DO NOT invent or estimate different values. These are computed from actual data.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358094698-05:00 f" {'Category':<30} {'This Week':>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358096918-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358099618-05:00 for cat, week_rev in sorted(weekly_cat_totals.items(), [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358101988-05:00 key=lambda x: -x[1]): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358103688-05:00 if not isinstance(cat, str) or not cat.strip(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358106308-05:00 continue [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358108318-05:00 avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358110108-05:00 if avg > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358111838-05:00 pct = (week_rev - avg) / avg * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358119058-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358124738-05:00 f" {cat:<30} ${week_rev:>10,.0f} ${avg:>10,.0f}/day {pct:>+7.1f}%") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358127228-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358128868-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358131008-05:00 f" {cat:<30} ${week_rev:>10,.0f} (no baseline)") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358132708-05:00 category_baselines_str = "\n".join(_cb_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358134298-05:00 elif _cat_mix_rows: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358136818-05:00 _classified = sum((c.get('revenue') or 0) for c in _cat_mix_rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358138428-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358140108-05:00 f"Per-category inside revenue ({_period_lbl}) — USE THESE EXACT NUMBERS " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358142038-05:00 f"in the Section 3 category table. They are computed from the owner's " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358143848-05:00 f"merchandise-code taxonomy and already sum to inside sales " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358145558-05:00 f"(${_classified:,.0f}). Only the row explicitly labelled " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358147208-05:00 f"'Other / Unclassified' is unclassified — do NOT move any other " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358148948-05:00 f"category's revenue into 'Other', and do NOT invent different values.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358150628-05:00 f" {'Category':<30} {_period_lbl:>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358152228-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358154538-05:00 for c in sorted(_cat_mix_rows, key=lambda x: -(x.get('revenue') or 0)): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358157248-05:00 cat = str(c['category']) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358158908-05:00 rev = c.get('revenue') or 0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358160588-05:00 > avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358162388-05:00 ^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358164288-05:00 E AttributeError: 'NoneType' object has no attribute 'get' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358165818-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358168578-05:00 src/storeminder/agents/synthesis_agent.py:1426: AttributeError@pytest.fixture(scope="module") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358170268-05:00 def weekly_report(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358171858-05:00 > return _run("weekly") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358173558-05:00 ^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358175058-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358176758-05:00 tests/test_report_traits_live.py:99: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358178488-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358180198-05:00 tests/test_report_traits_live.py:75: in _run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358181878-05:00 res = fn(store, end_date=d, dry_run=True, deliver=False, clean=True) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358183538-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358185108-05:00 src/storeminder/runner.py:359: in run_weekly [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358186688-05:00 return _run_report(params, clean=clean) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358188398-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358190048-05:00 src/storeminder/runner.py:193: in _run_report [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358191718-05:00 result = run_pipeline( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358196578-05:00 src/storeminder/crew.py:456: in run_pipeline [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358198338-05:00 report_result = _agent.run_weekly( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358200068-05:00 src/storeminder/agents/synthesis_agent.py:1028: in run_weekly [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358201878-05:00 return self.run(report_type="weekly", **kwargs) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358203978-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358206028-05:00 src/storeminder/agents/synthesis_agent.py:1073: in run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358208118-05:00 user = self._build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358210168-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358212088-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358214168-05:00 self = <storeminder.agents.synthesis_agent.SynthesisAgent object at 0x749b3d120d50> [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358216268-05:00 report_date = datetime.date(2026, 7, 10), store_id = 'Fastime' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358218588-05:00 trans = {'total_transactions': 618, 'open_hour_start': 4, 'open_hour_end': 21, 'report_date_filter': '2026-07-10', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358220688-05:00 scan = {'total_scan_rows': 1940, 'report_date_filter': '2026-07-10', 'total_sales': 6436.97, 'avg_item_price': 3.88, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358222818-05:00 baseline = {'day_of_week': 21, 'total_revenue': 100081.6, 'total_transactions': 3568.94, 'fuel_gallons': 16961.39, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358225228-05:00 flags = [{'metric': 'rank_drop', 'label': 'Rank Drop: COPENHAGEN FINE CUT', 'today': 40, 'baseline': 13, ...}, {'metric': 'ran...': 29, ...}, {'metric': 'rank_drop', 'label': 'Rank Drop: BUD LIGHT BEER 12 O', 'today': 33, 'baseline': 22, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358228088-05:00 fuel_section = '## 2. Fuel \n**2,583 gal | $9,446 | 279 fuel transactions** \nFuel volume came in at 2,583 gallons, down from 2,758...nfirm pump access and functionality during 22:00–23:00 if store is open | Measure: Fuel dispensed in evening gap hours' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358230178-05:00 inside_section = '## 3. Inside Sales\n\n**Inside Sales: $6,437 (+9.5% vs normal Tuesday)** \nTobacco / Nicotine surged on strong Marlb...EGG ↑14, ADD CHEESE ↑8) | Because: Modifier use rising but Plate Lunch down 60% | Measure: Attach rate per food ticket' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358235308-05:00 weather = {'yesterday': {'temp_max': 88.3, 'temp_min': 75.4, 'precip_in': 0.0, 'conditions': 'Clear'}, 'today_forecast': {'temp_...cloudy'}, 'rain_hours_today': [], 'weather_impact_assessment': 'Low — weather was mild, not a significant factor', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358237508-05:00 external_section = '## External Context\n\n**Google Reviews (5 reviews)** \nAverage rating of recent reviews (3.0/5) is below store’s ov...- Verify staff scheduling: confirm Luna’s shift coverage and retrain team on food handling consistency — Do by 3:00 PM' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358240008-05:00 loyalty_section = '## Loyalty & Promotions\n\n**Redemption Summary**\n| Promotion | Item | Today | Period Avg | Delta | Status |\n|-----...te and consider extending top performers like *Red Bull Original* and *Doritos Nacho Cheese* — evaluate by end of week' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358242268-05:00 ops_section = '## 5. Operations\n\n**Transactions & Throughput** \n962 total transactions | Avg ticket: $17.10 (+12% vs normal Frid... daily early fuel/retail volume.* \n\nNo additional hours recommended — optimize existing ownership and shift timing.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358244428-05:00 fuel_pricing_section = '## Fuel Pricing\n\n**Current Market** \n| Station | Regular | Midgrade | Premium | Diesel | Distance | \n|---------...y 8:00 AM tomorrow (2026-07-11). \n- Monitor Murphy USA and FASTIME for updates; both changed prices in last 3 hours.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358246558-05:00 prior_actions = None, report_type = 'weekly' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358251818-05:00 weekly_days = [{'date': '2026-07-04', 'dow_name': 'Saturday', 'total_revenue': 7392.12, 'fuel_gallons': 1142.1, ...}, {'date': '2026...42.4, ...}, {'date': '2026-07-09', 'dow_name': 'Thursday', 'total_revenue': 16227.6, 'fuel_gallons': 2780.6, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358253988-05:00 dow_profile = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358257488-05:00 kwargs = {'category_baselines': None, 'schedule_section': None, 'weekly_category_totals': None, 'ext_fetch_date': '2026-07-12', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358259388-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358261368-05:00 def _build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358263548-05:00 self, report_date, store_id, trans, scan, baseline, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358265678-05:00 flags, fuel_section, inside_section, weather, external_section, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358267768-05:00 loyalty_section, ops_section, fuel_pricing_section, prior_actions, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358270378-05:00 report_type: str = "daily", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358272538-05:00 weekly_days: Optional[List[Dict]] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358275568-05:00 dow_profile: Optional[Dict] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358278498-05:00 **kwargs, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358281758-05:00 ) -> str: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358284838-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358288158-05:00 # ── KPI table ───────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358290868-05:00 kpi_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358294307-05:00 "| Metric | Today | Baseline | Delta | Priority |", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358297617-05:00 "|--------|-------|----------|-------|----------|", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358300567-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358303487-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358306557-05:00 # Keys that live in scan_summary (inside sales) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358309977-05:00 _scan_keys = {'total_sales', 'avg_item_price', 'total_scan_rows'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358313027-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358316017-05:00 def kpi_row(label, key, fmt_str=",.0f", prefix="", priority=None): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358319287-05:00 # Prefer scan for inside-sales metrics, trans for everything else [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358322467-05:00 if key in _scan_keys: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358324617-05:00 t = scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358326687-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358328767-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358331417-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358333947-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358335957-05:00 return [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358338097-05:00 t_str = f"{prefix}{t:{fmt_str}}" if isinstance(t, (int, float)) else str(t) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358340397-05:00 b_str = f"{prefix}{b:{fmt_str}}" if isinstance(b, (int, float)) else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358342507-05:00 delta = f"{((t-b)/b*100):+.1f}%" if isinstance(t,(int,float)) and isinstance(b,(int,float)) and b else "—" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358344587-05:00 # Priority: use flag severity if exists, else spec default [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358346877-05:00 flag = next((f for f in flags if f['metric'] == key), None) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358348967-05:00 pri = flag['severity'] if flag else (priority or "—") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358351007-05:00 kpi_lines.append(f"| {label} | {t_str} | {b_str} | {delta} | {pri} |") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358357887-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358360117-05:00 kpi_row("Total Transactions", "total_transactions", ",.0f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358362187-05:00 kpi_row("Total Sales", "total_revenue", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358364987-05:00 kpi_row("Inside Sales", "total_sales", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358367117-05:00 kpi_row("Avg Inside Ticket", "avg_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358372657-05:00 kpi_row("Avg Fuel Ticket", "avg_fuel_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358374827-05:00 kpi_row("Fuel Gallons", "fuel_gallons", ",.1f", "", "HIGH") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358376887-05:00 kpi_row("Fuel Sales $", "fuel_sales", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358379047-05:00 kpi_row("Fuel Transactions", "fuel_transactions", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358381817-05:00 kpi_row("Avg Gallons/Tx", "avg_gallons_per_tx", ",.2f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358384327-05:00 kpi_row("Forecourt Conv %", "forecourt_conversion_pct", ",.1f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358386387-05:00 kpi_row("Void Count", "void_count", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358388447-05:00 kpi_row("Void Total $", "void_total_amt", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358390697-05:00 kpi_row("Inside/100 Gal (weekly avg)", "inside_per_100_gal", ",.0f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358392747-05:00 kpi_row("Eff. Fuel Price", "effective_fuel_price", ",.3f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358394737-05:00 kpi_row("Inside Share %", "inside_sales_share_pct", ",.1f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358396727-05:00 kpi_summary = "\n".join(kpi_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358398857-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358401077-05:00 # ── Derived metrics for snapshot header ──────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358403767-05:00 def _dm(key, label, prefix="", suffix="", decimals=2): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358406437-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358408527-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358410587-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358412567-05:00 return None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358414647-05:00 t_str = f"{prefix}{float(t):,.{decimals}f}{suffix}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358416717-05:00 if b and float(b) > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358419417-05:00 pct = (float(t) - float(b)) / float(b) * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358421477-05:00 return f"{label}: {t_str} ({pct:+.1f}% vs baseline)" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358423517-05:00 return f"{label}: {t_str}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358425557-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358427627-05:00 dm_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358429677-05:00 v = _dm('inside_per_100_gal', 'Inside/100gal', '$', '', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358431647-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358433697-05:00 v = _dm('effective_fuel_price', 'Effective fuel price', '$', '/gal', 3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358435667-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358437747-05:00 v = _dm('inside_sales_share_pct','Inside share of sales', '', '%', 1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358443457-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358445597-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358448277-05:00 gap = trans.get('fuel_inside_gap_pct') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358451067-05:00 if gap is not None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358453197-05:00 direction = "FUEL OUTPACING INSIDE" if gap > 0 else "INSIDE OUTPACING FUEL" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358455277-05:00 dm_lines.append(f"Fuel-vs-Inside gap: {gap:+.1f}pp ({direction})") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358457277-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358459397-05:00 derived_metrics = "\n".join(dm_lines) if dm_lines else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358461387-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358463907-05:00 # ── Flags summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358465947-05:00 if flags: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358468007-05:00 flags_str = "\n".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358470087-05:00 f"- [{f['severity']}] {f['label']}: {f['note']}" for f in flags) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358472197-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358474697-05:00 flags_str = "No significant anomalies detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358476927-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358479057-05:00 # ── Shift summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358481727-05:00 shifts = trans.get('shift_summary', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358483837-05:00 if shifts: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358485877-05:00 shift_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358487877-05:00 for name, data in shifts.items(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358489927-05:00 rev = data.get('revenue', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358492007-05:00 tx = data.get('transactions', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358493977-05:00 gals = data.get('fuel_gallons', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358496067-05:00 shift_lines.append(f"- {name}: ${rev:,.2f} inside | {tx} tx | {gals:,.1f} gal fuel") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358498187-05:00 shift_summary = "\n".join(shift_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358500217-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358502307-05:00 shift_summary = "Shift data not available." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358504287-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358506377-05:00 # ── Gap / zero-tx hours ─────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358508447-05:00 gaps = trans.get('top_hourly_gaps', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358510447-05:00 zero_tx = trans.get('zero_tx_hours', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358512507-05:00 gap_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358514587-05:00 if gaps: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358516617-05:00 gap_str = ", ".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358518707-05:00 "{:02d}:00 ({} tx)".format(g['_hour'], g['tx_count']) for g in gaps) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358520887-05:00 gap_lines.append(f"Top gap hours: {gap_str}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358522957-05:00 if zero_tx: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358527367-05:00 gap_lines.append(f"Zero-tx hours (open window): {zero_tx}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358529527-05:00 gap_summary = "\n".join(gap_lines) if gap_lines else "No unusual gaps detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358531617-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358533817-05:00 # Authoritative store hours (issue #128): when Google Places supplies the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358538977-05:00 # store's real hours, prepend them and instruct the model to treat [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358541077-05:00 # off-hours zero activity as expected closure rather than hedging. The [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358543137-05:00 # gap/zero-tx figures above are already computed against this same window. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358545157-05:00 store_hours_val = kwargs.get('store_hours') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358547227-05:00 if store_hours_val and store_hours_val.get('weekday_text'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358549677-05:00 hrs_lines = ["AUTHORITATIVE STORE HOURS (Google Places — treat as ground truth):"] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358551867-05:00 hrs_lines += [f" {ln}" for ln in store_hours_val['weekday_text']] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358553957-05:00 if store_hours_val.get('open_24h'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358556027-05:00 hrs_lines.append(" This store operates 24 hours on at least one day.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358558017-05:00 hrs_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358560627-05:00 "Zero-transaction hours OUTSIDE these posted hours are EXPECTED " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358562657-05:00 "CLOSURE — do NOT flag them as gaps and do NOT hedge about whether " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358564807-05:00 "the store was open. Treat zero/low activity DURING posted open " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358566877-05:00 "hours as a real gap worth investigating.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358569617-05:00 gap_summary = "\n".join(hrs_lines) + "\n\n" + gap_summary [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358572287-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358574577-05:00 # ── Weather ─────────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358576657-05:00 if weather: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358578887-05:00 temp = weather.get('temp', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358580996-05:00 precip = weather.get('precipitation_in', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358583036-05:00 conds = list(weather.get('conditions', {}).keys()) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358585096-05:00 impact = weather.get('weather_impact_assessment', '') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358587176-05:00 yd = weather.get('yesterday', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358589246-05:00 td = weather.get('today_forecast', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358591236-05:00 rain_hrs= weather.get('rain_hours_today', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358593216-05:00 weather_str = ( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358595356-05:00 f"Report day: {yd.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358597396-05:00 f"{yd.get('temp_min','?')}–{yd.get('temp_max','?')}°F | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358599426-05:00 f"{yd.get('precip_in',0)}\" precip\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358601496-05:00 f"Today forecast: {td.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358603586-05:00 f"{td.get('temp_min','?')}–{td.get('temp_max','?')}°F\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358605646-05:00 f"Impact assessment: {impact}\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358607696-05:00 + (f"Rain hours: {rain_hrs}\n" if rain_hrs else "") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358609676-05:00 ) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358611696-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358613636-05:00 weather_str = "No weather data provided." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358615696-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358617976-05:00 # ── Schedule section ────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358623786-05:00 schedule_section = kwargs.get('schedule_section', None) or '' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358626096-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358628256-05:00 # ── Category baselines ──────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358630296-05:00 cat_baselines = kwargs.get('category_baselines', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358632376-05:00 # weekly_category_totals: actual accumulated weekly revenue per category [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358634406-05:00 # passed from Cell 39 _week_cat — USE THESE VERBATIM in the report table [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358636446-05:00 weekly_cat_totals = kwargs.get('weekly_category_totals', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358638436-05:00 _is_weekly = (report_type == 'weekly') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358640496-05:00 # Authoritative per-category revenue comes from the owner-mapped [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358642546-05:00 # category_mix (preprocessor). Post-mapping it is aggregated by taxonomy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358644566-05:00 # name and already reconciles to inside sales, so it — NOT the seeded [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358646566-05:00 # baselines — must drive the category table. Previously this block only [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358648606-05:00 # emitted categories that had a seeded history baseline; when baselines [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358650626-05:00 # were sparse or unseeded the model received no authoritative totals and [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358653436-05:00 # dumped the bulk of inside sales into "Other / unclassified" (issue #81). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358655406-05:00 _cat_mix_rows = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358657496-05:00 c for c in scan.get('category_mix', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358659536-05:00 if isinstance(c.get('category'), str) and str(c.get('category')).strip() [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358661516-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358665866-05:00 _period_lbl = 'This Week' if _is_weekly else 'Today' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358668046-05:00 if _is_weekly and weekly_cat_totals: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358670066-05:00 # Legacy notebook path passes pre-accumulated weekly totals verbatim. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358672166-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358674276-05:00 "WEEKLY Category Totals — USE THESE EXACT NUMBERS in Section 3 table.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358676376-05:00 "DO NOT invent or estimate different values. These are computed from actual data.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358678596-05:00 f" {'Category':<30} {'This Week':>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358680586-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358682666-05:00 for cat, week_rev in sorted(weekly_cat_totals.items(), [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358684766-05:00 key=lambda x: -x[1]): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358686856-05:00 if not isinstance(cat, str) or not cat.strip(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358688906-05:00 continue [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358690986-05:00 avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358692986-05:00 if avg > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358695506-05:00 pct = (week_rev - avg) / avg * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358697606-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358699686-05:00 f" {cat:<30} ${week_rev:>10,.0f} ${avg:>10,.0f}/day {pct:>+7.1f}%") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358702196-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358708776-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358710906-05:00 f" {cat:<30} ${week_rev:>10,.0f} (no baseline)") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358713006-05:00 category_baselines_str = "\n".join(_cb_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358715726-05:00 elif _cat_mix_rows: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358717826-05:00 _classified = sum((c.get('revenue') or 0) for c in _cat_mix_rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358719906-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358721956-05:00 f"Per-category inside revenue ({_period_lbl}) — USE THESE EXACT NUMBERS " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358724086-05:00 f"in the Section 3 category table. They are computed from the owner's " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358726126-05:00 f"merchandise-code taxonomy and already sum to inside sales " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358728136-05:00 f"(${_classified:,.0f}). Only the row explicitly labelled " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358730236-05:00 f"'Other / Unclassified' is unclassified — do NOT move any other " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358732276-05:00 f"category's revenue into 'Other', and do NOT invent different values.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358734386-05:00 f" {'Category':<30} {_period_lbl:>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358736466-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358739066-05:00 for c in sorted(_cat_mix_rows, key=lambda x: -(x.get('revenue') or 0)): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358741626-05:00 cat = str(c['category']) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358743606-05:00 rev = c.get('revenue') or 0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358745616-05:00 > avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358747716-05:00 ^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358749806-05:00 E AttributeError: 'NoneType' object has no attribute 'get' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358751726-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358753856-05:00 src/storeminder/agents/synthesis_agent.py:1426: AttributeError@pytest.fixture(scope="module") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358756576-05:00 def weekly_report(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358758566-05:00 > return _run("weekly") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358760566-05:00 ^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358762466-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358764436-05:00 tests/test_report_traits_live.py:99: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358766586-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358768686-05:00 tests/test_report_traits_live.py:75: in _run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358771356-05:00 res = fn(store, end_date=d, dry_run=True, deliver=False, clean=True) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358773476-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358775566-05:00 src/storeminder/runner.py:359: in run_weekly [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358777546-05:00 return _run_report(params, clean=clean) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358780086-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358782086-05:00 src/storeminder/runner.py:193: in _run_report [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358784066-05:00 result = run_pipeline( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358786156-05:00 src/storeminder/crew.py:456: in run_pipeline [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358788096-05:00 report_result = _agent.run_weekly( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358790116-05:00 src/storeminder/agents/synthesis_agent.py:1028: in run_weekly [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358796656-05:00 return self.run(report_type="weekly", **kwargs) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358798676-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358800646-05:00 src/storeminder/agents/synthesis_agent.py:1073: in run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358802636-05:00 user = self._build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358804616-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358806516-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358808566-05:00 self = <storeminder.agents.synthesis_agent.SynthesisAgent object at 0x749b3d120d50> [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358810606-05:00 report_date = datetime.date(2026, 7, 10), store_id = 'Fastime' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358812736-05:00 trans = {'total_transactions': 618, 'open_hour_start': 4, 'open_hour_end': 21, 'report_date_filter': '2026-07-10', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358815246-05:00 scan = {'total_scan_rows': 1940, 'report_date_filter': '2026-07-10', 'total_sales': 6436.97, 'avg_item_price': 3.88, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358819926-05:00 baseline = {'day_of_week': 21, 'total_revenue': 100081.6, 'total_transactions': 3568.94, 'fuel_gallons': 16961.39, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358822116-05:00 flags = [{'metric': 'rank_drop', 'label': 'Rank Drop: COPENHAGEN FINE CUT', 'today': 40, 'baseline': 13, ...}, {'metric': 'ran...': 29, ...}, {'metric': 'rank_drop', 'label': 'Rank Drop: BUD LIGHT BEER 12 O', 'today': 33, 'baseline': 22, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358824226-05:00 fuel_section = '## 2. Fuel \n**2,583 gal | $9,446 | 279 fuel transactions** \nFuel volume came in at 2,583 gallons, down from 2,758...nfirm pump access and functionality during 22:00–23:00 if store is open | Measure: Fuel dispensed in evening gap hours' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358826376-05:00 inside_section = '## 3. Inside Sales\n\n**Inside Sales: $6,437 (+9.5% vs normal Tuesday)** \nTobacco / Nicotine surged on strong Marlb...EGG ↑14, ADD CHEESE ↑8) | Because: Modifier use rising but Plate Lunch down 60% | Measure: Attach rate per food ticket' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358828466-05:00 weather = {'yesterday': {'temp_max': 88.3, 'temp_min': 75.4, 'precip_in': 0.0, 'conditions': 'Clear'}, 'today_forecast': {'temp_...cloudy'}, 'rain_hours_today': [], 'weather_impact_assessment': 'Low — weather was mild, not a significant factor', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358830486-05:00 external_section = '## External Context\n\n**Google Reviews (5 reviews)** \nAverage rating of recent reviews (3.0/5) is below store’s ov...- Verify staff scheduling: confirm Luna’s shift coverage and retrain team on food handling consistency — Do by 3:00 PM' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358832556-05:00 loyalty_section = '## Loyalty & Promotions\n\n**Redemption Summary**\n| Promotion | Item | Today | Period Avg | Delta | Status |\n|-----...te and consider extending top performers like *Red Bull Original* and *Doritos Nacho Cheese* — evaluate by end of week' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358835256-05:00 ops_section = '## 5. Operations\n\n**Transactions & Throughput** \n962 total transactions | Avg ticket: $17.10 (+12% vs normal Frid... daily early fuel/retail volume.* \n\nNo additional hours recommended — optimize existing ownership and shift timing.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358837426-05:00 fuel_pricing_section = '## Fuel Pricing\n\n**Current Market** \n| Station | Regular | Midgrade | Premium | Diesel | Distance | \n|---------...y 8:00 AM tomorrow (2026-07-11). \n- Monitor Murphy USA and FASTIME for updates; both changed prices in last 3 hours.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358839436-05:00 prior_actions = None, report_type = 'weekly' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358841576-05:00 weekly_days = [{'date': '2026-07-04', 'dow_name': 'Saturday', 'total_revenue': 7392.12, 'fuel_gallons': 1142.1, ...}, {'date': '2026...42.4, ...}, {'date': '2026-07-09', 'dow_name': 'Thursday', 'total_revenue': 16227.6, 'fuel_gallons': 2780.6, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358847366-05:00 dow_profile = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358849556-05:00 kwargs = {'category_baselines': None, 'schedule_section': None, 'weekly_category_totals': None, 'ext_fetch_date': '2026-07-12', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358851466-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358853446-05:00 def _build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358855626-05:00 self, report_date, store_id, trans, scan, baseline, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358858216-05:00 flags, fuel_section, inside_section, weather, external_section, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358860396-05:00 loyalty_section, ops_section, fuel_pricing_section, prior_actions, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358862426-05:00 report_type: str = "daily", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358864436-05:00 weekly_days: Optional[List[Dict]] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358866365-05:00 dow_profile: Optional[Dict] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358868465-05:00 **kwargs, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358870485-05:00 ) -> str: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358872455-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358874755-05:00 # ── KPI table ───────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358876765-05:00 kpi_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358878865-05:00 "| Metric | Today | Baseline | Delta | Priority |", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358880915-05:00 "|--------|-------|----------|-------|----------|", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358882895-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358884995-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358887105-05:00 # Keys that live in scan_summary (inside sales) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358889145-05:00 _scan_keys = {'total_sales', 'avg_item_price', 'total_scan_rows'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358891305-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358893355-05:00 def kpi_row(label, key, fmt_str=",.0f", prefix="", priority=None): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358895505-05:00 # Prefer scan for inside-sales metrics, trans for everything else [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358897475-05:00 if key in _scan_keys: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358899475-05:00 t = scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358901495-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358903535-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358905665-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358907945-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358909945-05:00 return [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358911985-05:00 t_str = f"{prefix}{t:{fmt_str}}" if isinstance(t, (int, float)) else str(t) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358916285-05:00 b_str = f"{prefix}{b:{fmt_str}}" if isinstance(b, (int, float)) else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358929795-05:00 delta = f"{((t-b)/b*100):+.1f}%" if isinstance(t,(int,float)) and isinstance(b,(int,float)) and b else "—" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358934985-05:00 # Priority: use flag severity if exists, else spec default [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358937345-05:00 flag = next((f for f in flags if f['metric'] == key), None) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358939445-05:00 pri = flag['severity'] if flag else (priority or "—") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358942945-05:00 kpi_lines.append(f"| {label} | {t_str} | {b_str} | {delta} | {pri} |") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358945125-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358947195-05:00 kpi_row("Total Transactions", "total_transactions", ",.0f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358949775-05:00 kpi_row("Total Sales", "total_revenue", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358958345-05:00 kpi_row("Inside Sales", "total_sales", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358960415-05:00 kpi_row("Avg Inside Ticket", "avg_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358962555-05:00 kpi_row("Avg Fuel Ticket", "avg_fuel_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358964655-05:00 kpi_row("Fuel Gallons", "fuel_gallons", ",.1f", "", "HIGH") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358966765-05:00 kpi_row("Fuel Sales $", "fuel_sales", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358968845-05:00 kpi_row("Fuel Transactions", "fuel_transactions", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358970915-05:00 kpi_row("Avg Gallons/Tx", "avg_gallons_per_tx", ",.2f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358973295-05:00 kpi_row("Forecourt Conv %", "forecourt_conversion_pct", ",.1f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358975325-05:00 kpi_row("Void Count", "void_count", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358977355-05:00 kpi_row("Void Total $", "void_total_amt", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358979395-05:00 kpi_row("Inside/100 Gal (weekly avg)", "inside_per_100_gal", ",.0f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358981515-05:00 kpi_row("Eff. Fuel Price", "effective_fuel_price", ",.3f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358983705-05:00 kpi_row("Inside Share %", "inside_sales_share_pct", ",.1f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358985665-05:00 kpi_summary = "\n".join(kpi_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358987635-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358989755-05:00 # ── Derived metrics for snapshot header ──────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358991875-05:00 def _dm(key, label, prefix="", suffix="", decimals=2): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358993965-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358995995-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.358998085-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359000115-05:00 return None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359002145-05:00 t_str = f"{prefix}{float(t):,.{decimals}f}{suffix}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359004095-05:00 if b and float(b) > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359006505-05:00 pct = (float(t) - float(b)) / float(b) * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359008545-05:00 return f"{label}: {t_str} ({pct:+.1f}% vs baseline)" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359010585-05:00 return f"{label}: {t_str}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359012625-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359014745-05:00 dm_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359016855-05:00 v = _dm('inside_per_100_gal', 'Inside/100gal', '$', '', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359018875-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359020925-05:00 v = _dm('effective_fuel_price', 'Effective fuel price', '$', '/gal', 3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359022945-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359024965-05:00 v = _dm('inside_sales_share_pct','Inside share of sales', '', '%', 1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359026995-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359028975-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359031055-05:00 gap = trans.get('fuel_inside_gap_pct') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359033525-05:00 if gap is not None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359038735-05:00 direction = "FUEL OUTPACING INSIDE" if gap > 0 else "INSIDE OUTPACING FUEL" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359041425-05:00 dm_lines.append(f"Fuel-vs-Inside gap: {gap:+.1f}pp ({direction})") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359043495-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359045525-05:00 derived_metrics = "\n".join(dm_lines) if dm_lines else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359047585-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359049715-05:00 # ── Flags summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359051815-05:00 if flags: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359053815-05:00 flags_str = "\n".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359055975-05:00 f"- [{f['severity']}] {f['label']}: {f['note']}" for f in flags) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359058095-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359060255-05:00 flags_str = "No significant anomalies detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359062235-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359064355-05:00 # ── Shift summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359066365-05:00 shifts = trans.get('shift_summary', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359068455-05:00 if shifts: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359070465-05:00 shift_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359072465-05:00 for name, data in shifts.items(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359074735-05:00 rev = data.get('revenue', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359079305-05:00 tx = data.get('transactions', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359081405-05:00 gals = data.get('fuel_gallons', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359084005-05:00 shift_lines.append(f"- {name}: ${rev:,.2f} inside | {tx} tx | {gals:,.1f} gal fuel") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359086185-05:00 shift_summary = "\n".join(shift_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359088295-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359090335-05:00 shift_summary = "Shift data not available." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359092275-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359094445-05:00 # ── Gap / zero-tx hours ─────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359096535-05:00 gaps = trans.get('top_hourly_gaps', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359098545-05:00 zero_tx = trans.get('zero_tx_hours', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359100595-05:00 gap_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359102625-05:00 if gaps: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359104685-05:00 gap_str = ", ".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359106735-05:00 "{:02d}:00 ({} tx)".format(g['_hour'], g['tx_count']) for g in gaps) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359108755-05:00 gap_lines.append(f"Top gap hours: {gap_str}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359110805-05:00 if zero_tx: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359113345-05:00 gap_lines.append(f"Zero-tx hours (open window): {zero_tx}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359115385-05:00 gap_summary = "\n".join(gap_lines) if gap_lines else "No unusual gaps detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359117485-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359119565-05:00 # Authoritative store hours (issue #128): when Google Places supplies the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359121675-05:00 # store's real hours, prepend them and instruct the model to treat [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359123685-05:00 # off-hours zero activity as expected closure rather than hedging. The [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359129095-05:00 # gap/zero-tx figures above are already computed against this same window. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359131235-05:00 store_hours_val = kwargs.get('store_hours') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359133235-05:00 if store_hours_val and store_hours_val.get('weekday_text'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359135265-05:00 hrs_lines = ["AUTHORITATIVE STORE HOURS (Google Places — treat as ground truth):"] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359137315-05:00 hrs_lines += [f" {ln}" for ln in store_hours_val['weekday_text']] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359139394-05:00 if store_hours_val.get('open_24h'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359141454-05:00 hrs_lines.append(" This store operates 24 hours on at least one day.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359143494-05:00 hrs_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359145514-05:00 "Zero-transaction hours OUTSIDE these posted hours are EXPECTED " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359147534-05:00 "CLOSURE — do NOT flag them as gaps and do NOT hedge about whether " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359149594-05:00 "the store was open. Treat zero/low activity DURING posted open " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359151604-05:00 "hours as a real gap worth investigating.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359153644-05:00 gap_summary = "\n".join(hrs_lines) + "\n\n" + gap_summary [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359155654-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359157834-05:00 # ── Weather ─────────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359159854-05:00 if weather: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359161844-05:00 temp = weather.get('temp', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359164124-05:00 precip = weather.get('precipitation_in', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359166114-05:00 conds = list(weather.get('conditions', {}).keys()) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359168124-05:00 impact = weather.get('weather_impact_assessment', '') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359170104-05:00 yd = weather.get('yesterday', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359172204-05:00 td = weather.get('today_forecast', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359174234-05:00 rain_hrs= weather.get('rain_hours_today', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359176224-05:00 weather_str = ( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359178184-05:00 f"Report day: {yd.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359180334-05:00 f"{yd.get('temp_min','?')}–{yd.get('temp_max','?')}°F | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359182374-05:00 f"{yd.get('precip_in',0)}\" precip\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359184244-05:00 f"Today forecast: {td.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359185954-05:00 f"{td.get('temp_min','?')}–{td.get('temp_max','?')}°F\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359187644-05:00 f"Impact assessment: {impact}\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359189314-05:00 + (f"Rain hours: {rain_hrs}\n" if rain_hrs else "") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359190924-05:00 ) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359192504-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359194164-05:00 weather_str = "No weather data provided." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359195744-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359197454-05:00 # ── Schedule section ────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359199134-05:00 schedule_section = kwargs.get('schedule_section', None) or '' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359200724-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359205274-05:00 # ── Category baselines ──────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359207084-05:00 cat_baselines = kwargs.get('category_baselines', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359208704-05:00 # weekly_category_totals: actual accumulated weekly revenue per category [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359210744-05:00 # passed from Cell 39 _week_cat — USE THESE VERBATIM in the report table [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359222204-05:00 weekly_cat_totals = kwargs.get('weekly_category_totals', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359226534-05:00 _is_weekly = (report_type == 'weekly') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359229234-05:00 # Authoritative per-category revenue comes from the owner-mapped [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359231514-05:00 # category_mix (preprocessor). Post-mapping it is aggregated by taxonomy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359233714-05:00 # name and already reconciles to inside sales, so it — NOT the seeded [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359240324-05:00 # baselines — must drive the category table. Previously this block only [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359242664-05:00 # emitted categories that had a seeded history baseline; when baselines [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359245064-05:00 # were sparse or unseeded the model received no authoritative totals and [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359247374-05:00 # dumped the bulk of inside sales into "Other / unclassified" (issue #81). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359249614-05:00 _cat_mix_rows = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359251984-05:00 c for c in scan.get('category_mix', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359254394-05:00 if isinstance(c.get('category'), str) and str(c.get('category')).strip() [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359256634-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359258814-05:00 _period_lbl = 'This Week' if _is_weekly else 'Today' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359261014-05:00 if _is_weekly and weekly_cat_totals: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359263194-05:00 # Legacy notebook path passes pre-accumulated weekly totals verbatim. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359265244-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359267474-05:00 "WEEKLY Category Totals — USE THESE EXACT NUMBERS in Section 3 table.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359269964-05:00 "DO NOT invent or estimate different values. These are computed from actual data.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359272074-05:00 f" {'Category':<30} {'This Week':>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359274234-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359276514-05:00 for cat, week_rev in sorted(weekly_cat_totals.items(), [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359278894-05:00 key=lambda x: -x[1]): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359280974-05:00 if not isinstance(cat, str) or not cat.strip(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359283104-05:00 continue [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359285484-05:00 avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359287554-05:00 if avg > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359289814-05:00 pct = (week_rev - avg) / avg * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359292184-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359294424-05:00 f" {cat:<30} ${week_rev:>10,.0f} ${avg:>10,.0f}/day {pct:>+7.1f}%") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359296634-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359298714-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359300874-05:00 f" {cat:<30} ${week_rev:>10,.0f} (no baseline)") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359303194-05:00 category_baselines_str = "\n".join(_cb_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359311024-05:00 elif _cat_mix_rows: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359314104-05:00 _classified = sum((c.get('revenue') or 0) for c in _cat_mix_rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359316414-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359318574-05:00 f"Per-category inside revenue ({_period_lbl}) — USE THESE EXACT NUMBERS " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359320824-05:00 f"in the Section 3 category table. They are computed from the owner's " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359322984-05:00 f"merchandise-code taxonomy and already sum to inside sales " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359325264-05:00 f"(${_classified:,.0f}). Only the row explicitly labelled " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359327564-05:00 f"'Other / Unclassified' is unclassified — do NOT move any other " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359329964-05:00 f"category's revenue into 'Other', and do NOT invent different values.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359332344-05:00 f" {'Category':<30} {_period_lbl:>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359334794-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359337144-05:00 for c in sorted(_cat_mix_rows, key=lambda x: -(x.get('revenue') or 0)): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359339304-05:00 cat = str(c['category']) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359341334-05:00 rev = c.get('revenue') or 0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359343514-05:00 > avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359346394-05:00 ^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359348694-05:00 E AttributeError: 'NoneType' object has no attribute 'get' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359350854-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359354164-05:00 src/storeminder/agents/synthesis_agent.py:1426: AttributeError@pytest.fixture(scope="module") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359356594-05:00 def weekly_report(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359358784-05:00 > return _run("weekly") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359361064-05:00 ^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359363044-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359365124-05:00 tests/test_report_traits_live.py:99: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359367624-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359369874-05:00 tests/test_report_traits_live.py:75: in _run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359372074-05:00 res = fn(store, end_date=d, dry_run=True, deliver=False, clean=True) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359374174-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359376854-05:00 src/storeminder/runner.py:359: in run_weekly [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359378944-05:00 return _run_report(params, clean=clean) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359381544-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359383694-05:00 src/storeminder/runner.py:193: in _run_report [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359385764-05:00 result = run_pipeline( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359387884-05:00 src/storeminder/crew.py:456: in run_pipeline [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359389904-05:00 report_result = _agent.run_weekly( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359391954-05:00 src/storeminder/agents/synthesis_agent.py:1028: in run_weekly [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359393914-05:00 return self.run(report_type="weekly", **kwargs) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359395894-05:00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359397914-05:00 src/storeminder/agents/synthesis_agent.py:1073: in run [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359403374-05:00 user = self._build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359408983-05:00 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359411263-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359413633-05:00 self = <storeminder.agents.synthesis_agent.SynthesisAgent object at 0x749b3d120d50> [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359415833-05:00 report_date = datetime.date(2026, 7, 10), store_id = 'Fastime' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359418153-05:00 trans = {'total_transactions': 618, 'open_hour_start': 4, 'open_hour_end': 21, 'report_date_filter': '2026-07-10', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359420423-05:00 scan = {'total_scan_rows': 1940, 'report_date_filter': '2026-07-10', 'total_sales': 6436.97, 'avg_item_price': 3.88, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359423383-05:00 baseline = {'day_of_week': 21, 'total_revenue': 100081.6, 'total_transactions': 3568.94, 'fuel_gallons': 16961.39, ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359426043-05:00 flags = [{'metric': 'rank_drop', 'label': 'Rank Drop: COPENHAGEN FINE CUT', 'today': 40, 'baseline': 13, ...}, {'metric': 'ran...': 29, ...}, {'metric': 'rank_drop', 'label': 'Rank Drop: BUD LIGHT BEER 12 O', 'today': 33, 'baseline': 22, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359429193-05:00 fuel_section = '## 2. Fuel \n**2,583 gal | $9,446 | 279 fuel transactions** \nFuel volume came in at 2,583 gallons, down from 2,758...nfirm pump access and functionality during 22:00–23:00 if store is open | Measure: Fuel dispensed in evening gap hours' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359431483-05:00 inside_section = '## 3. Inside Sales\n\n**Inside Sales: $6,437 (+9.5% vs normal Tuesday)** \nTobacco / Nicotine surged on strong Marlb...EGG ↑14, ADD CHEESE ↑8) | Because: Modifier use rising but Plate Lunch down 60% | Measure: Attach rate per food ticket' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359433983-05:00 weather = {'yesterday': {'temp_max': 88.3, 'temp_min': 75.4, 'precip_in': 0.0, 'conditions': 'Clear'}, 'today_forecast': {'temp_...cloudy'}, 'rain_hours_today': [], 'weather_impact_assessment': 'Low — weather was mild, not a significant factor', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359436263-05:00 external_section = '## External Context\n\n**Google Reviews (5 reviews)** \nAverage rating of recent reviews (3.0/5) is below store’s ov...- Verify staff scheduling: confirm Luna’s shift coverage and retrain team on food handling consistency — Do by 3:00 PM' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359438753-05:00 loyalty_section = '## Loyalty & Promotions\n\n**Redemption Summary**\n| Promotion | Item | Today | Period Avg | Delta | Status |\n|-----...te and consider extending top performers like *Red Bull Original* and *Doritos Nacho Cheese* — evaluate by end of week' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359441133-05:00 ops_section = '## 5. Operations\n\n**Transactions & Throughput** \n962 total transactions | Avg ticket: $17.10 (+12% vs normal Frid... daily early fuel/retail volume.* \n\nNo additional hours recommended — optimize existing ownership and shift timing.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359443593-05:00 fuel_pricing_section = '## Fuel Pricing\n\n**Current Market** \n| Station | Regular | Midgrade | Premium | Diesel | Distance | \n|---------...y 8:00 AM tomorrow (2026-07-11). \n- Monitor Murphy USA and FASTIME for updates; both changed prices in last 3 hours.' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359445713-05:00 prior_actions = None, report_type = 'weekly' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359448683-05:00 weekly_days = [{'date': '2026-07-04', 'dow_name': 'Saturday', 'total_revenue': 7392.12, 'fuel_gallons': 1142.1, ...}, {'date': '2026...42.4, ...}, {'date': '2026-07-09', 'dow_name': 'Thursday', 'total_revenue': 16227.6, 'fuel_gallons': 2780.6, ...}, ...] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359450803-05:00 dow_profile = None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359453113-05:00 kwargs = {'category_baselines': None, 'schedule_section': None, 'weekly_category_totals': None, 'ext_fetch_date': '2026-07-12', ...} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359460883-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359463193-05:00 def _build_user_prompt( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359465413-05:00 self, report_date, store_id, trans, scan, baseline, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359467673-05:00 flags, fuel_section, inside_section, weather, external_section, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359469763-05:00 loyalty_section, ops_section, fuel_pricing_section, prior_actions, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359471913-05:00 report_type: str = "daily", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359474123-05:00 weekly_days: Optional[List[Dict]] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359476253-05:00 dow_profile: Optional[Dict] = None, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359478463-05:00 **kwargs, [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359480853-05:00 ) -> str: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359483123-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359486153-05:00 # ── KPI table ───────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359488333-05:00 kpi_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359490543-05:00 "| Metric | Today | Baseline | Delta | Priority |", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359492783-05:00 "|--------|-------|----------|-------|----------|", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359495053-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359497203-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359499363-05:00 # Keys that live in scan_summary (inside sales) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359501793-05:00 _scan_keys = {'total_sales', 'avg_item_price', 'total_scan_rows'} [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359503953-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359509213-05:00 def kpi_row(label, key, fmt_str=",.0f", prefix="", priority=None): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359511663-05:00 # Prefer scan for inside-sales metrics, trans for everything else [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359513983-05:00 if key in _scan_keys: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359516123-05:00 t = scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359518203-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359520483-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359522783-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359525013-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359527163-05:00 return [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359529263-05:00 t_str = f"{prefix}{t:{fmt_str}}" if isinstance(t, (int, float)) else str(t) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359531793-05:00 b_str = f"{prefix}{b:{fmt_str}}" if isinstance(b, (int, float)) else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359534073-05:00 delta = f"{((t-b)/b*100):+.1f}%" if isinstance(t,(int,float)) and isinstance(b,(int,float)) and b else "—" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359536473-05:00 # Priority: use flag severity if exists, else spec default [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359538693-05:00 flag = next((f for f in flags if f['metric'] == key), None) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359541133-05:00 pri = flag['severity'] if flag else (priority or "—") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359543293-05:00 kpi_lines.append(f"| {label} | {t_str} | {b_str} | {delta} | {pri} |") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359545443-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359547693-05:00 kpi_row("Total Transactions", "total_transactions", ",.0f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359550323-05:00 kpi_row("Total Sales", "total_revenue", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359552543-05:00 kpi_row("Inside Sales", "total_sales", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359554813-05:00 kpi_row("Avg Inside Ticket", "avg_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359560513-05:00 kpi_row("Avg Fuel Ticket", "avg_fuel_ticket", ",.2f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359562983-05:00 kpi_row("Fuel Gallons", "fuel_gallons", ",.1f", "", "HIGH") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359565703-05:00 kpi_row("Fuel Sales $", "fuel_sales", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359567953-05:00 kpi_row("Fuel Transactions", "fuel_transactions", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359570283-05:00 kpi_row("Avg Gallons/Tx", "avg_gallons_per_tx", ",.2f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359572553-05:00 kpi_row("Forecourt Conv %", "forecourt_conversion_pct", ",.1f", "", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359574763-05:00 kpi_row("Void Count", "void_count", ",.0f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359576913-05:00 kpi_row("Void Total $", "void_total_amt", ",.2f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359579033-05:00 kpi_row("Inside/100 Gal (weekly avg)", "inside_per_100_gal", ",.0f", "$", "MEDIUM") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359581433-05:00 kpi_row("Eff. Fuel Price", "effective_fuel_price", ",.3f", "$", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359583563-05:00 kpi_row("Inside Share %", "inside_sales_share_pct", ",.1f", "", "LOW") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359585783-05:00 kpi_summary = "\n".join(kpi_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359588013-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359590643-05:00 # ── Derived metrics for snapshot header ──────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359592833-05:00 def _dm(key, label, prefix="", suffix="", decimals=2): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359595153-05:00 t = trans.get(key) if trans.get(key) is not None else scan.get(key) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359597333-05:00 b = baseline.get(key) if baseline else None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359599573-05:00 if t is None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359601753-05:00 return None [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359603833-05:00 t_str = f"{prefix}{float(t):,.{decimals}f}{suffix}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359606013-05:00 if b and float(b) > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359608263-05:00 pct = (float(t) - float(b)) / float(b) * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359610773-05:00 return f"{label}: {t_str} ({pct:+.1f}% vs baseline)" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359613033-05:00 return f"{label}: {t_str}" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359615243-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359617503-05:00 dm_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359619743-05:00 v = _dm('inside_per_100_gal', 'Inside/100gal', '$', '', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359621873-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359624203-05:00 v = _dm('effective_fuel_price', 'Effective fuel price', '$', '/gal', 3) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359626513-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359628693-05:00 v = _dm('inside_sales_share_pct','Inside share of sales', '', '%', 1) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359630873-05:00 if v: dm_lines.append(v) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359633803-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359635973-05:00 gap = trans.get('fuel_inside_gap_pct') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359638153-05:00 if gap is not None: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359640413-05:00 direction = "FUEL OUTPACING INSIDE" if gap > 0 else "INSIDE OUTPACING FUEL" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359642613-05:00 dm_lines.append(f"Fuel-vs-Inside gap: {gap:+.1f}pp ({direction})") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359648113-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359650633-05:00 derived_metrics = "\n".join(dm_lines) if dm_lines else "N/A" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359652783-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359655093-05:00 # ── Flags summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359657283-05:00 if flags: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359659413-05:00 flags_str = "\n".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359665283-05:00 f"- [{f['severity']}] {f['label']}: {f['note']}" for f in flags) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359667583-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359671323-05:00 flags_str = "No significant anomalies detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359673623-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359675913-05:00 # ── Shift summary ───────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359678162-05:00 shifts = trans.get('shift_summary', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359680392-05:00 if shifts: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359682562-05:00 shift_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359684782-05:00 for name, data in shifts.items(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359687382-05:00 rev = data.get('revenue', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359689532-05:00 tx = data.get('transactions', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359691792-05:00 gals = data.get('fuel_gallons', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359694122-05:00 shift_lines.append(f"- {name}: ${rev:,.2f} inside | {tx} tx | {gals:,.1f} gal fuel") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359696442-05:00 shift_summary = "\n".join(shift_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359698772-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359701212-05:00 shift_summary = "Shift data not available." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359703452-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359705562-05:00 # ── Gap / zero-tx hours ─────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359708492-05:00 gaps = trans.get('top_hourly_gaps', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359710722-05:00 zero_tx = trans.get('zero_tx_hours', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359712902-05:00 gap_lines = [] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359715232-05:00 if gaps: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359717292-05:00 gap_str = ", ".join( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359719462-05:00 "{:02d}:00 ({} tx)".format(g['_hour'], g['tx_count']) for g in gaps) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359721782-05:00 gap_lines.append(f"Top gap hours: {gap_str}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359723942-05:00 if zero_tx: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359726162-05:00 gap_lines.append(f"Zero-tx hours (open window): {zero_tx}") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359728272-05:00 gap_summary = "\n".join(gap_lines) if gap_lines else "No unusual gaps detected." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359730482-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359732782-05:00 # Authoritative store hours (issue #128): when Google Places supplies the [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359734942-05:00 # store's real hours, prepend them and instruct the model to treat [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359737102-05:00 # off-hours zero activity as expected closure rather than hedging. The [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359739162-05:00 # gap/zero-tx figures above are already computed against this same window. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359741272-05:00 store_hours_val = kwargs.get('store_hours') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359744172-05:00 if store_hours_val and store_hours_val.get('weekday_text'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359750032-05:00 hrs_lines = ["AUTHORITATIVE STORE HOURS (Google Places — treat as ground truth):"] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359752332-05:00 hrs_lines += [f" {ln}" for ln in store_hours_val['weekday_text']] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359754402-05:00 if store_hours_val.get('open_24h'): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359756512-05:00 hrs_lines.append(" This store operates 24 hours on at least one day.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359758662-05:00 hrs_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359760892-05:00 "Zero-transaction hours OUTSIDE these posted hours are EXPECTED " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359763052-05:00 "CLOSURE — do NOT flag them as gaps and do NOT hedge about whether " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359765232-05:00 "the store was open. Treat zero/low activity DURING posted open " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359767322-05:00 "hours as a real gap worth investigating.") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359769462-05:00 gap_summary = "\n".join(hrs_lines) + "\n\n" + gap_summary [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359771652-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359773992-05:00 # ── Weather ─────────────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359776182-05:00 if weather: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359778362-05:00 temp = weather.get('temp', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359780462-05:00 precip = weather.get('precipitation_in', 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359782902-05:00 conds = list(weather.get('conditions', {}).keys()) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359785302-05:00 impact = weather.get('weather_impact_assessment', '') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359787362-05:00 yd = weather.get('yesterday', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359789562-05:00 td = weather.get('today_forecast', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359791692-05:00 rain_hrs= weather.get('rain_hours_today', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359793792-05:00 weather_str = ( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359795872-05:00 f"Report day: {yd.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359798132-05:00 f"{yd.get('temp_min','?')}–{yd.get('temp_max','?')}°F | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359800272-05:00 f"{yd.get('precip_in',0)}\" precip\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359802472-05:00 f"Today forecast: {td.get('conditions','N/A')} | " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359805332-05:00 f"{td.get('temp_min','?')}–{td.get('temp_max','?')}°F\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359807502-05:00 f"Impact assessment: {impact}\n" [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359809932-05:00 + (f"Rain hours: {rain_hrs}\n" if rain_hrs else "") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359812282-05:00 ) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359814462-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359816572-05:00 weather_str = "No weather data provided." [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359818632-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359821182-05:00 # ── Schedule section ────────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359828882-05:00 schedule_section = kwargs.get('schedule_section', None) or '' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359831162-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359833522-05:00 # ── Category baselines ──────────────────────────────────────────────── [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359835802-05:00 cat_baselines = kwargs.get('category_baselines', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359842102-05:00 # weekly_category_totals: actual accumulated weekly revenue per category [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359844942-05:00 # passed from Cell 39 _week_cat — USE THESE VERBATIM in the report table [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359847232-05:00 weekly_cat_totals = kwargs.get('weekly_category_totals', {}) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359850182-05:00 _is_weekly = (report_type == 'weekly') [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359852802-05:00 # Authoritative per-category revenue comes from the owner-mapped [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359855042-05:00 # category_mix (preprocessor). Post-mapping it is aggregated by taxonomy [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359857992-05:00 # name and already reconciles to inside sales, so it — NOT the seeded [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359860342-05:00 # baselines — must drive the category table. Previously this block only [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359862752-05:00 # emitted categories that had a seeded history baseline; when baselines [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359865802-05:00 # were sparse or unseeded the model received no authoritative totals and [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359868022-05:00 # dumped the bulk of inside sales into "Other / unclassified" (issue #81). [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359870142-05:00 _cat_mix_rows = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359872382-05:00 c for c in scan.get('category_mix', []) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359874572-05:00 if isinstance(c.get('category'), str) and str(c.get('category')).strip() [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359876722-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359878802-05:00 _period_lbl = 'This Week' if _is_weekly else 'Today' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359881322-05:00 if _is_weekly and weekly_cat_totals: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359883422-05:00 # Legacy notebook path passes pre-accumulated weekly totals verbatim. [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359885802-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359888012-05:00 "WEEKLY Category Totals — USE THESE EXACT NUMBERS in Section 3 table.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359890222-05:00 "DO NOT invent or estimate different values. These are computed from actual data.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359892432-05:00 f" {'Category':<30} {'This Week':>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359894502-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359896602-05:00 for cat, week_rev in sorted(weekly_cat_totals.items(), [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359898802-05:00 key=lambda x: -x[1]): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359901042-05:00 if not isinstance(cat, str) or not cat.strip(): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359903172-05:00 continue [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359905322-05:00 avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359907522-05:00 if avg > 0: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359909752-05:00 pct = (week_rev - avg) / avg * 100 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359912512-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359914682-05:00 f" {cat:<30} ${week_rev:>10,.0f} ${avg:>10,.0f}/day {pct:>+7.1f}%") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359916922-05:00 else: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359933802-05:00 _cb_lines.append( [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359936692-05:00 f" {cat:<30} ${week_rev:>10,.0f} (no baseline)") [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359938982-05:00 category_baselines_str = "\n".join(_cb_lines) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359941192-05:00 elif _cat_mix_rows: [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359943422-05:00 _classified = sum((c.get('revenue') or 0) for c in _cat_mix_rows) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359950061-05:00 _cb_lines = [ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359952731-05:00 f"Per-category inside revenue ({_period_lbl}) — USE THESE EXACT NUMBERS " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359955561-05:00 f"in the Section 3 category table. They are computed from the owner's " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359957961-05:00 f"merchandise-code taxonomy and already sum to inside sales " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359960151-05:00 f"(${_classified:,.0f}). Only the row explicitly labelled " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359962341-05:00 f"'Other / Unclassified' is unclassified — do NOT move any other " [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359964481-05:00 f"category's revenue into 'Other', and do NOT invent different values.", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359966721-05:00 f" {'Category':<30} {_period_lbl:>12} {'Normal/day':>12} {'Delta':>8}", [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359968891-05:00 ] [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359971091-05:00 for c in sorted(_cat_mix_rows, key=lambda x: -(x.get('revenue') or 0)): [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359973181-05:00 cat = str(c['category']) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359975441-05:00 rev = c.get('revenue') or 0 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359977711-05:00 > avg = cat_baselines.get(cat, 0) [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359979961-05:00 ^^^^^^^^^^^^^^^^^ [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359982211-05:00 E AttributeError: 'NoneType' object has no attribute 'get' [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359984251-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359986401-05:00 src/storeminder/agents/synthesis_agent.py:1426: AttributeError===JUNIT_END=== [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.359988491-05:00 ----- end live Job logs ----- [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/step-run-live] 2026-07-12T18:42:00.408576577-05:00 ==> live build: status=Failed conclusion=failure [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/prepare] 2026-07-12T18:38:54.934639154-05:00 2026/07/12 23:38:54 Entrypoint initialization [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/place-scripts] 2026-07-12T18:38:55.627291330-05:00 2026/07/12 23:38:55 Decoded script /tekton/scripts/script-0-cfsln [pod/bootstrap-pr-report-check-pr250-9fwkh-live-build-pod/place-scripts] 2026-07-12T18:38:55.644043332-05:00 2026/07/12 23:38:55 Decoded script /tekton/scripts/script-2-4n65x ===== bootstrap-pr-report-check-pr250-9fwkh-post-check-live-pod.log ===== [pod/bootstrap-pr-report-check-pr250-9fwkh-post-check-live-pod/place-scripts] 2026-07-12T18:42:07.605490797-05:00 2026/07/12 23:42:07 Decoded script /tekton/scripts/script-0-glb2n [pod/bootstrap-pr-report-check-pr250-9fwkh-post-check-live-pod/step-post-check] 2026-07-12T18:42:11.255754217-05:00 Installing PyJWT + cryptography + requests… [pod/bootstrap-pr-report-check-pr250-9fwkh-post-check-live-pod/prepare] 2026-07-12T18:42:06.060561873-05:00 2026/07/12 23:42:06 Entrypoint initialization ===== bootstrap-pr-report-check-pr250-9fwkh-post-check-pod.log ===== [pod/bootstrap-pr-report-check-pr250-9fwkh-post-check-pod/prepare] 2026-07-12T18:42:04.711588126-05:00 2026/07/12 23:42:04 Entrypoint initialization [pod/bootstrap-pr-report-check-pr250-9fwkh-post-check-pod/place-scripts] 2026-07-12T18:42:05.679435535-05:00 2026/07/12 23:42:05 Decoded script /tekton/scripts/script-0-4mkhz [pod/bootstrap-pr-report-check-pr250-9fwkh-post-check-pod/step-post-check] 2026-07-12T18:42:09.619333261-05:00 Installing PyJWT + cryptography + requests… ===== bootstrap-pr-report-check-pr250-9fwkh-post-check-traits-pod.log ===== [pod/bootstrap-pr-report-check-pr250-9fwkh-post-check-traits-pod/step-post-check] 2026-07-12T18:42:11.215900991-05:00 Installing PyJWT + cryptography + requests… [pod/bootstrap-pr-report-check-pr250-9fwkh-post-check-traits-pod/prepare] 2026-07-12T18:42:05.378947716-05:00 2026/07/12 23:42:05 Entrypoint initialization [pod/bootstrap-pr-report-check-pr250-9fwkh-post-check-traits-pod/place-scripts] 2026-07-12T18:42:07.621403650-05:00 2026/07/12 23:42:07 Decoded script /tekton/scripts/script-0-5lg55 ===== bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod.log ===== [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/prepare] 2026-07-12T18:42:06.049954941-05:00 2026/07/12 23:42:06 Entrypoint initialization [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/place-scripts] 2026-07-12T18:42:07.579104173-05:00 2026/07/12 23:42:07 Decoded script /tekton/scripts/script-0-cp2nh [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:10.975599551-05:00 Posting commit status to GitHub [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:10.975640730-05:00 Repo: storeminder-ai/bootstrap [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:10.975646080-05:00 SHA: d15c4b94b94c40ce1e7fefa13dcc806acd7ccee5 [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:10.975649150-05:00 State: failure (Failed) [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:10.975652790-05:00 Target URL: https://github.com/storeminder-ai/bootstrap/pull/250 [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402115518-05:00 { [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402127638-05:00 "url": "https://api.github.com/repos/storeminder-ai/bootstrap/statuses/d15c4b94b94c40ce1e7fefa13dcc806acd7ccee5", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402136338-05:00 "avatar_url": "https://avatars.githubusercontent.com/u/702924?v=4", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402138908-05:00 "id": 50348847343, [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402141338-05:00 "node_id": "SC_kwDOSuVe4s8AAAALuQZw7w", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402143228-05:00 "state": "failure", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402145368-05:00 "description": "Checks failed", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402147288-05:00 "target_url": "https://github.com/storeminder-ai/bootstrap/pull/250", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402149118-05:00 "context": "tekton/report-build-live", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402151058-05:00 "created_at": "2026-07-12T23:42:11Z", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402152878-05:00 "updated_at": "2026-07-12T23:42:11Z", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402154788-05:00 "creator": { [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402157008-05:00 "login": "steveblackmon", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402158868-05:00 "id": 702924, [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402160838-05:00 "node_id": "MDQ6VXNlcjcwMjkyNA==", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402162678-05:00 "avatar_url": "https://avatars.githubusercontent.com/u/702924?v=4", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402164488-05:00 "gravatar_id": "", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402166718-05:00 "url": "https://api.github.com/users/steveblackmon", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402168588-05:00 "html_url": "https://github.com/steveblackmon", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402170768-05:00 "followers_url": "https://api.github.com/users/steveblackmon/followers", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402173078-05:00 "following_url": "https://api.github.com/users/steveblackmon/following{/other_user}", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402175018-05:00 "gists_url": "https://api.github.com/users/steveblackmon/gists{/gist_id}", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402177748-05:00 "starred_url": "https://api.github.com/users/steveblackmon/starred{/owner}{/repo}", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402189098-05:00 "subscriptions_url": "https://api.github.com/users/steveblackmon/subscriptions", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402191338-05:00 "organizations_url": "https://api.github.com/users/steveblackmon/orgs", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402193988-05:00 "repos_url": "https://api.github.com/users/steveblackmon/repos", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402195848-05:00 "events_url": "https://api.github.com/users/steveblackmon/events{/privacy}", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402197728-05:00 "received_events_url": "https://api.github.com/users/steveblackmon/received_events", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402199548-05:00 "type": "User", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402201518-05:00 "user_view_type": "public", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402203368-05:00 "site_admin": false [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402205257-05:00 } [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.402207167-05:00 } [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.406767061-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-live-pod/step-post-status] 2026-07-12T18:42:11.406784511-05:00 Status posted successfully ===== bootstrap-pr-report-check-pr250-9fwkh-post-status-pod.log ===== [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/prepare] 2026-07-12T18:42:04.943412673-05:00 2026/07/12 23:42:04 Entrypoint initialization [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/place-scripts] 2026-07-12T18:42:05.511357349-05:00 2026/07/12 23:42:05 Decoded script /tekton/scripts/script-0-q8kxx [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.108863259-05:00 Posting commit status to GitHub [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.108907009-05:00 Repo: storeminder-ai/bootstrap [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.108910539-05:00 SHA: d15c4b94b94c40ce1e7fefa13dcc806acd7ccee5 [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.108913579-05:00 State: success (Succeeded) [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.108916028-05:00 Target URL: https://ci-logs.storeminder.ai/storeminder-ai/bootstrap/pr-250/bootstrap-pr-report-check-pr250-9fwkh/logs.txt [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531362130-05:00 { [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531383240-05:00 "url": "https://api.github.com/repos/storeminder-ai/bootstrap/statuses/d15c4b94b94c40ce1e7fefa13dcc806acd7ccee5", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531387510-05:00 "avatar_url": "https://avatars.githubusercontent.com/u/702924?v=4", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531390300-05:00 "id": 50348847385, [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531393040-05:00 "node_id": "SC_kwDOSuVe4s8AAAALuQZxGQ", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531395500-05:00 "state": "success", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531398000-05:00 "description": "Checks passed", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531400680-05:00 "target_url": "https://ci-logs.storeminder.ai/storeminder-ai/bootstrap/pr-250/bootstrap-pr-report-check-pr250-9fwkh/logs.txt", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531403180-05:00 "context": "tekton/report-build", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531405590-05:00 "created_at": "2026-07-12T23:42:11Z", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531407990-05:00 "updated_at": "2026-07-12T23:42:11Z", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531410480-05:00 "creator": { [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531413870-05:00 "login": "steveblackmon", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531416760-05:00 "id": 702924, [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531419270-05:00 "node_id": "MDQ6VXNlcjcwMjkyNA==", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531422180-05:00 "avatar_url": "https://avatars.githubusercontent.com/u/702924?v=4", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531424780-05:00 "gravatar_id": "", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531427310-05:00 "url": "https://api.github.com/users/steveblackmon", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531429870-05:00 "html_url": "https://github.com/steveblackmon", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531432360-05:00 "followers_url": "https://api.github.com/users/steveblackmon/followers", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531434930-05:00 "following_url": "https://api.github.com/users/steveblackmon/following{/other_user}", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531437710-05:00 "gists_url": "https://api.github.com/users/steveblackmon/gists{/gist_id}", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531440130-05:00 "starred_url": "https://api.github.com/users/steveblackmon/starred{/owner}{/repo}", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531449480-05:00 "subscriptions_url": "https://api.github.com/users/steveblackmon/subscriptions", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531452110-05:00 "organizations_url": "https://api.github.com/users/steveblackmon/orgs", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531454550-05:00 "repos_url": "https://api.github.com/users/steveblackmon/repos", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531456940-05:00 "events_url": "https://api.github.com/users/steveblackmon/events{/privacy}", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531459270-05:00 "received_events_url": "https://api.github.com/users/steveblackmon/received_events", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531461660-05:00 "type": "User", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531465370-05:00 "user_view_type": "public", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531467920-05:00 "site_admin": false [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531470350-05:00 } [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.531472780-05:00 } [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.535212686-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-pod/step-post-status] 2026-07-12T18:42:11.535230566-05:00 Status posted successfully ===== bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod.log ===== [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/prepare] 2026-07-12T18:42:05.124638961-05:00 2026/07/12 23:42:05 Entrypoint initialization [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/place-scripts] 2026-07-12T18:42:07.569638607-05:00 2026/07/12 23:42:07 Decoded script /tekton/scripts/script-0-tb755 [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:10.931696329-05:00 Posting commit status to GitHub [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:10.931723099-05:00 Repo: storeminder-ai/bootstrap [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:10.931726769-05:00 SHA: d15c4b94b94c40ce1e7fefa13dcc806acd7ccee5 [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:10.931729249-05:00 State: success (Succeeded) [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:10.931731859-05:00 Target URL: https://github.com/storeminder-ai/bootstrap/pull/250 [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363644947-05:00 { [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363667227-05:00 "url": "https://api.github.com/repos/storeminder-ai/bootstrap/statuses/d15c4b94b94c40ce1e7fefa13dcc806acd7ccee5", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363672257-05:00 "avatar_url": "https://avatars.githubusercontent.com/u/702924?v=4", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363676357-05:00 "id": 50348847330, [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363680147-05:00 "node_id": "SC_kwDOSuVe4s8AAAALuQZw4g", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363683957-05:00 "state": "success", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363687377-05:00 "description": "Checks passed", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363690687-05:00 "target_url": "https://github.com/storeminder-ai/bootstrap/pull/250", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363695017-05:00 "context": "tekton/report-traits", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363698907-05:00 "created_at": "2026-07-12T23:42:11Z", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363703217-05:00 "updated_at": "2026-07-12T23:42:11Z", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363707017-05:00 "creator": { [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363710757-05:00 "login": "steveblackmon", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363713707-05:00 "id": 702924, [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363716017-05:00 "node_id": "MDQ6VXNlcjcwMjkyNA==", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363718447-05:00 "avatar_url": "https://avatars.githubusercontent.com/u/702924?v=4", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363721247-05:00 "gravatar_id": "", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363724077-05:00 "url": "https://api.github.com/users/steveblackmon", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363726347-05:00 "html_url": "https://github.com/steveblackmon", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363728727-05:00 "followers_url": "https://api.github.com/users/steveblackmon/followers", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363731357-05:00 "following_url": "https://api.github.com/users/steveblackmon/following{/other_user}", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363734347-05:00 "gists_url": "https://api.github.com/users/steveblackmon/gists{/gist_id}", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363736717-05:00 "starred_url": "https://api.github.com/users/steveblackmon/starred{/owner}{/repo}", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363739107-05:00 "subscriptions_url": "https://api.github.com/users/steveblackmon/subscriptions", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363741587-05:00 "organizations_url": "https://api.github.com/users/steveblackmon/orgs", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363744047-05:00 "repos_url": "https://api.github.com/users/steveblackmon/repos", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363746507-05:00 "events_url": "https://api.github.com/users/steveblackmon/events{/privacy}", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363749607-05:00 "received_events_url": "https://api.github.com/users/steveblackmon/received_events", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363751927-05:00 "type": "User", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363754357-05:00 "user_view_type": "public", [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363756807-05:00 "site_admin": false [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363759107-05:00 } [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.363761577-05:00 } [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.368818158-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-post-status-traits-pod/step-post-status] 2026-07-12T18:42:11.368838658-05:00 Status posted successfully ===== bootstrap-pr-report-check-pr250-9fwkh-reclaim-workspace-pod.log ===== [pod/bootstrap-pr-report-check-pr250-9fwkh-reclaim-workspace-pod/place-scripts] 2026-07-12T18:42:08.134617810-05:00 2026/07/12 23:42:08 Decoded script /tekton/scripts/script-0-m7zmw [pod/bootstrap-pr-report-check-pr250-9fwkh-reclaim-workspace-pod/step-delete-run-pvcs] 2026-07-12T18:42:13.468679026-05:00 ==> reclaiming workspace PVC(s) owned by PipelineRun=bootstrap-pr-report-check-pr250-9fwkh in ns=tekton-pipelines [pod/bootstrap-pr-report-check-pr250-9fwkh-reclaim-workspace-pod/step-delete-run-pvcs] 2026-07-12T18:42:14.140261192-05:00 ==> deleting PVC pvc-045fa2728b (5Gi) [pod/bootstrap-pr-report-check-pr250-9fwkh-reclaim-workspace-pod/step-delete-run-pvcs] 2026-07-12T18:42:14.345738926-05:00 persistentvolumeclaim "pvc-045fa2728b" deleted [pod/bootstrap-pr-report-check-pr250-9fwkh-reclaim-workspace-pod/step-delete-run-pvcs] 2026-07-12T18:42:14.350111591-05:00 ==> requested deletion of 1 run-owned PVC(s) [pod/bootstrap-pr-report-check-pr250-9fwkh-reclaim-workspace-pod/prepare] 2026-07-12T18:42:06.476999304-05:00 2026/07/12 23:42:06 Entrypoint initialization ===== bootstrap-pr-report-check-pr250-9fwkh-sendable-pod.log ===== [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/place-scripts] 2026-07-12T18:38:42.575191606-05:00 2026/07/12 23:38:42 Decoded script /tekton/scripts/script-0-wllbs [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:46.913790122-05:00 Installing pytest… [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:51.388221745-05:00 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager, possibly rendering your system unusable. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv. Use the --root-user-action option if you know what you are doing and want to suppress this warning. [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:51.472312308-05:00 Neutralizing eager agent imports in src/storeminder/agents/__init__.py (hermetic checkout)… [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:51.473969932-05:00 Running report sendability guard: tests/test_report_validator_cadence.py [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:51.835477120-05:00 ============================= test session starts ============================== [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:51.835493130-05:00 platform linux -- Python 3.13.14, pytest-9.1.1, pluggy-1.6.0 -- /usr/local/bin/python [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:51.835496650-05:00 cachedir: .pytest_cache [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:51.835499740-05:00 rootdir: /workspace/source/source [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:51.835503640-05:00 configfile: pyproject.toml [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:51.835505820-05:00 plugins: cov-7.1.0 [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:51.885110130-05:00 collecting ... collected 6 items [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:51.885124560-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:51.896490322-05:00 tests/test_report_validator_cadence.py::test_leaky_daily_is_not_sendable PASSED [ 16%] [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:51.897806977-05:00 tests/test_report_validator_cadence.py::test_clean_daily_passes_without_false_positives PASSED [ 33%] [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:51.898868444-05:00 tests/test_report_validator_cadence.py::test_four_week_benchmark_phrasing_is_allowed PASSED [ 50%] [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:51.900181499-05:00 tests/test_report_validator_cadence.py::test_weekly_report_is_not_guarded PASSED [ 66%] [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:51.901218146-05:00 tests/test_report_validator_cadence.py::test_dow_mismatch_in_headline PASSED [ 83%] [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:52.469145957-05:00 tests/test_report_validator_cadence.py::test_json_path_guard_scans_narrative_values_only PASSED [100%] [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:52.469160937-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:52.469164197-05:00 ------ generated xml file: /workspace/source/artifacts/junit-sendable.xml ------ [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:52.469166967-05:00 ================================ tests coverage ================================ [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:52.469168957-05:00 _______________ coverage: platform linux, python 3.13.14-final-0 _______________ [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:52.469170957-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:52.469172917-05:00 Coverage XML written to file /workspace/source/artifacts/coverage-sendable.xml [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/step-pytest] 2026-07-12T18:38:52.469174877-05:00 ============================== 6 passed in 0.54s =============================== [pod/bootstrap-pr-report-check-pr250-9fwkh-sendable-pod/prepare] 2026-07-12T18:38:41.703323500-05:00 2026/07/12 23:38:41 Entrypoint initialization ===== bootstrap-pr-report-check-pr250-9fwkh-traits-pod.log ===== [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/prepare] 2026-07-12T18:38:44.073661865-05:00 2026/07/12 23:38:44 Entrypoint initialization [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/place-scripts] 2026-07-12T18:38:44.881046339-05:00 2026/07/12 23:38:44 Decoded script /tekton/scripts/script-0-vhptj [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:49.248176740-05:00 Installing pytest… [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:52.255969615-05:00 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:52.366395148-05:00 Running deterministic report-trait catalog: tests/test_report_traits.py [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.078363746-05:00 ============================= test session starts ============================== [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.078379676-05:00 platform linux -- Python 3.11.15, pytest-9.1.1, pluggy-1.6.0 -- /usr/local/bin/python [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.078384646-05:00 cachedir: .pytest_cache [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.078387076-05:00 rootdir: /workspace/source/source [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.078389586-05:00 configfile: pyproject.toml [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.078391566-05:00 plugins: cov-7.1.0, anyio-4.14.2 [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.323149789-05:00 collecting ... collected 25 items [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.323166989-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.336525783-05:00 tests/test_report_traits.py::test_pennycheck_inside_kpi_matches_journal PASSED [ 4%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.337708379-05:00 tests/test_report_traits.py::test_pennycheck_total_sales_cross_foots_fuel_plus_inside PASSED [ 8%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.338862715-05:00 tests/test_report_traits.py::test_pennycheck_fuel_gallons_kpi_matches_journal PASSED [ 12%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.339923242-05:00 tests/test_report_traits.py::test_pennycheck_scan_row_count_is_the_penny_day PASSED [ 16%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.346496909-05:00 tests/test_report_traits.py::test_daily_is_sendable_with_zero_weekly_markers PASSED [ 20%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.348188743-05:00 tests/test_report_traits.py::test_daily_rendered_markdown_has_no_weekly_structure PASSED [ 24%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.352212430-05:00 tests/test_report_traits.py::test_deliberately_leaked_daily_is_flagged_not_sendable PASSED [ 28%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.353747364-05:00 tests/test_report_traits.py::test_category_totals_reconcile_to_inside_sales[report_daily_fastime.json-Inside Sales] PASSED [ 32%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.355215079-05:00 tests/test_report_traits.py::test_category_totals_reconcile_to_inside_sales[report_weekly_fastime.json-Inside Sales] PASSED [ 36%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.356494875-05:00 tests/test_report_traits.py::test_unclassified_share_is_a_small_residual[report_daily_fastime.json] PASSED [ 40%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.357760891-05:00 tests/test_report_traits.py::test_unclassified_share_is_a_small_residual[report_weekly_fastime.json] PASSED [ 44%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.359076796-05:00 tests/test_report_traits.py::test_rendered_daily_categories_use_mj_taxonomy PASSED [ 48%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.360515031-05:00 tests/test_report_traits.py::test_rank_table_labels_its_comparison_basis PASSED [ 52%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.361781687-05:00 tests/test_report_traits.py::test_rank_deltas_are_bounded_for_staples PASSED [ 56%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.363564681-05:00 tests/test_report_traits.py::test_rank_arrows_reproduce_from_the_journal PASSED [ 60%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.364689657-05:00 tests/test_report_traits.py::test_daily_header_spells_out_correct_weekday PASSED [ 64%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.366979929-05:00 tests/test_report_traits.py::test_weekly_conversion_rows_carry_correct_weekday PASSED [ 68%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.368611434-05:00 tests/test_report_traits.py::test_all_weekday_date_pairs_are_internally_consistent PASSED [ 72%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.370096549-05:00 tests/test_report_traits.py::test_headline_reads_total_sales_on_every_surface[daily_md-$14,417.11] PASSED [ 76%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.371503554-05:00 tests/test_report_traits.py::test_headline_reads_total_sales_on_every_surface[weekly_md-$99,489.00] PASSED [ 80%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.373570447-05:00 tests/test_report_traits.py::test_sms_blocks_within_limits_and_never_trimmed[daily_md-daily] PASSED [ 84%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.375161471-05:00 tests/test_report_traits.py::test_sms_blocks_within_limits_and_never_trimmed[weekly_md-weekly] PASSED [ 88%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.376761926-05:00 tests/test_report_traits.py::test_sms_actions_block_keeps_whole_items_and_reply_prompt PASSED [ 92%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:53.386856961-05:00 tests/test_report_traits.py::test_store_events_are_configured_with_install_date PASSED [ 96%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:54.126447412-05:00 tests/test_report_traits.py::test_store_events_reach_the_synthesis_prompt PASSED [100%] [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:54.126466022-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:54.126469442-05:00 ------- generated xml file: /workspace/source/artifacts/junit-traits.xml ------- [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:54.126471702-05:00 ================================ tests coverage ================================ [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:54.126473792-05:00 _______________ coverage: platform linux, python 3.11.15-final-0 _______________ [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:54.126475732-05:00 [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:54.126479192-05:00 Coverage XML written to file /workspace/source/artifacts/coverage-traits.xml [pod/bootstrap-pr-report-check-pr250-9fwkh-traits-pod/step-pytest] 2026-07-12T18:38:54.126481352-05:00 ============================== 25 passed in 0.95s ==============================