# Bento 1 fresh bentonite consumption `config/bento1-material-consumption.yaml` defines `bento1-fresh-bentonite-consumption`, an estimate of **fresh bentonite consumption** from actual spreader roll speeds. Both needle rolls apply sequentially across the full web width. The recycled/recovered third spreader is excluded. The generic `rotational_discharge` source converts one or more rpm signals to the existing integrator's kg/h: `sum(rpm) × nominal_width_m × specific_discharge_kg_per_rev_m × 60`. Bento config sets the factor to **3.12 kg/(rev·m)** and uses: - Right ACT: `6e5d2d94-98f9-4cc1-8a88-7987c6282525` - Left ACT: `cd7385c4-337b-4759-ab32-45d65beaf190` ENLYZE now returns physical rpm directly (scaling factor 1.0); no division by 1000 is applied. For 3.739 + 3.956 rpm and 5 m width, the rate is 120.042 kg/min (7202.52 kg/h), giving 20.007 kg in ten seconds. Transport speed `fef41976-1103-4090-b780-eaeecc02fdfa` is exclusively the production gate: speed must be strictly greater than 0.3 m/min. It does not multiply the mass rate. The generic rotational mode also supports omitting `gate_signal_ref` and `gate_threshold`, in which case all valid intervals are active. Bento retains its gate. The two old SET sources `19ea65d2-bd35-4d02-89de-4495583d9026` and `d9f47615-69cf-4f97-88df-199585764491` are no longer requested by Bento. `direct_mass_rate` remains the default for K7; `area_application` remains available for other configurations. There is no Bento branch in the integrator. The previous-value integration, JSON state format and order bootstrap/resume remain unchanged. Totals accumulate across disjoint runs, with a new baseline at each run boundary, even if the inter-run gap is under 20 seconds. Intervals up to 20 seconds use the preceding rate and gate; longer sample gaps and unobserved run tails are not integrated. Missing/nonfinite source values reject the cycle. Finite negative speeds retain the existing generic signed-rate semantics; no clamping is introduced. Width comes from the existing ERP nominal-width parser, with workplace `Bento 1` and order format `Bento 1-{production_order}`. Workplace comparisons use `strip().casefold()`; order matching remains strict. Missing/mismatched ERP context or an unparseable width rejects the cycle without saving state. The current ERP context supplies width for all runs of the same order, assuming constant article width. Historical orders no longer in the ERP workplace view need a historical width source for replay. The configured factor is based on the supplied independent historical checks: 3.1175 at 5.00 m (article 180305), 3.1193 and 3.1187 at 4.85 m (article 173000), and median 3.1208 at 5.00 m (article 180005; p05–p95 3.0703–3.1558). Tests use realistic rpm values and synthetic samples, including disjoint-run bootstrap and persisted resume. They are not a new replay of recorded ENLYZE data. ## Old calculation cleanup: review before execution No production state or database rows were changed during this implementation, and no service start was requested. The operator reports Bento stopped; sandbox systemd access could not independently confirm that state. The deployed `/etc/systemd/system/production-analytics-bento1.service` specifies `/opt/git-projects/production-analytics/data/state/material` as its state directory. The existing state file for machine `5f42a4f6-9ca0-4f6f-9786-40d50a35b230` and order `Bento 1-12026000857` is exactly: ```text /opt/git-projects/production-analytics/data/state/material/4d6cc209e35bc58887e5b4a3816835006425429e810e0d957f651971723e8c2f.json ``` This identity was verified using the store's SHA-256 of the JSON machine/order pair. The file exists but its contents are not readable by the sandbox user. The other state file, `6d11088159b8e4fd21543560a54a31a0f86faf22bbf8f16873b228d1903c23f9.json`, has not been attributed here and must be left untouched. Old PostgreSQL snapshots are in `material_consumption_snapshots`, scoped by both `calculation_id = 'bento1-fresh-bentonite-consumption'` and `machine_id = '5f42a4f6-9ca0-4f6f-9786-40d50a35b230'`. The table has no calculation-source/version provenance column. Given the stopped service and no rpm deployment yet, existing rows in this scope belong to the old calculation. PostgreSQL was unreachable from the sandbox, so exact row counts, order/run membership and timestamp bounds remain unverified. Do not mistake this predicate for a completed row inventory. Review the following output first. Run these **read-only inventory commands** on the deployment host: ```sh cd /opt/git-projects/production-analytics sudo systemctl is-active production-analytics-bento1.service sudo cat data/state/material/4d6cc209e35bc58887e5b4a3816835006425429e810e0d957f651971723e8c2f.json sudo docker compose exec -T timescaledb psql -U production_analytics -d production_analytics -v ON_ERROR_STOP=1 <<'SQL' BEGIN READ ONLY; SELECT production_order, run_id, count(*) AS rows, min(timestamp) AS first_snapshot, max(timestamp) AS last_snapshot, min(consumption_kg), max(consumption_kg) FROM material_consumption_snapshots WHERE calculation_id = 'bento1-fresh-bentonite-consumption' AND machine_id = '5f42a4f6-9ca0-4f6f-9786-40d50a35b230' GROUP BY production_order, run_id ORDER BY first_snapshot; SELECT * FROM material_consumption_snapshots WHERE calculation_id = 'bento1-fresh-bentonite-consumption' AND machine_id = '5f42a4f6-9ca0-4f6f-9786-40d50a35b230' ORDER BY production_order, timestamp; COMMIT; SQL ``` After reviewing the inventory, and **only with cleanup authorization**, keep the service stopped and execute this backup-and-delete transaction. The backup table intentionally has no `IF NOT EXISTS`: a repeated invocation fails rather than reusing an older backup. Only rows with the backed-up primary keys are deleted. K7 rows are outside the predicate. ```sh cd /opt/git-projects/production-analytics sudo docker compose exec -T timescaledb psql -U production_analytics -d production_analytics -v ON_ERROR_STOP=1 <<'SQL' BEGIN; CREATE TABLE bento1_material_snapshots_before_rpm_20260907 AS SELECT * FROM material_consumption_snapshots WHERE calculation_id = 'bento1-fresh-bentonite-consumption' AND machine_id = '5f42a4f6-9ca0-4f6f-9786-40d50a35b230'; DELETE FROM material_consumption_snapshots AS s USING bento1_material_snapshots_before_rpm_20260907 AS old WHERE s.calculation_id = old.calculation_id AND s.machine_id = old.machine_id AND s.production_order = old.production_order AND s.timestamp = old.timestamp; COMMIT; SQL sudo mv -n -- \ data/state/material/4d6cc209e35bc58887e5b4a3816835006425429e810e0d957f651971723e8c2f.json \ data/state/material/4d6cc209e35bc58887e5b4a3816835006425429e810e0d957f651971723e8c2f.json.before-rpm-20260907 ``` Verify the original JSON path is absent before restarting; `mv -n` preserves an existing backup and will not overwrite it. If inventory reveals other Bento orders, derive their paths with `JsonMaterialStateStore._path(machine, order)` and review any existing files before moving them too. Do not clear the shared directory. Complete both state and snapshot cleanup before starting the new code: the state schema deliberately does not detect a changed source model. A later start bootstraps only the currently open order using its current ERP width. It does not automatically rebuild snapshots for closed historical orders. Reconstructing those requires a separately reviewed historical replay.