factorysemantics-mes 0.3.1__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- factorysemantics_mes-0.4.0/.github/scripts/two_nodes_on_one_box.py +369 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/workflows/ci.yml +122 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/CHANGELOG.md +159 -1
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/CITATION.cff +2 -2
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/PKG-INFO +2 -2
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/README.md +1 -1
- factorysemantics_mes-0.4.0/docker/docker-compose.redundant.yml +184 -0
- factorysemantics_mes-0.4.0/docker/nginx.redundant.conf +42 -0
- factorysemantics_mes-0.4.0/docker/redundant/fleet.toml +13 -0
- factorysemantics_mes-0.4.0/docker/redundant/plant.toml +74 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ARCHITECTURE.md +1 -1
- factorysemantics_mes-0.4.0/docs/decisions/0042-one-plant-many-nodes.md +127 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/index.md +1 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/deploy.md +1 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/fleet.md +49 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/packs.md +2 -1
- factorysemantics_mes-0.4.0/docs/operate/redundancy.md +395 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/upgrade.md +32 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/releases/0.3.0.md +1 -5
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/releases/0.3.1.md +1 -5
- factorysemantics_mes-0.4.0/docs/releases/0.4.0.md +118 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/releases/index.md +1 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/mkdocs.yml +4 -0
- factorysemantics_mes-0.4.0/scripts/kill-a-node.sh +264 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/__init__.py +1 -1
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/app.py +10 -2
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/system.py +60 -2
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/cli.py +70 -16
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/config.py +59 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/__init__.py +14 -0
- factorysemantics_mes-0.4.0/src/fsmes/domain/leases.py +76 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/fleet/commands.py +196 -18
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/fleet/console.py +83 -6
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/fleet/owned.py +17 -2
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/identity.py +13 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/opc/agent.py +170 -9
- factorysemantics_mes-0.4.0/src/fsmes/migrations/versions/b6f0c2a71d58_a_lease_and_a_counter_baseline.py +71 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/modules.py +11 -1
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/pack/check.py +98 -1
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/pack/fleet.py +8 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/pack/format.py +45 -1
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/plant.py +379 -36
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/schema.py +56 -0
- factorysemantics_mes-0.4.0/src/fsmes/services/counters.py +69 -0
- factorysemantics_mes-0.4.0/src/fsmes/services/leases.py +304 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/sim/operations.py +29 -2
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/common.js +80 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/fleet.css +11 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/fleet.js +72 -0
- factorysemantics_mes-0.4.0/tests/test_a_plant_can_run_as_two_nodes_on_one_box_and_the_fleet_knows_both.py +1033 -0
- factorysemantics_mes-0.4.0/tests/test_a_plant_on_two_nodes_survives_one_of_them_dying.py +602 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_agent_batching.py +22 -1
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_fleet_create_makes_a_usable_plant.py +67 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_fleet_ownership.py +2 -2
- factorysemantics_mes-0.4.0/tests/test_no_command_loses_a_word_of_its_help_to_the_painter.py +78 -0
- factorysemantics_mes-0.4.0/tests/test_one_reader_at_a_time_is_a_lease_in_the_database.py +440 -0
- factorysemantics_mes-0.4.0/tests/test_the_dashboard_says_which_node_answered_and_who_reads_the_machines.py +318 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_release_body_github_will_accept.py +47 -4
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_simulated_floor_signs_in_again_when_its_login_runs_out.py +31 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.dockerignore +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.env.example +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/CODEOWNERS +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/ISSUE_TEMPLATE/bug.yml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/ISSUE_TEMPLATE/integration.yml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/ISSUE_TEMPLATE/plant-pack.yml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/dependabot.yml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/scripts/assistant_folder_from_the_wheel.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/scripts/demo_from_wheel.sh +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/scripts/doctor_writes_nothing_of_yours.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/scripts/lab_starters.sh +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/scripts/plant_from_shape.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/scripts/plant_from_wheel.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/scripts/recover_a_half_dead_plant.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/scripts/release_notes.sh +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/scripts/upgrade_from_previous_release.sh +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/workflows/dco.yml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/workflows/docs.yml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/workflows/erpnext-live.yml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/workflows/release.yml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/workflows/scorecard.yml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.github/workflows/welcome.yml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/.gitignore +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/CODE_OF_CONDUCT.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/CONTRIBUTING.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/GOVERNANCE.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/LICENSE +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/MAINTAINERS.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/NOTICE +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/ROADMAP.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/SECURITY.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/alembic.ini +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/config/inbound_mapping.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/config/inbound_sql.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/config/line_layout.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/config/tag_map.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/config/tag_map_kepsim.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/config/tag_map_kepware.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/deploy/README.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/deploy/fsmes-autoloop.service +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/deploy/fsmes-autoloop.timer +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/deploy/fsmes-prod-plant@.service +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/deploy/promote.sh +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docker/Dockerfile +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docker/docker-compose.yml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docker/nginx.conf +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/LANDSCAPE.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/agents/connect-mcp-client.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/agents/evals.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/agents/index.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/agents/write-discipline.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/ASSIST-EVAL.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/BUDGET.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/JEV-CALIBRATION.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/JEV.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/JUDGMENT-IN-THE-BUILD-LOOP.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/OBSERVABILITY.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/assist-eval/2026-09-27-live-admin.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/assist-eval/2026-09-27-live-operator.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/assist-eval/2026-09-27-scripted.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/calibration/2026-09-17/full/calibration.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/calibration/2026-09-17/full/d1-against-truth.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/calibration/2026-09-17/full/d1-component-stopped-reporting.svg +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/calibration/2026-09-17/full/d1-counter-went-backwards.svg +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/calibration/2026-09-17/full/p1-by-confidence.svg +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/calibration/2026-09-17/full/p1-by-probability.svg +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/calibration/2026-09-17/stopped-not-why/calibration.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/calibration/2026-09-17/stopped-not-why/d1-against-truth.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/calibration/2026-09-17/stopped-not-why/d1-component-stopped-reporting.svg +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/calibration/2026-09-17/stopped-not-why/d1-counter-went-backwards.svg +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/calibration/2026-09-17/stopped-not-why/p1-by-confidence.svg +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/ai/calibration/2026-09-17/stopped-not-why/p1-by-probability.svg +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/assets/walk.gif +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0001-apache-2-with-dco.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0002-kernel-and-modules.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0003-one-mcp-server.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0004-never-invent-production.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0005-plain-html-no-build-step.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0006-sqlite-laptop-postgresql-plant.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0007-fresh-public-history.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0008-erp-connectors-are-modules.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0009-organisation-not-personal-account.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0010-github-discussions-only.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0011-mkdocs-material-generated-reference.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0012-contributor-covenant-3.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0013-governance-is-one-page.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0014-first-public-version-is-0-1-0.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0015-mcp-registry-name.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0016-no-trademark-registration.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0017-one-public-demo-gated-and-bounded.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0018-public-on-2026-09-08-and-the-pre-tag-check.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0019-count-everything-the-machine-counted.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0020-what-supported-means-for-an-erp-connector.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0021-one-database-per-plant.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0022-what-a-plant-pack-may-contain.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0023-the-fleet-console-observes.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0024-a-nonconformance-has-a-life.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0025-performance-is-measured-not-capped.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0026-counts-that-outrun-the-run-time.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0027-an-spc-signal-raises-a-hold.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0028-which-shift-a-minute-belongs-to.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0029-an-order-does-not-finish-itself.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0030-a-lost-connection-is-unknown-time.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0031-a-judgment-is-a-proposal.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0032-a-hosted-judgment-and-the-shadow.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0033-availability-is-a-share-of-what-was-watched.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0034-a-count-is-owed-until-it-is-booked.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0035-configuration-is-authored-by-roles-and-selected-by-operators.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0036-the-chart-draws-every-rule-the-plant-chooses-which-hold.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0038-an-agent-is-an-account-with-a-role-a-budget-and-a-cadence.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0039-an-analysis-is-recorded-code-that-can-only-read.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0040-the-chart-type-follows-the-sampling-plan.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/0041-a-characteristic-the-plant-counts.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/decisions/TEMPLATE.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/STYLE.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/agentic-harness.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/README.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/admin-edit-roles.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/analysis-cards-scaling.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/assistant-submits-an-inspection.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/book-output-explicit-order.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/clickable-kpi-tiles.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/clickable-machine-breadcrumbs.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/floor-machines-paginate-and-filter.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/measurements-card-filtering.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/measurements-characteristic-filter.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/nonconformance-lifecycle.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/opc-integration-suite.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/order-card-search-finds-a-material.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/order-lifecycle-actions.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/orders-creatable-materials.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/orders-supervisor-filters.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/persistent-header-menu.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/review-orders.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/review-station.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/scrap-reason-codes.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/shop-floor-actions-strip.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/sixty-machine-plant.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/spc-capability-withheld.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/specifications-card-filtering.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/specifications-server-paged.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/station-quality-card.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/station-spc.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/station-view.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/supervisor-drill-down.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/backlog/ui-style-drift-bc58fb4f8e4d.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/config-assistance.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/config-audit-2026-09-21.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/deep-analysis.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/m8-packs-and-fleet.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/design/pack-in-pack.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/develop/erp-connectors.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/develop/experiments.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/develop/index.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/develop/your-first-module.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/history/private-era.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/hooks/generate.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/index.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/onboarding/GUIDE-ENGINEERING.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/onboarding/GUIDE-IT.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/onboarding/README.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/onboarding/worksheet-example.csv +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/onboarding/worksheet-template.csv +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/administration-numbers.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/ai.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/assistant-coverage.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/backup.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/compatibility.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/confirmation-examples/completion-with-a-lot.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/confirmation-examples/completion-with-a-lot.xml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/confirmation-examples/completion-with-an-over-run.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/confirmation-examples/completion-with-an-over-run.xml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/confirmation-examples/completion-without-a-lot.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/confirmation-examples/completion-without-a-lot.xml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/confirmation-examples/operation-clean.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/confirmation-examples/operation-clean.xml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/confirmation-examples/operation-over-run.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/confirmation-examples/operation-over-run.xml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/confirmation-examples/operation-with-scrap-and-components.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/confirmation-examples/operation-with-scrap-and-components.xml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/confirmation-files.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/coverage.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/doctor.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/engineering-numbers.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/erp-settings.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/erpnext.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/first-plant.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/first-week.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/images/review-walk-station.png +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/images/review-walkthrough.png +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/images/shape-equipment.png +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/images/shape-timeline.png +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/inbound.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/index.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/modules.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/opc-disconnections.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/opc-readonly.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/plant-from-your-mes.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/quality-numbers.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/quality-severities.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/registry.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/releasing.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/run-it-on-your-own-box.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/security.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/shadow-mode.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/shadow-scorecard.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/the-simulated-floor.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/tls.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/uns.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/operate/who-names-the-reasons.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/plant/first-line.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/plant/images/spc-xbar-and-r.png +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/plant/index.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/plant/never-invent-production.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/plant/quality-signals.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/plant/reading-a-trend.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/plant/reading-oee.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/plant/shifts.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/docs/releases/0.2.0.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/cutlery/README.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/cutlery/analysis/build_notebook.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/cutlery/analysis/build_report.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/cutlery/analysis/cutlery_run_analysis.ipynb +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/cutlery/analysis/viz.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/cutlery/build_config.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/cutlery/fill_day.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/cutlery/fleet.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/cutlery/init.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/cutlery/line.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/cutlery/make_tag_map.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/cutlery/masterdata/downtime_reasons.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/cutlery/masterdata/nc_severities.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/cutlery/plant.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/cutlery/run_cutlery.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/cutlery/tag_map.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/cutlery/walkthroughs/install_walkthroughs.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/cutlery/walkthroughs/walkthroughs.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/erpnext/VERSIONS.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/erpnext/docker-compose.yml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/erpnext/seed_erpnext.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/experiments/lost-connection.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/experiments/one-line-bad-hour.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/experiments/over-run.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/experiments/scrap-burst.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/experiments/starved-and-blocked.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/experiments/two-plants-two-zones.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/kepsim/.env.example +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/kepsim/README.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/kepsim/kepware_setup.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/kepsim/line.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/kepsim/scenario.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/kepsim/setup_dsn.ps1 +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/kepsim/verify_readback.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/megafactory/build_config.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/megafactory/fleet.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/megafactory/full/build_config.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/megafactory/full/init.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/megafactory/full/line.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/megafactory/full/plant.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/megafactory/full/run_full.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/megafactory/full/seed_breadth.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/megafactory/full/surfaces.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/megafactory/full/tag_map.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/megafactory/init.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/megafactory/line.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/megafactory/make_tag_map.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/megafactory/run_spike.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/megafactory/seed_breadth.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/megafactory/tag_map.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/README.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/floor.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/line.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/masterdata/README.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/masterdata/bom.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/masterdata/dispatch_rules.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/masterdata/downtime_reasons.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/masterdata/equipment.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/masterdata/gauges.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/masterdata/lots.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/masterdata/maintenance_plans.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/masterdata/materials.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/masterdata/nc_severities.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/masterdata/personnel.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/masterdata/personnel_skills.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/masterdata/quality_specs.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/masterdata/roster.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/masterdata/routings.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/masterdata/shifts.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/masterdata/skills.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/masterdata/work_orders.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/plant.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/bottling/tag_map.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/finewire/README.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/finewire/floor.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/finewire/mappings/inbound_mapping.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/finewire/masterdata/downtime_reasons.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/finewire/masterdata/equipment.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/finewire/masterdata/lots.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/finewire/masterdata/materials.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/finewire/masterdata/nc_severities.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/finewire/masterdata/quality_specs.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/finewire/masterdata/routings.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/finewire/masterdata/work_orders.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/finewire/plant.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/finewire/tag_map.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/fleet.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/machining/floor.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/machining/line.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/machining/masterdata/README.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/machining/masterdata/downtime_reasons.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/machining/masterdata/equipment.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/machining/masterdata/lots.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/machining/masterdata/materials.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/machining/masterdata/nc_severities.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/machining/masterdata/quality_specs.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/machining/masterdata/routings.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/machining/masterdata/work_orders.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/machining/plant.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/labs/multiplant/machining/tag_map.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/pyproject.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/assistant/0-how-to-help.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/assistant/1-install.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/assistant/2-your-plant.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/assistant/3-shadow-mode.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/assistant/4-migration.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/assistant/README.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/assistant/SKILL.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/FORMAT.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/SKILL.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/example/plant-shape-grouped.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/example/plant-shape.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/mssql/01_inventory.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/mssql/01b_row_count.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/mssql/02_columns.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/mssql/03_window.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/mssql/04_column_profile.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/mssql/04b_column_sample.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/mssql/05_assets.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/mssql/05b_tag_samples.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/mssql/05c_tag_samples_nostate.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/mssql/05d_routings.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/mssql/06_states.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/mssql/06b_production.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/mssql/06c_quality.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/mssql/06d_orders.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/mssql/06e_shifts.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/mssql/06f_documents.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/mssql/07_erp.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/sqlite/01_inventory.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/sqlite/01b_row_count.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/sqlite/02_columns.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/sqlite/03_window.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/sqlite/04_column_profile.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/sqlite/04b_column_sample.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/sqlite/05_assets.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/sqlite/05b_tag_samples.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/sqlite/05c_tag_samples_nostate.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/sqlite/05d_routings.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/sqlite/06_states.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/sqlite/06b_production.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/sqlite/06c_quality.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/sqlite/06d_orders.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/sqlite/06e_shifts.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/sqlite/06f_documents.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/queries/sqlite/07_erp.sql +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/scripts/explain.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/scripts/leakcheck.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/skills/plant-from-your-mes/scripts/profile.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/__main__.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/__init__.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/deps.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/idempotency.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/paging.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/__init__.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/adjustments.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/admin.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/analysis.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/assist.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/auth.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/coa.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/dashboard.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/design.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/documents.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/equipment.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/erp.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/execution.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/kpis.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/line.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/maintenance.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/masterdata.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/ops.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/quality.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/reasons.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/scheduling.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/serialization.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/severities.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/triggers.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/api/routers/workorders.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/assist_coverage.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/backup.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/core/__init__.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/core/oplock.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/db.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/demo_feed.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/doctor/__init__.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/doctor/gather.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/doctor/leak.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/doctor/render.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/doctor/scrub.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/adjustments.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/ai_turns.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/audit.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/calendar.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/common.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/crew.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/documents.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/equipment.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/execution.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/gauges.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/idempotency.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/inbound.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/integration.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/maintenance.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/masterdata.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/plant_settings.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/quality.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/reasons.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/scheduling.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/serialization.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/severities.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/timeseries.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/triggers.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/uns.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/domain/workorders.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/firstrun.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/fleet/__init__.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/fleet/logsink.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/fleet/observe.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/__init__.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/erp/__init__.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/erp/b2mml.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/erp/base.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/erp/conformance.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/erp/contract.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/erp/erpnext_adapter.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/erp/erpnext_setup.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/erp/examples.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/erp/file_adapter.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/erp/incumbent.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/erp/mock_erp.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/erp/rest_adapter.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/erp/schema.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/erp/scorecard.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/erp/scorecard_html.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/erp/sync.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/erp/validate.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/events.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/inbound/__init__.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/inbound/contract.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/inbound/folder.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/inbound/mqtt.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/inbound/sql.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/jev/__init__.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/jev/client.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/jev/transport.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/opc/__init__.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/opc/csv_replay.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/opc/inspect.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/opc/security.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/opc/simulator.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/opc/tag_map.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/opc/worksheet.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/uns/__init__.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/uns/envelope.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/uns/publisher.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/uns/topics.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/integrations/uns/transport.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/kernel/__init__.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/kernel/common.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/kernel/tags.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/lab/__init__.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/lab/assist_eval.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/lab/assist_fixtures.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/lab/assist_seed.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/lab/build.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/lab/console.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/lab/feedback.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/lab/measure.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/lab/observe.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/lab/plan.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/lab/report.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/lab/review.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/lab/run.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/lab/truth.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/logging.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/mcp/__init__.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/mcp/adjustments.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/mcp/analysis.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/mcp/coa.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/mcp/equipment.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/mcp/erp.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/mcp/maintenance.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/mcp/masterdata.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/mcp/quality.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/mcp/reasons.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/mcp/scheduling.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/mcp/serialization.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/mcp/settings.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/mcp/severities.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/mcp/triggers.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/mcp_server.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migration.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/env.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/script.py.mako +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/153379d6cf19_merge_cutlery_and_walkthrough_chains.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/18af853fc5c4_initial_schema.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/7c1e5a9d2b4f_serial_sequences.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/9c4d2c836d05_add_user_passwords.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/a1b2c3d4e5f6_agent_identity.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/a1c47f8b2d93_what_a_maintenance_job_needs_of_the_line.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/a1c5e70b2d94_plant_settings.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/a1c5e83b7602_calendar_and_scheduling.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/a3f6c81d09e2_unassigned_production.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/a4d9c2e70f18_uns_publications.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/a7c1e93b5f20_roles_as_data.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/a7c2e5d1b9f0_document_kind_steps_needs_walkthroughs.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/a9c4e17b3d60_spc_signals_act.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/b1f4c73a9e08_nonconformance_lifecycle.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/b1f4c7a2e903_production_logs_equipment_ts_index.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/b2c3d4e5f6a7_outbox_hardening.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/b3f8d2a41c67_controlled_documents.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/b3f9c07e51ad_ai_turns.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/b5c1d09e73af_inbound_events_and_their_source.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/b7d2e9f4a1c3_unit_inspections.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/b8f4d27e1a53_serialisation.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/b9f31c7a4e50_a_characteristic_inspected_n_at_a_time.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/c1e7a04b93d5_downtime_reasons.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/c2e9b74a3f81_gauges_and_calibration.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/c3d4e5f6a7b8_triggers.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/c3d7e91a4b28_equipment_connections.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/c4e8f1a2d5b6_serial_units_one_index.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/c6a4b81f39d7_shift_attribution.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/c7a2e94b16d3_five_facts_the_plant_was_dropping.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/c8b1e40d7a92_erp_messages_direction_index.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/c9e2a4b71d38_material_at_a_station.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/d2b7e51c9a04_nc_severities.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/d4a1c7e5b902_equipment_cost_center.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/d4a7f10c8b52_component_may_enter_twice.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/d4e5f6a7b8c9_recommended_adjustments.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/d4e8a1c93b70_a_characteristic_the_plant_counts.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/d7b4e92a1c58_the_maintenance_crew_and_its_rules.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/d9a3f61c48e0_inbound_sql_watermarks.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/e3c8a17d5b46_gauge_warning_and_pieces.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/e5f6a7b8c9d0_tag_values_latest_index.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/e6b3c92d4f17_drop_the_old_bom_constraint.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/e7b3c9d18a45_workorder_work_center.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/f0a1b2c3d4e5_equipment_states_ended_index.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/f2a8d31c6b74_routing_durations.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/migrations/versions/f7d1a58e2c94_preventive_maintenance.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/pack/__init__.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/pack/apply.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/pack/masterdata.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/pack/migrate.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/pack/plan.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/pack/repoint.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/py.typed +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/seed.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/seed_kepsim.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/seed_line.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/__init__.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/adjustments.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/agent.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/ai_status.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/ai_trace.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/analysis.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/assist_eval.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/assistant.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/audit.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/auth.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/calendar.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/capabilities.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/coa.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/connection.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/coverage.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/design.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/design_triage.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/dispatch.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/documents.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/drafting.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/equipment.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/erp.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/execution.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/gauges.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/inbound.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/line.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/line_clock.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/maintenance.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/masterdata.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/moment.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/oee.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/outbox.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/plant_settings.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/quality.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/reasons.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/retention.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/review.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/scheduling.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/serialization.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/severities.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/spc.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/spc_point.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/staging.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/state_block.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/tags.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/trace_analysis.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/trend.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/triggers.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/uns.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/vocabulary.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/walkthroughs.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/services/workorders.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/shadow.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/shape/__init__.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/shape/model.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/shape/pack.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/shape/read.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/sim/__init__.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/sim/agent_eval.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/sim/autoloop.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/sim/calibration.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/sim/config_audit.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/sim/config_audit_curated.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/sim/generate.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/sim/labelled.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/sim/mcp_server.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/sim/measurement.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/sim/rollup.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/sim/runner.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/sim/score.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/sim/store.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/sim/sweep.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/sim/triage.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/sim/truth.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/sim/ui_check.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/skills.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/standalone.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/storage.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/adjustments.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/adjustments.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/admin.css +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/admin.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/admin.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/ai.css +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/ai.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/ai.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/analysis.css +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/analysis.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/analysis.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/app.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/assist-record.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/assist.css +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/assist.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/coa.css +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/coa.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/coa.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/config.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/config.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/demo/kepsim-hour.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/design.css +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/design.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/fleet.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/gauges.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/gauges.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/index.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/instructions.css +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/instructions.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/instructions.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/kit.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/line/app.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/line/feed.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/line/flow.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/line/parts.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/line/robot.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/line/scene.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/line-page.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/line.css +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/line.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/line3d.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/machine.css +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/machine.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/machine.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/machines.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/machines.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/maintenance.css +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/maintenance.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/maintenance.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/masterdata.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/masterdata.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/ops.css +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/ops.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/ops.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/orders.css +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/orders.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/orders.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/quality.css +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/quality.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/quality.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/reasons.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/reasons.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/schedule.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/schedule.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/severities.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/severities.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/spc.css +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/spc.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/spc.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/station.css +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/station.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/station.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/styles.css +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/tags.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/tags.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/themes.css +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/themes.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/trace.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/trace.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/triggers.html +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/triggers.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/vendor/OrbitControls.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/vendor/README.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/vendor/three.core.min.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/src/fsmes/web/vendor/three.module.min.js +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/assist_suite/README.md +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/assist_suite/admin.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/assist_suite/agent.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/assist_suite/analyst.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/assist_suite/operator.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/assist_suite/supervisor.toml +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/conftest.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/fixtures/labelled-run/line/bench.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/fixtures/labelled-run/packs/bench/tag_map.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/fixtures/labelled-run/recorded/bench-scorecard.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/fixtures/labelled-run/replay/bench/Line.csv +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/fixtures/labelled-run/replay/bench/Pack.csv +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/fixtures/labelled-run/replay/bench/Press.csv +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/fixtures/labelled-run/replay/bench/Trim.csv +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/fixtures/labelled-run/replay/bench/Weld.csv +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/fixtures/labelled-run/replay/bench/schema.ini +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/fixtures/labelled-run/scores.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/fixtures/labelled-set.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/live_replies.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/page_anchors.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_a_block_on_the_state_timeline_carries_the_records_behind_it.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_a_characteristic_inspected_five_at_a_time.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_a_characteristic_the_plant_counts.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_a_chart_draws_only_what_the_api_measured.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_a_plant_books_while_its_dashboard_is_read.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_a_plant_from_the_package_writes_only_to_a_users_own_directory.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_a_plant_logs_at_a_rate_a_year_can_stand.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_a_plant_records_what_each_of_its_processes_is.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_a_plant_shape_built_blind_from_an_unknown_sql_mes_is_the_bottling_line.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_a_plant_starts_and_stops_the_same_way_on_windows.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_a_point_on_the_control_chart_carries_the_records_behind_it.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_a_refusal_names_the_capability_and_who_holds_it.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_a_sample_on_the_control_chart_carries_the_five_bottles_behind_it.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_a_shift_of_stops_comes_out_in_the_plants_own_proportions.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_a_signature_they_hold_is_a_walk_and_not_a_refusal.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_a_walk_waits_for_a_control_the_page_is_still_drawing.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_adjustments.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_agent.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_agent_eval.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_ai_status.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_an_analysis_counts_people_before_it_names_one.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_an_exploration_draws_what_the_plant_measured.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_analysis.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_api.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_approval_review.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_assist_suite_scripted.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_assistant.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_auth.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_autoloop.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_backup.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_bottling_pack.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_capabilities.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_cli.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_clicking_a_block_on_the_state_timeline_opens_the_records_behind_it.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_clicking_a_lot_on_the_spc_chart_opens_which_defects.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_clicking_a_point_on_the_spc_chart_opens_the_records_behind_it.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_clicking_a_point_on_the_trend_graph_opens_the_records_behind_it.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_clicking_a_sample_on_the_spc_chart_opens_the_five_bottles.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_coa.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_config_audit.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_configuration_sections.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_core_oplock.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_core_purity.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_coverage_ledger.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_cutlery.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_demo_verdict.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_design.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_design_triage.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_doctor_writes_one_file_with_nothing_of_yours_in_it.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_documents.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_downtime_reasons.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_environments.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_equipment_oee.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_erp_confirmation_files.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_erp_conformance.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_erp_contract.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_erp_roundtrip.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_erpnext.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_erpnext_live.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_every_browser_test_is_in_the_browser_tier.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_every_command_the_assistant_folder_tells_you_to_run_exists.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_every_settings_list_fits_in_one_tool_result.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_every_show_me_lands_on_the_real_control.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_every_write_route_has_a_tool_or_a_reason.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_every_write_tool_names_the_capability_its_route_demands.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_everything_the_model_said_in_one_turn_reaches_the_reply.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_execution.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_file_erp.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_fleet_console.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_fleet_plan.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_fsmes_runs_as_a_module_when_it_is_not_on_the_path.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_hierarchy.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_hold.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_inbound.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_inbound_folder.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_inbound_mqtt.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_inbound_sql.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_jev_agent_eval_scoring.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_jev_calibration.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_jev_fail_safe.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_jev_labelled_set.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_jev_run_log_triage.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_jev_sdk_surface.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_kepsim.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_kernel_common.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_kpi_budget.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_lab_connection.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_lab_console.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_lab_experiments.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_lab_feedback.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_lab_latency.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_lab_order_book.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_lab_orders.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_lab_scenarios.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_line_api.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_line_scoping.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_live_admin_settings.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_live_engineering_settings.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_live_plant_settings.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_maintenance.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_maintenance_work_goes_to_the_right_trade.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_mcp_product.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_megafactory.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_migrations_have_one_head.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_module_wall.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_nc_severities.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_no_tenant_literals.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_onboarding.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_opc_disconnection.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_outbox_events.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_overproduction.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_pack_format.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_pack_third.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_packaged_config.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_paging.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_performance_at_replay_speed.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_plant_identity.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_plant_migrate.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_plant_scale_lists.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_plant_scale_ui.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_plant_serve_only.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_promote_script.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_quality.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_quality_numbers.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_read_never_waits.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_rollup.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_route_coverage.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_routing_depth.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_run_ports.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_scale.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_scheduling_trace.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_schema_upgrade.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_serial_scale.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_shadow_mode.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_shadow_scorecard.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_shift_attribution.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_sim.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_spc_gauges.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_spc_moving_range.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_spc_signals.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_sqlite_write_locks.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_status_names_its_database.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_store.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_supply_chain_settings.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_sweep_replay.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_tag_fabric.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_tag_fabric_review.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_tags.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_tags_grouped_the_way_a_real_opc_server_groups_them.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_ai_screen_shows_a_conversation_and_gates_itself.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_ai_tab_explores_and_draws_what_it_read.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_ai_tab_says_what_the_assistant_costs_without_local_ai.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_analyses_answer_an_agent_the_way_they_answer_a_screen.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_analysis_agent_is_a_kind_of_its_own.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_analysis_agent_is_asked_for_the_next_questions.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_assistant_proposes_rather_than_asking.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_assistant_reads_before_it_says_no.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_bottling_shift_leaves_a_chain_a_person_can_walk_back.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_calendar_tool_names_every_kind_its_api_accepts.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_chart_comes_into_the_chat_and_stays_clickable.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_chat_can_open_the_dossier_behind_a_charted_point.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_control_chart_the_chat_reads_leaves_the_bottles_out.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_conversation_is_cached_and_not_only_the_catalogue.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_demo_path_says_what_is_wrong_and_what_to_type.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_dispatcher_at_three_hundred_people.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_docker_image_carries_the_packs.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_gauges_screen_shows_the_two_scales_and_when_each_was_calibrated.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_panel_can_ask_the_other_agent.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_panel_keeps_one_brain_through_a_failed_turn.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_plant_keeps_a_trace_of_what_its_ai_did.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_plant_stops_dropping_five_facts_it_already_had.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_screens_that_show_a_characteristic_inspected_five_at_a_time.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_simulated_crew_does_the_work_it_was_given.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_simulated_floor_plants_causes_an_engineer_can_find.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_skills_zip_holds_exactly_what_it_says_and_nothing_else.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_spc_chart_wears_the_accepted_chart_look.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_sql_server_query_set_is_not_sqlite_in_disguise.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_station_redraws_only_what_changed.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_station_says_when_no_order_is_open_on_the_plant.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_suite_knows_its_database.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_supervisor_writes_a_rule_as_a_sentence.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_supervisors_screen_reads_the_shift_and_writes_a_rule.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_trend_graph_walks_link_three_of_the_chain.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_wheel_ships_the_assistant_folder.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_the_wheel_ships_the_lab_packs.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_themes.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_triage.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_triggers.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_two_plants_from_two_shapes_keep_each_plants_own_shape.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_two_tags_on_one_axis_and_the_records_behind_a_point.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_ui_check.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_ui_lists_filter_at_scale.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_ui_nav.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_ui_oee_above_rated.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_uns.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_walkthroughs.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_washer_story.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_web.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_what_a_maintenance_job_needs_of_the_line.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_wip.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/test_workorders.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/ui/baselines/control-room.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/ui/baselines/daylight.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/ui/baselines/high-contrast.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/ui/baselines/night-shift.json +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/tests/unknown_mes.py +0 -0
- {factorysemantics_mes-0.3.1 → factorysemantics_mes-0.4.0}/uv.lock +0 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Two nodes of one plant on one box, started and stopped by the fleet.
|
|
3
|
+
|
|
4
|
+
Scott, 2026-10-15, about the release this is part of: *"must run on
|
|
5
|
+
windows."* Nobody on this project has a Windows machine, so this script is
|
|
6
|
+
how the two-node machinery is read on one: a pack that names its nodes, one
|
|
7
|
+
PostgreSQL, one machine layer, and the three fleet verbs a person types.
|
|
8
|
+
|
|
9
|
+
What it proves, in the order it does it:
|
|
10
|
+
|
|
11
|
+
1. `fsmes pack check` accepts a pack that names two nodes.
|
|
12
|
+
2. `fsmes fleet create` builds the plant once - two nodes are one plant and
|
|
13
|
+
one database - and records that this installation owns it.
|
|
14
|
+
3. `fsmes fleet start` starts **both** nodes, each with its own node name,
|
|
15
|
+
its own port, its own pid file and its own log directory.
|
|
16
|
+
4. Both nodes answer `/health`, each saying its own name.
|
|
17
|
+
5. Every role - the reader, the floor, the housekeeping - is held by exactly
|
|
18
|
+
one of them, and `/health` on either node says the same thing about who
|
|
19
|
+
holds what, because the holders come from the database both nodes share.
|
|
20
|
+
6. `fsmes fleet status` says two nodes answered and names what each holds.
|
|
21
|
+
7. `fsmes fleet stop` stops both, and neither port answers afterwards.
|
|
22
|
+
|
|
23
|
+
What it does **not** prove is the kill: the takeover, the lease units and the
|
|
24
|
+
counters that must not be booked twice are proven by
|
|
25
|
+
`tests/test_a_plant_on_two_nodes_survives_one_of_them_dying.py` and by
|
|
26
|
+
`scripts/kill-a-node.sh`, both on Linux. `Popen.terminate()` on Windows does
|
|
27
|
+
not go through the supervisor's own shutdown, so a killed node there is a
|
|
28
|
+
different mechanism from a killed node here and deserves its own proof
|
|
29
|
+
rather than a claim made by this script.
|
|
30
|
+
|
|
31
|
+
It runs on either platform - it is proven on Linux before it is pushed, and
|
|
32
|
+
it is the Windows runner it exists for.
|
|
33
|
+
|
|
34
|
+
python .github/scripts/two_nodes_on_one_box.py \
|
|
35
|
+
--database-url postgresql+psycopg://user:pw@127.0.0.1:5432/two_nodes
|
|
36
|
+
|
|
37
|
+
Every path goes through `pathlib`, every child's output is read as UTF-8, and
|
|
38
|
+
nothing here uses a shell, a signal or a process group: the three things that
|
|
39
|
+
are not the same on both platforms.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from __future__ import annotations
|
|
43
|
+
|
|
44
|
+
import argparse
|
|
45
|
+
import json
|
|
46
|
+
import os
|
|
47
|
+
import re
|
|
48
|
+
import shutil
|
|
49
|
+
import subprocess
|
|
50
|
+
import sys
|
|
51
|
+
import tempfile
|
|
52
|
+
import time
|
|
53
|
+
import urllib.error
|
|
54
|
+
import urllib.request
|
|
55
|
+
from pathlib import Path
|
|
56
|
+
|
|
57
|
+
ROOT = Path(__file__).resolve().parents[2]
|
|
58
|
+
#: The variable the pack names for the thing that signs sessions. Both nodes
|
|
59
|
+
#: read the same one: a person signed in on one node is signed in on the
|
|
60
|
+
#: plant, and two of them would sign that person out every time the proxy
|
|
61
|
+
#: sent them to the other node.
|
|
62
|
+
#:
|
|
63
|
+
#: Its name says `signer` and not `secret` on purpose. The pack names the
|
|
64
|
+
#: variable and never the value, so this constant is a variable name and
|
|
65
|
+
#: nothing private - but code scanning reads a name with `secret` in it as a
|
|
66
|
+
#: credential, and a scanner that cries wolf on a CI script is a scanner
|
|
67
|
+
#: nobody reads on the day it is right (`py/clear-text-storage-sensitive-data`,
|
|
68
|
+
#: PR #178).
|
|
69
|
+
SIGNING_VARIABLE = "FSMES_TWO_NODE_SIGNER"
|
|
70
|
+
#: Short enough that a role is claimed while this script is watching, long
|
|
71
|
+
#: enough that a busy runner does not drop a renewal. Every wait below is a
|
|
72
|
+
#: multiple of it, never a number of seconds chosen for its own sake.
|
|
73
|
+
LEASE_SECONDS = 6.0
|
|
74
|
+
ROLES = ("reader", "floor", "housekeeping")
|
|
75
|
+
#: This interpreter's own `fsmes`. Not the console script: on Windows that
|
|
76
|
+
#: lives in a `Scripts` directory that is on PATH only if the installer put
|
|
77
|
+
#: it there, and `python -m fsmes` is the same program either way.
|
|
78
|
+
MES = [sys.executable, "-m", "fsmes"]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def say(line: str = "") -> None:
|
|
82
|
+
print(line, flush=True)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def fail(why: str) -> None:
|
|
86
|
+
say("")
|
|
87
|
+
say(f"NOT PROVEN: {why}")
|
|
88
|
+
raise SystemExit(1)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# ------------------------------------------------------------------ the pack
|
|
92
|
+
|
|
93
|
+
def pack_with_two_nodes(where: Path, *, database_url: str, port_a: int, port_b: int,
|
|
94
|
+
opc_endpoint: str) -> Path:
|
|
95
|
+
"""The lab's bottling pack with its nodes named, and nothing else changed.
|
|
96
|
+
|
|
97
|
+
Patched in the four places that make it this script's: where the machine
|
|
98
|
+
layer is, where the database is, that it runs on two nodes, and the name
|
|
99
|
+
of the variable its signing key lives in. The replay data is the
|
|
100
|
+
reference hour beside it, which generates in a moment.
|
|
101
|
+
"""
|
|
102
|
+
pack = where / "bottling"
|
|
103
|
+
shutil.copytree(ROOT / "labs" / "multiplant" / "bottling", pack)
|
|
104
|
+
shutil.copy(ROOT / "labs" / "kepsim" / "line.json", pack / "line.json")
|
|
105
|
+
|
|
106
|
+
toml = (pack / "plant.toml").read_text(encoding="utf-8")
|
|
107
|
+
toml = toml.replace('opc_endpoint = "opc.tcp://127.0.0.1:4841/fsmes/bottling"',
|
|
108
|
+
f'opc_endpoint = "{opc_endpoint}"')
|
|
109
|
+
toml = toml.replace("api_port = 8010", f"api_port = {port_a}")
|
|
110
|
+
toml = toml.replace("simulate = true",
|
|
111
|
+
f'simulate = true\nsecret_key_env = "{SIGNING_VARIABLE}"\n\n'
|
|
112
|
+
f'[[serve.nodes]]\nid = "a"\napi_port = {port_a}\n\n'
|
|
113
|
+
f'[[serve.nodes]]\nid = "b"\napi_port = {port_b}\n')
|
|
114
|
+
# A pack a person writes names `database_password_file` and keeps the
|
|
115
|
+
# password out of the file that says what the plant is. This one splices
|
|
116
|
+
# the whole URL because that is what it was given, into a directory that
|
|
117
|
+
# is deleted at the end.
|
|
118
|
+
toml += (f'\n[storage]\ndatabase_url = "{database_url}"\n'
|
|
119
|
+
f'\n[redundancy]\nenabled = true\nlease_seconds = {LEASE_SECONDS}\n')
|
|
120
|
+
(pack / "plant.toml").write_text(toml, encoding="utf-8")
|
|
121
|
+
return pack
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
# --------------------------------------------------------------- the asking
|
|
125
|
+
|
|
126
|
+
def get(url: str, timeout: float = 4.0) -> tuple[int, str]:
|
|
127
|
+
try:
|
|
128
|
+
with urllib.request.urlopen(url, timeout=timeout) as answer:
|
|
129
|
+
return answer.status, answer.read().decode("utf-8", "replace")
|
|
130
|
+
except urllib.error.HTTPError as refused:
|
|
131
|
+
return refused.code, ""
|
|
132
|
+
except Exception:
|
|
133
|
+
return 0, ""
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def health(port: int) -> dict:
|
|
137
|
+
status, body = get(f"http://127.0.0.1:{port}/health")
|
|
138
|
+
if status != 200 or not body:
|
|
139
|
+
return {}
|
|
140
|
+
try:
|
|
141
|
+
return json.loads(body)
|
|
142
|
+
except ValueError:
|
|
143
|
+
return {}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def answers(port: int, timeout: float) -> dict:
|
|
147
|
+
"""Wait for one node's dashboard to answer, and say what it said."""
|
|
148
|
+
until = time.monotonic() + timeout
|
|
149
|
+
while time.monotonic() < until:
|
|
150
|
+
body = health(port)
|
|
151
|
+
if body:
|
|
152
|
+
return body
|
|
153
|
+
time.sleep(0.5)
|
|
154
|
+
return {}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def holders(body: dict) -> dict:
|
|
158
|
+
"""role -> the node holding it, as `/health` reports it."""
|
|
159
|
+
node = body.get("node") or {}
|
|
160
|
+
return {role: (node.get("holders") or {}).get(role) for role in ROLES}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def wait_for_every_role(port: int, timeout: float) -> dict:
|
|
164
|
+
"""Wait until every role has a holder, and say who holds what.
|
|
165
|
+
|
|
166
|
+
Each role is a race of its own - the reader, the floor and the
|
|
167
|
+
housekeeping are claimed by three different processes - so this waits for
|
|
168
|
+
all three to be claimed rather than for one node to claim all three.
|
|
169
|
+
"""
|
|
170
|
+
until = time.monotonic() + timeout
|
|
171
|
+
held: dict = {}
|
|
172
|
+
while time.monotonic() < until:
|
|
173
|
+
held = holders(health(port))
|
|
174
|
+
if all(held.get(role) for role in ROLES):
|
|
175
|
+
return held
|
|
176
|
+
time.sleep(0.5)
|
|
177
|
+
return held
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
# ------------------------------------------------------------- the commands
|
|
181
|
+
|
|
182
|
+
def fsmes(*args: str, env: dict, where: Path | None = None, timeout: float = 600.0):
|
|
183
|
+
"""One `fsmes` command, its output read as UTF-8 and printed."""
|
|
184
|
+
done = subprocess.run([*MES, *args], cwd=where or ROOT, env=env, capture_output=True,
|
|
185
|
+
text=True, encoding="utf-8", errors="replace", timeout=timeout)
|
|
186
|
+
for line in (done.stdout or "").splitlines():
|
|
187
|
+
say(f" | {line}")
|
|
188
|
+
if done.returncode != 0:
|
|
189
|
+
for line in (done.stderr or "").splitlines():
|
|
190
|
+
say(f" ! {line}")
|
|
191
|
+
return done
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def main() -> int:
|
|
195
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
196
|
+
parser.add_argument("--database-url", required=True,
|
|
197
|
+
help="a PostgreSQL both nodes share; the database must exist")
|
|
198
|
+
parser.add_argument("--port-a", type=int, default=8010)
|
|
199
|
+
parser.add_argument("--port-b", type=int, default=8011)
|
|
200
|
+
parser.add_argument("--opc-port", type=int, default=4841)
|
|
201
|
+
parser.add_argument("--speed", type=float, default=60.0,
|
|
202
|
+
help="line seconds per wall second for the replay")
|
|
203
|
+
parser.add_argument("--workdir", default="",
|
|
204
|
+
help="where the pack, the data and the logs go (default: a temp dir)")
|
|
205
|
+
parser.add_argument("--keep-running", action="store_true",
|
|
206
|
+
help="leave both nodes up at the end, for a person to look at")
|
|
207
|
+
args = parser.parse_args()
|
|
208
|
+
|
|
209
|
+
if "postgresql" not in args.database_url:
|
|
210
|
+
fail("two nodes share one database, which a file database cannot be: "
|
|
211
|
+
"--database-url must be a PostgreSQL URL")
|
|
212
|
+
|
|
213
|
+
where = Path(args.workdir).resolve() if args.workdir else Path(tempfile.mkdtemp(
|
|
214
|
+
prefix="fsmes-two-nodes-"))
|
|
215
|
+
where.mkdir(parents=True, exist_ok=True)
|
|
216
|
+
root = where / "run"
|
|
217
|
+
root.mkdir(parents=True, exist_ok=True)
|
|
218
|
+
logs = where / "logs"
|
|
219
|
+
logs.mkdir(parents=True, exist_ok=True)
|
|
220
|
+
endpoint = f"opc.tcp://127.0.0.1:{args.opc_port}/fsmes/bottling-two-nodes"
|
|
221
|
+
|
|
222
|
+
env = dict(os.environ)
|
|
223
|
+
env[SIGNING_VARIABLE] = "the-same-signer-on-both-nodes"
|
|
224
|
+
env["PYTHONIOENCODING"] = "utf-8"
|
|
225
|
+
|
|
226
|
+
say(f"Two nodes of one plant on {sys.platform}, in {where}")
|
|
227
|
+
pack = pack_with_two_nodes(where, database_url=args.database_url, port_a=args.port_a,
|
|
228
|
+
port_b=args.port_b, opc_endpoint=endpoint)
|
|
229
|
+
say(" the line data the replay plays")
|
|
230
|
+
if fsmes("sim-generate", str(pack / "line.json"), "--out", str(pack / "out"),
|
|
231
|
+
env=env).returncode != 0:
|
|
232
|
+
fail("the line data would not generate")
|
|
233
|
+
|
|
234
|
+
say(" fsmes pack check, on a pack that names two nodes")
|
|
235
|
+
if fsmes("pack", "check", str(pack), env=env).returncode != 0:
|
|
236
|
+
fail("the checker refused a pack naming two nodes")
|
|
237
|
+
|
|
238
|
+
replay = None
|
|
239
|
+
started_nodes = False
|
|
240
|
+
replay_log = logs / "replay.log"
|
|
241
|
+
try:
|
|
242
|
+
say(" the machine layer: one OPC replay for the whole plant")
|
|
243
|
+
sim_env = dict(env)
|
|
244
|
+
sim_env.update(MES_PLANT_NAME="bottling", MES_OPC_ENDPOINT=endpoint,
|
|
245
|
+
MES_REPLAY_DIR=str(pack / "out"),
|
|
246
|
+
MES_TAG_MAP_FILE=str(pack / "tag_map.json"),
|
|
247
|
+
MES_SIM_SPEED=str(args.speed), MES_LOG_DIR=str(logs))
|
|
248
|
+
with replay_log.open("ab") as sink:
|
|
249
|
+
replay = subprocess.Popen([*MES, "run-opc-sim", "--replay"], cwd=ROOT, env=sim_env,
|
|
250
|
+
stdout=sink, stderr=subprocess.STDOUT)
|
|
251
|
+
until = time.monotonic() + 90
|
|
252
|
+
while time.monotonic() < until:
|
|
253
|
+
if "replay online" in replay_log.read_text(encoding="utf-8", errors="replace"):
|
|
254
|
+
break
|
|
255
|
+
if replay.poll() is not None:
|
|
256
|
+
fail(f"the machine layer exited ({replay.returncode}); see {replay_log}")
|
|
257
|
+
time.sleep(0.25)
|
|
258
|
+
else:
|
|
259
|
+
fail(f"the machine layer never came online; see {replay_log}")
|
|
260
|
+
say(" | replay online")
|
|
261
|
+
|
|
262
|
+
say(" fsmes fleet create, once, for one plant on two nodes")
|
|
263
|
+
if fsmes("fleet", "create", str(pack), "--root", str(root), env=env).returncode != 0:
|
|
264
|
+
fail("the plant would not build from a pack naming two nodes")
|
|
265
|
+
|
|
266
|
+
say(" fsmes fleet start, which starts every node the pack names")
|
|
267
|
+
started = fsmes("fleet", "start", "bottling", "--root", str(root),
|
|
268
|
+
"--speed", str(args.speed), env=env)
|
|
269
|
+
started_nodes = True
|
|
270
|
+
if started.returncode != 0:
|
|
271
|
+
fail("fleet start refused a plant with two nodes")
|
|
272
|
+
for node in ("a", "b"):
|
|
273
|
+
if f"node {node}" not in started.stdout:
|
|
274
|
+
fail(f"fleet start said nothing about node {node}:\n{started.stdout}")
|
|
275
|
+
|
|
276
|
+
say(" both nodes answer /health, each with its own name")
|
|
277
|
+
seen = {}
|
|
278
|
+
for node, port in (("a", args.port_a), ("b", args.port_b)):
|
|
279
|
+
body = answers(port, timeout=180)
|
|
280
|
+
if not body:
|
|
281
|
+
fail(f"node {node} never answered on port {port}")
|
|
282
|
+
said = (body.get("node") or {}).get("name")
|
|
283
|
+
if said != node:
|
|
284
|
+
fail(f"the node on port {port} calls itself {said!r}, not {node!r}")
|
|
285
|
+
seen[node] = body
|
|
286
|
+
say(f" | node {node} answered on {port}, redundancy "
|
|
287
|
+
f"{(body.get('node') or {}).get('redundancy')}")
|
|
288
|
+
|
|
289
|
+
say(" every role is held by exactly one node, and both nodes agree")
|
|
290
|
+
held = wait_for_every_role(args.port_a, timeout=LEASE_SECONDS * 20)
|
|
291
|
+
missing = [role for role in ROLES if not held.get(role)]
|
|
292
|
+
if missing:
|
|
293
|
+
fail(f"nobody held {', '.join(missing)} within 20 leases; "
|
|
294
|
+
f"what was held: {held}")
|
|
295
|
+
for role, holder in held.items():
|
|
296
|
+
if holder not in ("a", "b"):
|
|
297
|
+
fail(f"{role} is held by {holder!r}, which is neither node")
|
|
298
|
+
say(f" | {role} is held by node {holder}")
|
|
299
|
+
from_b = holders(health(args.port_b))
|
|
300
|
+
if from_b != held:
|
|
301
|
+
fail(f"the nodes disagree about who holds what: a says {held}, b says {from_b}")
|
|
302
|
+
say(" | node b says the same, because the holders are in the database")
|
|
303
|
+
|
|
304
|
+
say(" fsmes fleet status names both nodes and what they hold")
|
|
305
|
+
status = fsmes("fleet", "status", "bottling", "--root", str(root), env=env)
|
|
306
|
+
if status.returncode != 0:
|
|
307
|
+
fail("fleet status refused a plant with two nodes")
|
|
308
|
+
if not re.search(r"nodes\s+2, 2 answered", status.stdout):
|
|
309
|
+
fail(f"fleet status did not say two nodes answered:\n{status.stdout}")
|
|
310
|
+
for node in ("a", "b"):
|
|
311
|
+
if not re.search(rf"^\s+{node}\s+\S+.*holding ", status.stdout, re.MULTILINE):
|
|
312
|
+
fail(f"fleet status said nothing about node {node}:\n{status.stdout}")
|
|
313
|
+
|
|
314
|
+
say(" fsmes fleet stop stops both")
|
|
315
|
+
if args.keep_running:
|
|
316
|
+
say(f" | --keep-running: both nodes are up on {args.port_a} and "
|
|
317
|
+
f"{args.port_b}, the machine layer is still playing on "
|
|
318
|
+
f"{endpoint} (pid {replay.pid}), and {root} is theirs. Stop "
|
|
319
|
+
f"them with `fsmes fleet stop bottling --root {root}`, and "
|
|
320
|
+
f"the machine layer by its pid.")
|
|
321
|
+
return 0
|
|
322
|
+
stopped = fsmes("fleet", "stop", "bottling", "--root", str(root), env=env)
|
|
323
|
+
if stopped.returncode != 0:
|
|
324
|
+
fail("fleet stop refused a plant with two nodes")
|
|
325
|
+
started_nodes = False
|
|
326
|
+
for node, port in (("a", args.port_a), ("b", args.port_b)):
|
|
327
|
+
if f"node {node}" not in stopped.stdout:
|
|
328
|
+
fail(f"fleet stop said nothing about node {node}:\n{stopped.stdout}")
|
|
329
|
+
until = time.monotonic() + 60
|
|
330
|
+
while time.monotonic() < until and health(port):
|
|
331
|
+
time.sleep(0.5)
|
|
332
|
+
if health(port):
|
|
333
|
+
fail(f"node {node} was still answering on {port} after fleet stop")
|
|
334
|
+
say(f" | node {node} is gone from {port}")
|
|
335
|
+
|
|
336
|
+
say("")
|
|
337
|
+
say("PROVEN on this box: a pack that names two nodes is checked, built once, "
|
|
338
|
+
"started as two nodes that each answer for themselves, has every role held "
|
|
339
|
+
"by exactly one of them, and is stopped by one command. The kill is proven "
|
|
340
|
+
"on Linux and is not claimed here.")
|
|
341
|
+
return 0
|
|
342
|
+
finally:
|
|
343
|
+
# A failure before the stop step leaves two nodes answering, which on
|
|
344
|
+
# a runner is a job that never ends and on a person's box is two
|
|
345
|
+
# plants they did not ask for. `--keep-running` is the one case where
|
|
346
|
+
# they are meant to stay up.
|
|
347
|
+
if started_nodes and not args.keep_running:
|
|
348
|
+
fsmes("fleet", "stop", "bottling", "--root", str(root), env=env, timeout=120)
|
|
349
|
+
if replay is not None and replay.poll() is None and not args.keep_running:
|
|
350
|
+
# `--keep-running` keeps the machine layer too: two nodes with
|
|
351
|
+
# nothing to read are a plant whose machines all say
|
|
352
|
+
# *disconnected*, which is not what the person asked to look at.
|
|
353
|
+
replay.terminate()
|
|
354
|
+
try:
|
|
355
|
+
replay.wait(timeout=30)
|
|
356
|
+
except subprocess.TimeoutExpired:
|
|
357
|
+
replay.kill()
|
|
358
|
+
for log in sorted(logs.rglob("*.log")) + sorted((root / "logs").rglob("*.jsonl")):
|
|
359
|
+
say("")
|
|
360
|
+
say(f"--- the last 40 lines of {log}")
|
|
361
|
+
try:
|
|
362
|
+
for line in log.read_text(encoding="utf-8", errors="replace").splitlines()[-40:]:
|
|
363
|
+
say(f" {line}")
|
|
364
|
+
except OSError as unreadable:
|
|
365
|
+
say(f" (unreadable: {unreadable})")
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
if __name__ == "__main__":
|
|
369
|
+
raise SystemExit(main())
|
|
@@ -493,6 +493,128 @@ jobs:
|
|
|
493
493
|
esac
|
|
494
494
|
done
|
|
495
495
|
|
|
496
|
+
two-nodes-in-compose:
|
|
497
|
+
# The two-box demo, killed on purpose, on every pull request.
|
|
498
|
+
#
|
|
499
|
+
# `docker/docker-compose.redundant.yml` is one PostgreSQL, one machine
|
|
500
|
+
# layer, two nodes of the bottling plant and nginx in front of them, and
|
|
501
|
+
# `scripts/kill-a-node.sh` kills one node and checks the other carried
|
|
502
|
+
# on: the reader lease moved within three leases, the counters kept
|
|
503
|
+
# moving, every machine booked exactly what it counted, no reading was
|
|
504
|
+
# stored twice, and the one address kept answering. It is the same claim
|
|
505
|
+
# `postgres`'s two-node test makes, made through containers and a proxy
|
|
506
|
+
# instead of two processes - which is what anybody who tries this product
|
|
507
|
+
# will actually run.
|
|
508
|
+
#
|
|
509
|
+
# Linux only. `docker compose` on the Windows runner has no Linux daemon
|
|
510
|
+
# to run these images on, and a plant engineer's Windows box is covered by
|
|
511
|
+
# `half-dead-plant` and `two-nodes-on-windows` instead.
|
|
512
|
+
#
|
|
513
|
+
# Behind `image`: there is nothing to learn from a compose demo of an
|
|
514
|
+
# image that does not build, and the layer cache that job wrote makes the
|
|
515
|
+
# build here a cache hit rather than a second four-minute pip install.
|
|
516
|
+
# The timing this job asserts is in lease units measured against the
|
|
517
|
+
# database's own clock, never in seconds on a shared runner.
|
|
518
|
+
needs: image
|
|
519
|
+
runs-on: ubuntu-latest
|
|
520
|
+
timeout-minutes: 25
|
|
521
|
+
steps:
|
|
522
|
+
- uses: actions/checkout@v7
|
|
523
|
+
- uses: docker/setup-buildx-action@v4
|
|
524
|
+
- name: Build the image the compose file names
|
|
525
|
+
uses: docker/build-push-action@v7
|
|
526
|
+
with:
|
|
527
|
+
context: .
|
|
528
|
+
file: docker/Dockerfile
|
|
529
|
+
platforms: linux/amd64
|
|
530
|
+
push: false
|
|
531
|
+
load: true
|
|
532
|
+
tags: factorysemantics-mes:two-nodes
|
|
533
|
+
# Read-only: `image` owns this scope, and two jobs writing one
|
|
534
|
+
# scope evict each other's layers.
|
|
535
|
+
cache-from: type=gha,scope=ci-image
|
|
536
|
+
|
|
537
|
+
- name: Kill one of two nodes and check the plant carried on
|
|
538
|
+
run: bash scripts/kill-a-node.sh
|
|
539
|
+
|
|
540
|
+
- name: What the containers said
|
|
541
|
+
# Always, pass or fail: a failover nobody can read the logs of is a
|
|
542
|
+
# green check or a red one and nothing else.
|
|
543
|
+
if: always()
|
|
544
|
+
run: |
|
|
545
|
+
docker compose -f docker/docker-compose.redundant.yml ps -a || true
|
|
546
|
+
docker compose -f docker/docker-compose.redundant.yml logs --no-color --tail 200 || true
|
|
547
|
+
|
|
548
|
+
- name: Put it away
|
|
549
|
+
if: always()
|
|
550
|
+
run: docker compose -f docker/docker-compose.redundant.yml down -v || true
|
|
551
|
+
|
|
552
|
+
two-nodes-on-windows:
|
|
553
|
+
# Scott, about this release: "must run on windows." Nobody on this
|
|
554
|
+
# project has a Windows machine, so this job is where the two-node
|
|
555
|
+
# machinery is read on one.
|
|
556
|
+
#
|
|
557
|
+
# It names its nodes in the pack, builds the plant once, starts it with
|
|
558
|
+
# `fsmes fleet start`, and checks that both nodes answer `/health` under
|
|
559
|
+
# their own names, that each of the three roles is held by exactly one of
|
|
560
|
+
# them, that `fleet status` names both, and that `fleet stop` stops both.
|
|
561
|
+
# The PostgreSQL is the one the runner image ships - installed and
|
|
562
|
+
# stopped, so the job starts it.
|
|
563
|
+
#
|
|
564
|
+
# What this job does NOT do is kill a node. `Popen.terminate()` on
|
|
565
|
+
# Windows does not go through the plant's own shutdown handler, so a
|
|
566
|
+
# killed node there is a different mechanism from a killed node on Linux
|
|
567
|
+
# and deserves a proof of its own rather than a claim made here. The
|
|
568
|
+
# lease, the takeover in lease units and the counters that must not be
|
|
569
|
+
# booked twice are proven by the `postgres` job and by
|
|
570
|
+
# `two-nodes-in-compose`, both Linux.
|
|
571
|
+
#
|
|
572
|
+
# Windows only: the Linux half of the same claim is the `postgres` job,
|
|
573
|
+
# which runs the whole two-node failover test, and running this script
|
|
574
|
+
# there as well would buy the weaker half of a stronger proof.
|
|
575
|
+
runs-on: windows-latest
|
|
576
|
+
timeout-minutes: 40
|
|
577
|
+
steps:
|
|
578
|
+
- uses: actions/checkout@v7
|
|
579
|
+
- uses: actions/setup-python@v7
|
|
580
|
+
with:
|
|
581
|
+
python-version: "3.12"
|
|
582
|
+
|
|
583
|
+
- name: Start the PostgreSQL this runner ships
|
|
584
|
+
shell: pwsh
|
|
585
|
+
run: |
|
|
586
|
+
$service = Get-Service -Name "postgresql*" | Select-Object -First 1
|
|
587
|
+
if (-not $service) { throw "this runner image has no PostgreSQL service" }
|
|
588
|
+
Write-Host "service $($service.Name) is $($service.Status)"
|
|
589
|
+
Set-Service -Name $service.Name -StartupType Manual
|
|
590
|
+
Start-Service -Name $service.Name
|
|
591
|
+
$env:PGPASSWORD = "root"
|
|
592
|
+
$ready = $false
|
|
593
|
+
foreach ($try in 1..30) {
|
|
594
|
+
& "$env:PGBIN\pg_isready.exe" -h 127.0.0.1 -p 5432 -U postgres
|
|
595
|
+
if ($LASTEXITCODE -eq 0) { $ready = $true; break }
|
|
596
|
+
Start-Sleep -Seconds 2
|
|
597
|
+
}
|
|
598
|
+
if (-not $ready) { throw "PostgreSQL never accepted a connection" }
|
|
599
|
+
& "$env:PGBIN\createdb.exe" -h 127.0.0.1 -p 5432 -U postgres two_nodes
|
|
600
|
+
if ($LASTEXITCODE -ne 0) { throw "could not create the database" }
|
|
601
|
+
|
|
602
|
+
- name: Install
|
|
603
|
+
run: python -m pip install --upgrade pip && python -m pip install -e ".[postgres]"
|
|
604
|
+
|
|
605
|
+
- name: Two nodes of one plant, started and stopped by the fleet
|
|
606
|
+
shell: bash
|
|
607
|
+
env:
|
|
608
|
+
# The runner image's own password for its own PostgreSQL. It is in
|
|
609
|
+
# the image's documentation, it is reachable from this job and from
|
|
610
|
+
# nothing else, and the database it opens is deleted with the
|
|
611
|
+
# runner.
|
|
612
|
+
PGPASSWORD: root
|
|
613
|
+
run: |
|
|
614
|
+
python .github/scripts/two_nodes_on_one_box.py \
|
|
615
|
+
--database-url "postgresql+psycopg://postgres:root@127.0.0.1:5432/two_nodes" \
|
|
616
|
+
--speed 60
|
|
617
|
+
|
|
496
618
|
lab:
|
|
497
619
|
# The lab's starter experiments, on a runner.
|
|
498
620
|
#
|
|
@@ -12,6 +12,163 @@ goes under Honesty with a migration line, so plant people can find it.
|
|
|
12
12
|
|
|
13
13
|
Nothing yet.
|
|
14
14
|
|
|
15
|
+
## [0.4.0] — 2026-10-16
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- **A plant can run on two boxes and keep counting when one of them dies.**
|
|
20
|
+
Two or more **nodes** of one plant against one PostgreSQL, with the pack
|
|
21
|
+
saying so:
|
|
22
|
+
|
|
23
|
+
```toml
|
|
24
|
+
[redundancy]
|
|
25
|
+
enabled = true
|
|
26
|
+
lease_seconds = 10
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Three jobs that must run once — reading the machines, the simulated floor,
|
|
30
|
+
and pruning the history — are held one at a time by a row in a new
|
|
31
|
+
`plant_leases` table, claimed and renewed against the **database's** clock
|
|
32
|
+
and never the box's. A holder that cannot renew stops at once and comes
|
|
33
|
+
back as a standby; the other node has the job within a lease or two. The
|
|
34
|
+
dashboard on the surviving node keeps answering, a person signed in on one
|
|
35
|
+
node is signed in on the other (the pack names the variable holding the
|
|
36
|
+
signing key, `secret_key_env`, and a plant with `[redundancy]` on refuses
|
|
37
|
+
to start without it), and production is booked **exactly once** — the
|
|
38
|
+
counters are absolute, and a new `counter_baselines` table carries each
|
|
39
|
+
one's baseline in the same transaction as the booking it belongs to, so a
|
|
40
|
+
node taking over continues the count rather than starting it again.
|
|
41
|
+
`fsmes plant <name> run --node <id> --api-port <port>` names a node and
|
|
42
|
+
moves it off the plant's own port; `/health` grows a `node` block saying
|
|
43
|
+
which node answered, how long its lease is, what it holds and who holds
|
|
44
|
+
the rest; `fsmes plant <name> status` says the same as a line. PostgreSQL
|
|
45
|
+
only: `[redundancy]` with a SQLite URL refuses in one sentence, and the
|
|
46
|
+
single-box SQLite demo is untouched. Measured in lease units on one
|
|
47
|
+
machine, not in seconds. What it does **not** do is in
|
|
48
|
+
[one plant on two boxes](docs/operate/redundancy.md) and decision 0042:
|
|
49
|
+
the front door is yours (two nodes are two addresses), the database's own
|
|
50
|
+
standby is PostgreSQL's job, the PLC write path stays single, ERP sync and
|
|
51
|
+
the UNS publisher are still one copy each, and a conversation on the AI tab
|
|
52
|
+
starts over after a failover. (#177)
|
|
53
|
+
|
|
54
|
+
- **The two-box demo, and the kill test anyone can run.**
|
|
55
|
+
`docker compose -f docker/docker-compose.redundant.yml up` brings up one
|
|
56
|
+
PostgreSQL, the simulated machines, two nodes of one plant and nginx in
|
|
57
|
+
front of both on one address; `bash scripts/kill-a-node.sh` brings it up,
|
|
58
|
+
waits for the counts to move, kills a node outright, watches the one
|
|
59
|
+
address until the other node holds the reader, and prints the takeover in
|
|
60
|
+
lease units with a check that nothing was booked twice and nothing was
|
|
61
|
+
lost. A CI job (`two-nodes-in-compose`) runs that same script on every
|
|
62
|
+
pull request. On this product's own box on 2026-10-16 the reader was on the
|
|
63
|
+
second node after 1.13 leases of 6 s, 0 of the requests asked during the
|
|
64
|
+
failover were refused, all 6 machines booked exactly what their counters
|
|
65
|
+
moved, and 0 readings were booked twice. (#178)
|
|
66
|
+
|
|
67
|
+
- **The screens say which box holds what.** The line beside the plant's name
|
|
68
|
+
on the dashboard — *served by b · machines read by a* — used to be written
|
|
69
|
+
once when the page loaded, which left it naming a dead node until somebody
|
|
70
|
+
reloaded; it now follows a takeover on the page's own poll, within five
|
|
71
|
+
seconds and with no reload, and only on a plant that says it runs on more
|
|
72
|
+
than one node (a single-node plant starts no timer and makes no request it
|
|
73
|
+
did not make before). The [fleet console](docs/operate/fleet.md) shows such
|
|
74
|
+
a plant once with its nodes under it — how many there are and how many
|
|
75
|
+
answered, then one line each saying where that node is and what it holds in
|
|
76
|
+
words — and `fsmes fleet status`, `fsmes fleet list` and `/fleet.json`
|
|
77
|
+
carry the same. A node that did not answer is **unknown** there, never a
|
|
78
|
+
node holding nothing. (#178)
|
|
79
|
+
|
|
80
|
+
- **A pack may name the nodes that run on one box**, which is the cheap way
|
|
81
|
+
to see all of this before you own the second box:
|
|
82
|
+
|
|
83
|
+
```toml
|
|
84
|
+
[[serve.nodes]]
|
|
85
|
+
id = "a"
|
|
86
|
+
api_port = 8010
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`fsmes fleet start`, `stop`, `status`, `list` and `apply` then act on every
|
|
90
|
+
node the pack names, each with its own pid file and its own log directory,
|
|
91
|
+
and `fsmes pack check` refuses the four ways a node list half-starts a
|
|
92
|
+
plant (two nodes sharing a port or a name, nodes named with `[redundancy]`
|
|
93
|
+
off, no signing key between them). A pack that names no nodes is the
|
|
94
|
+
ordinary plant on one address and behaves exactly as it did. (#178)
|
|
95
|
+
|
|
96
|
+
- **`fsmes fleet start` starts the machines a two-node simulated plant
|
|
97
|
+
shares, and `fleet stop` stops them.** A simulated plant on one node starts
|
|
98
|
+
its own OPC replay; a pack that named nodes deliberately did not, because a
|
|
99
|
+
replay started inside a node dies with that node and the surviving reader
|
|
100
|
+
then sees the line start over — which reads as a PLC reset and books
|
|
101
|
+
nothing. Nobody started it instead: after the four commands in
|
|
102
|
+
[one plant on two boxes](docs/operate/redundancy.md) the console said
|
|
103
|
+
*6 of 6 disconnected* and nothing was booked. The plant now starts it once,
|
|
104
|
+
before either node, with a log directory of its own; it is left playing
|
|
105
|
+
when it is already playing, both when this installation's own pid file
|
|
106
|
+
names it and when anything else already answers on the endpoint the pack
|
|
107
|
+
names, so a replay you started by hand — or the one `docker compose` owns —
|
|
108
|
+
is never stopped by `fsmes fleet stop`. A plant whose pack says
|
|
109
|
+
`simulate = false` starts nothing here: its machine layer is a PLC somebody
|
|
110
|
+
else runs.
|
|
111
|
+
|
|
112
|
+
- **`fsmes fleet stop <plant> --node <id>`** stops one node of a plant and
|
|
113
|
+
leaves the others and the shared machines running, for taking one box down
|
|
114
|
+
on purpose. It is a graceful stop — the node is asked, then told, and it
|
|
115
|
+
lets go of its leases on the way out — so it is not the proof that a plant
|
|
116
|
+
survives a box dying: that proof is a `kill`, and the docs say which is
|
|
117
|
+
which. A node the pack does not name is refused by name, with the nodes it
|
|
118
|
+
does have.
|
|
119
|
+
|
|
120
|
+
### Fixed
|
|
121
|
+
|
|
122
|
+
- **A plant whose first node died could not be stopped.** `fsmes fleet stop`
|
|
123
|
+
asked one address — the one in the ownership record — and counted a pid
|
|
124
|
+
file a plant with nodes never writes, so a plant running happily on its
|
|
125
|
+
second node refused to stop at all: *nothing in its pid file is alive, so
|
|
126
|
+
there may be nothing left to stop*, with three live processes. `stop`,
|
|
127
|
+
`apply` and `start` now ask every node the pack names, and a refusal counts
|
|
128
|
+
every node's pid file and says how many it read. (#178)
|
|
129
|
+
|
|
130
|
+
- **The console's row for a plant running on its second node was `unknown`
|
|
131
|
+
in every column** — no label, no schema, no machines, no orders — because
|
|
132
|
+
the row was filled from the address in the pack, which was the node that
|
|
133
|
+
had died. A plant with nodes is now read at whichever node answers, and the
|
|
134
|
+
silent node is still named as silent on its own line underneath. The node
|
|
135
|
+
that answered is asked the same ownership question as the first address, so
|
|
136
|
+
a port answering as something else is refused rather than believed. (#178)
|
|
137
|
+
|
|
138
|
+
- **`fsmes fleet apply` to a book with nothing in it leaves accounts in it.**
|
|
139
|
+
Pointing an existing pack at a new PostgreSQL ran the migrations and seeded
|
|
140
|
+
the plant's master data and stopped there: every sign-in answered *invalid
|
|
141
|
+
credentials*, and on a simulated plant both floor processes died saying
|
|
142
|
+
*could not sign in* with nothing saying why. `apply` now makes the accounts
|
|
143
|
+
`fsmes fleet create` makes, in the one case where it cannot be taking
|
|
144
|
+
anybody's decision away — a book with no account in it at all — and says on
|
|
145
|
+
a line of its own that it did. A plant that has accounts is left alone:
|
|
146
|
+
`apply` is not the command that brings back one you deleted. And the
|
|
147
|
+
floor's own line now names the likeliest cause, which is either a plant
|
|
148
|
+
that answered and refused the credentials or a plant that never answered
|
|
149
|
+
at all.
|
|
150
|
+
|
|
151
|
+
### Honesty
|
|
152
|
+
|
|
153
|
+
- **A restart no longer re-books what was already booked, on one box or
|
|
154
|
+
two.** Production is counted from absolute machine counters, and until now
|
|
155
|
+
the baseline each counter was measured from lived in the reader's memory:
|
|
156
|
+
a reader that restarted treated the counter's whole value as new
|
|
157
|
+
production. One migration, `b6f0c2a71d58`, adds `plant_leases` and
|
|
158
|
+
`counter_baselines`; nothing to do on a single-box plant, where the two
|
|
159
|
+
tables sit unused until `[redundancy]` is turned on.
|
|
160
|
+
|
|
161
|
+
- **Windows: what is and is not proven.** A CI job
|
|
162
|
+
(`two-nodes-on-windows`) builds a two-node pack, checks it, creates it,
|
|
163
|
+
starts it and stops it, and holds that both nodes answer under their own
|
|
164
|
+
names and that each of the three roles is held by exactly one of them. The
|
|
165
|
+
**kill is not proven on Windows**: `Popen.terminate()` there does not go
|
|
166
|
+
through the plant's own shutdown handler, so a node killed that way stops
|
|
167
|
+
by a different mechanism than a node whose power is cut, and the takeover
|
|
168
|
+
that follows is a different thing to measure. The lease, the takeover in
|
|
169
|
+
lease units and the counts that must not be booked twice are proven on
|
|
170
|
+
Linux only.
|
|
171
|
+
|
|
15
172
|
## [0.3.1] — 2026-10-15
|
|
16
173
|
|
|
17
174
|
### Added
|
|
@@ -4907,7 +5064,8 @@ number in it comes from a simulated plant.
|
|
|
4907
5064
|
MES was not watching. Unlabelled downtime is reported as unlabelled. These
|
|
4908
5065
|
are the house rules and they are tested.
|
|
4909
5066
|
|
|
4910
|
-
[Unreleased]: https://github.com/factorysemantics/factorysemantics-mes/compare/v0.
|
|
5067
|
+
[Unreleased]: https://github.com/factorysemantics/factorysemantics-mes/compare/v0.4.0...HEAD
|
|
5068
|
+
[0.4.0]: https://github.com/factorysemantics/factorysemantics-mes/releases/tag/v0.4.0
|
|
4911
5069
|
[0.3.1]: https://github.com/factorysemantics/factorysemantics-mes/releases/tag/v0.3.1
|
|
4912
5070
|
[0.3.0]: https://github.com/factorysemantics/factorysemantics-mes/releases/tag/v0.3.0
|
|
4913
5071
|
[0.2.0]: https://github.com/factorysemantics/factorysemantics-mes/releases/tag/v0.2.0
|