From 5c552adfa94d5124cf783ada3aab0fe460ba5292 Mon Sep 17 00:00:00 2001 From: Aldon Smith Date: Tue, 26 May 2026 20:01:52 -0400 Subject: [PATCH] docs: update runtime docs for issue 233 --- FILE_INDEX.md | 6 +- README.md | 381 ++++--------- apps/web/README.md | 191 ++----- apps/web/tests/app-shell.test.mjs | 6 +- docs/API_DESIGN.md | 4 +- docs/ARCHITECTURE.md | 36 +- docs/CONNECTOR_SAFETY_MODEL.md | 7 +- docs/DEMO_RUNBOOK.md | 413 ++------------ docs/DEVELOPMENT.md | 38 +- docs/DOMAIN_MODEL.md | 4 +- docs/EVIDENCE_TIMELINE_API_CONTRACT.md | 6 +- docs/GOVERNED_ACTIONS.md | 8 +- docs/LEARNING_LOG.md | 65 +++ docs/MVP_SCOPE.md | 63 +-- docs/SECURITY_MODEL.md | 6 +- docs/START_HERE_FOR_CODEX.md | 22 +- docs/TESTING.md | 35 +- docs/demo/DEMO_SAFE_COPY_GUIDELINES.md | 187 +------ docs/demo/MANUFACTURER_DEMO_RUNBOOK.md | 450 +-------------- docs/demo/MANUFACTURER_DEMO_USER_JOURNEY.md | 154 +---- docs/demo/OPC_UA_DEMO_NAMESPACE.md | 243 +------- .../demo/OPERATIONS_WORKBENCH_DEMO_RUNBOOK.md | 136 +---- ...IONS_WORKBENCH_INFORMATION_ARCHITECTURE.md | 137 +---- docs/demo/PRE_DEMO_CHECKLIST.md | 149 +---- docs/demo/TROUBLESHOOTING.md | 412 +------------- prompts/03-synthetic-factory-simulator.md | 46 +- prompts/README.md | 2 +- services/api/README.md | 12 +- services/ingestion/README.md | 526 ++---------------- services/process-sentinel/README.md | 29 +- services/simulator/README.md | 349 +----------- .../factory_simulator/opcua_server.py | 2 +- .../test_demo_safe_copy_guidelines_docs.py | 137 ----- .../tests/test_demo_troubleshooting_docs.py | 89 --- .../test_manufacturer_demo_runbook_docs.py | 107 ---- ...est_manufacturer_demo_user_journey_docs.py | 120 ---- .../tests/test_opc_ua_demo_namespace_docs.py | 143 ----- .../simulator/tests/test_opcua_demo_server.py | 20 +- .../test_operations_workbench_ia_docs.py | 111 ---- .../tests/test_pre_demo_checklist_docs.py | 62 --- .../tests/test_readme_demo_workflow_docs.py | 82 --- .../tests/test_runtime_docs_transition.py | 98 ++++ 42 files changed, 786 insertions(+), 4308 deletions(-) delete mode 100644 services/simulator/tests/test_demo_safe_copy_guidelines_docs.py delete mode 100644 services/simulator/tests/test_demo_troubleshooting_docs.py delete mode 100644 services/simulator/tests/test_manufacturer_demo_runbook_docs.py delete mode 100644 services/simulator/tests/test_manufacturer_demo_user_journey_docs.py delete mode 100644 services/simulator/tests/test_opc_ua_demo_namespace_docs.py delete mode 100644 services/simulator/tests/test_operations_workbench_ia_docs.py delete mode 100644 services/simulator/tests/test_pre_demo_checklist_docs.py delete mode 100644 services/simulator/tests/test_readme_demo_workflow_docs.py create mode 100644 services/simulator/tests/test_runtime_docs_transition.py diff --git a/FILE_INDEX.md b/FILE_INDEX.md index 9c03fa3..25e2fdc 100644 --- a/FILE_INDEX.md +++ b/FILE_INDEX.md @@ -20,7 +20,7 @@ https://github.com/Open-Factory-Initiative/Factory-Intelligence-Platform - `GOVERNANCE.md` — governance model - `ROADMAP.md` — phased project roadmap - `CHANGELOG.md` — changelog starter -- `Makefile` — placeholder command interface +- `Makefile` — local command interface - `.env.example` — environment variable template - `.editorconfig` — editor defaults - `.gitignore` — ignore rules @@ -79,9 +79,9 @@ https://github.com/Open-Factory-Initiative/Factory-Intelligence-Platform - `packages/factory-events/` — shared factory event schemas and contract tests - `packages/test-fixtures/` — valid, invalid, and drift scenario fixtures -- `services/simulator/` — deterministic simulator scenario generator +- `services/simulator/` — legacy local fixture code, not the default runtime path - `services/ingestion/` — event validation, dead-letter handling, and storage schema - `services/process-sentinel/` — drift rules, evidence, recommendations, and state store - `services/api/` — FastAPI API over MVP state -- `apps/web/` — placeholder for the future web workbench +- `apps/web/` — Next.js Operations Workbench - `infra/docker/` — local PostgreSQL Docker Compose config diff --git a/README.md b/README.md index 5be0090..8f480f2 100644 --- a/README.md +++ b/README.md @@ -2,360 +2,173 @@ **Open-source infrastructure for intelligent, connected, and AI-ready factories.** -Factory Intelligence Platform is the first major platform project from the **Open Factory Initiative**. It is designed to become a modular Factory Intelligence Layer that connects industrial data sources, normalizes factory events, detects quality/process drift, supports evidence-based investigations, and enables governed AI-assisted workflows across manufacturing operations. +Factory Intelligence Platform is an Open Factory Initiative project for a +modular Factory Intelligence Layer. The platform consumes factory observations, +normalizes them into FactoryEvents, detects operational and quality risk, shows +evidence-backed investigation context, and supports governed human-reviewed +recommendation workflows. -> Status: early-stage open-source project. The first vertical slice is **Process Sentinel**, a quality drift and deviation intelligence workflow. +> Status: early-stage open-source project. The current direction is a +> Docker Compose microservices runtime that consumes external Demo-Factory +> protocol data through read-only connectors. -## What This Platform Does +## Runtime Direction -The platform helps manufacturing teams answer questions such as: - -- What is happening in the factory right now? -- Which process signals changed before quality drift appeared? -- Which work orders, assets, lines, materials, and batches are affected? -- What evidence supports a recommended containment action? -- What should a quality engineer review before approving action? -- What did the plant learn from similar prior incidents? - -## MVP Vertical Slice - -The first end-to-end MVP should be intentionally narrow: +Demo-Factory is now the separate local source/simulation repository. This +repository owns the intelligence platform runtime: ```text -Synthetic Factory Simulator -→ Ingestion Worker -→ Factory Event Store / Unified Namespace -→ Process Sentinel Drift Detection -→ Evidence Timeline -→ Governed Recommendation Queue -→ Web UI Workbench -→ RCA / CAPA Draft Export -→ Factory Memory +Demo-Factory protocols +-> FIP read-only connector worker +-> FactoryEvent ingestion and storage +-> Process Sentinel worker +-> FastAPI API +-> Next.js Operations Workbench ``` +The in-repo generator and demo targets remain as legacy fixtures for tests and +backward-compatible local checks. They are not the default FIP runtime path. + ## Target Architecture ```mermaid flowchart LR - subgraph Sources["Factory Data Sources"] - Simulator["Synthetic Factory Simulator"] - MQTT["MQTT / OPC UA / CSV / API"] + subgraph Sources["External Factory Data Sources"] + DF["Demo-Factory protocol services"] + OPC["OPC-UA"] + MQTT["MQTT"] + BAC["BACnet"] MES["MES / QMS / CMMS / ERP"] end subgraph Platform["Factory Intelligence Platform"] - Ingestion["Ingestion Services"] - UNS["Unified Factory Event Model"] - Store["Operational + Time-Series Store"] + Connectors["Read-only connector workers"] + Events["Unified FactoryEvent model"] + Store["Postgres event store"] Sentinel["Process Sentinel"] - Evidence["Evidence Timeline Service"] - Governance["Governed Action Service"] - API["API Gateway"] - end - - subgraph Apps["User Applications"] + Evidence["Evidence timeline"] + Governance["Governed recommendations"] + API["FastAPI API"] UI["Operations Workbench"] - Reports["RCA / CAPA Drafts"] - Memory["Factory Memory"] end - Sources --> Ingestion - Ingestion --> UNS - UNS --> Store + Sources --> Connectors + Connectors --> Events + Events --> Store Store --> Sentinel Sentinel --> Evidence Evidence --> Governance Governance --> API API --> UI - API --> Reports - API --> Memory ``` -## Suggested Initial Stack - -This starter documentation assumes the initial implementation will use: - -- **Backend:** Python + FastAPI -- **Frontend:** TypeScript + React / Next.js -- **Database:** PostgreSQL -- **Time-series patterns:** TimescaleDB-compatible schema design -- **Eventing:** MQTT-first local dev path; Kafka/Redpanda-compatible later -- **Testing:** Pytest, Playwright, contract tests, integration tests, and end-to-end tests -- **Documentation:** Markdown, Mermaid diagrams, ADRs, and contributor guides -- **AI workflows:** Human-approved recommendations with evidence and audit logs - -These choices are intentionally open-source-friendly and practical for a Codex-assisted MVP. - -## Repository Structure - -Recommended structure: +## Current Repository Shape ```text -. -├── AGENTS.md -├── PLANS.md -├── CODE_REVIEW.md -├── README.md -├── CONTRIBUTING.md -├── SECURITY.md -├── SUPPORT.md -├── GOVERNANCE.md -├── ROADMAP.md -├── docs/ -│ ├── START_HERE_FOR_CODEX.md -│ ├── ARCHITECTURE.md -│ ├── PRODUCT_REQUIREMENTS.md -│ ├── MVP_SCOPE.md -│ ├── DOMAIN_MODEL.md -│ ├── DATA_CONTRACTS.md -│ ├── DEVELOPMENT.md -│ ├── TESTING.md -│ ├── DOCUMENTATION.md -│ ├── LEARNING_MODE.md -│ ├── GOVERNED_ACTIONS.md -│ ├── UNIFIED_NAMESPACE.md -│ ├── OBSERVABILITY.md -│ ├── SECURITY_MODEL.md -│ └── decisions/ -├── prompts/ -│ ├── README.md -│ └── *.md -├── apps/ -│ └── web/ -├── services/ -│ ├── api/ -│ ├── ingestion/ -│ ├── simulator/ -│ └── process-sentinel/ -├── packages/ -│ ├── factory-events/ -│ └── test-fixtures/ -└── infra/ - └── docker/ +apps/web Next.js Operations Workbench +services/api FastAPI endpoints over FIP state +services/ingestion Event validation and read-only protocol adapters +services/process-sentinel Drift rules, evidence, recommendations, state store +services/simulator Legacy local fixture code, not the default runtime +packages/factory-events Shared event and connection profile contracts +packages/test-fixtures Contract and connector fixture data +infra/docker Local Compose configuration +docs Architecture, safety, runtime, and contributor docs +prompts Codex implementation prompts ``` -## Current MVP Skeleton - -The first executable skeleton now focuses on the simulator-backed Process -Sentinel workflow: +## Safety Boundary -```text -services/simulator Deterministic normal/drift/excursion events -packages/factory-events Shared Pydantic event contracts -services/ingestion Event validation, dead-letter handling, local storage -services/process-sentinel Explainable drift rules, evidence, recommendations -services/api FastAPI endpoints over stored MVP state -apps/web Operations Workbench for the local manufacturer demo -infra/docker Local PostgreSQL configuration -``` +FIP is read-only by default for industrial protocols. It must not: -The default developer loop uses JSONL files under `.local/` so contributors can -run tests without a database. PostgreSQL is included for the durable storage path -and initialized with the MVP schema. +- write to PLCs, DCS, SCADA, OPC-UA nodes, MQTT topics, BACnet objects, or + equipment controllers; +- perform arbitrary tag writes; +- change machine parameters; +- release, quarantine, hold, or otherwise disposition product; +- write to QMS/MES records; +- claim production readiness, validated GxP use, electronic signatures, or + compliance validation from local development fixtures. -## Local Setup +See `docs/CONNECTOR_SAFETY_MODEL.md` for the current connector safety model. -Prerequisites: +## Local Runtime Commands -- Git -- Python 3.12+ -- Make -- Docker Desktop or another Docker Compose-compatible runtime - -Clone the repository and install the Python development environment: +The Compose runtime is being introduced through epic #232. Start Demo-Factory +separately when validating external local protocol sources: ```bash -git clone https://github.com/Open-Factory-Initiative/Factory-Intelligence-Platform.git -cd Factory-Intelligence-Platform -make setup +cd ../Demo-Factory +docker compose up -d --build ``` -`make setup` creates a repo-local `.venv` and installs development dependencies -from `requirements-dev.txt`. The first run needs network access to PyPI unless -the packages are already available in your local pip cache. - -If the repository is already cloned, run setup from the repository root: +Start the FIP Compose stack from this repository: ```bash -make setup +cd ../Factory-Intelligence-Platform +docker compose -f infra/docker/docker-compose.yml up --build +docker compose -f infra/docker/docker-compose.yml ps +docker compose -f infra/docker/docker-compose.yml logs -f +curl http://localhost:8000/health +docker compose -f infra/docker/docker-compose.yml down ``` -Optional: create a local environment file from the checked-in template: +Some Compose services are still landing in follow-up issues. Until then, +contributors can run the API and Workbench directly for focused development: ```bash -cp .env.example .env -``` - -The current backend skeleton does not require `.env` for the default JSONL path; -the template documents the local paths and future PostgreSQL configuration. - -Run the simulator-backed Process Sentinel flow: - -```bash -make simulate SCENARIO=gradual_drift -make ingest INPUT=.local/events/gradual_drift.jsonl -make sentinel-run +make setup make api -``` - -## Run the demo - -Use this path when you want to show the current simulator-backed Process -Sentinel manufacturer demo from a clean local state. - -The demo is intentionally local and simulator-backed: - -- It does not connect to a real plant, customer system, production batch - record, QMS, MES, SCADA, PLC, or equipment controller. -- Recommendations are advisory and human-reviewed. -- It does not perform autonomous control. -- It does not perform product disposition, QMS/MES writeback, or compliance validation. - -From the repository root, prepare deterministic demo state: - -```bash -make demo -``` - -`make demo` runs: - -```bash -make demo-reset -make demo-data -make demo-ingest -make demo-sentinel-run -make demo-api-smoke -``` - -Expected output includes: - -```text -accepted_events: 70 -dead_letter_count: 0 -sentinel complete: detections=1 evidence=2 recommendations=1 -demo api smoke passed -``` - -Expected demo IDs: - -```text -Detection: det_fill_weight_gradual_drift -Recommendation: rec_fill_weight_gradual_drift -``` - -After `make demo` completes, start the API in one terminal: - -```bash -make api EVENTS_STORE=.local/storage/fill_weight_drift_demo_events.jsonl SENTINEL_STATE_DIR=.local/storage/fill_weight_drift_demo_sentinel -``` - -Start the Workbench in another terminal: - -```bash cd apps/web npm run dev ``` -Open the Workbench: - -```text -http://127.0.0.1:3000 -``` - -The demo proves the first Process Sentinel workflow can run end to end with -local generated data: - -- A deterministic synthetic factory scenario is generated and ingested. -- Process Sentinel produces one clear fill-weight drift detection. -- The Workbench shows the detection, readable evidence timeline, and factory - context. -- A governed recommendation is available for human approve, reject, or defer - review. -- The recommendation decision is recorded in local demo state. -- An RCA/CAPA draft preview is generated for human review. - -Post-demo work is still separate from this local demo: - -- It does not prove production readiness. -- It does not prove validated GxP use. -- It does not include authentication/RBAC or enterprise audit controls. -- It does not include real plant integration, cloud deployment, or closed-loop industrial writeback. - -The demo commands write only generated files under `.local/`, which is ignored -by Git. See `docs/DEMO_RUNBOOK.md` for the technical demo flow, -`docs/demo/OPERATIONS_WORKBENCH_DEMO_RUNBOOK.md` for the Workbench browser -checklist, `docs/demo/MANUFACTURER_DEMO_RUNBOOK.md` for the manufacturer -pre-call checklist, talk track, demo boundaries, and post-demo feedback prompts, -`docs/demo/PRE_DEMO_CHECKLIST.md` for the final call-prep checklist, and -`docs/demo/TROUBLESHOOTING.md` for local demo failure recovery. - -Simulator scenarios can be selected with `SCENARIO=normal`, -`SCENARIO=gradual_drift`, or `SCENARIO=sudden_excursion`. Use `SEED`, `COUNT`, -`DURATION_MINUTES`, and `OUTPUT` to reproduce specific JSONL event streams; see -`services/simulator/README.md` for the full simulator workflow and ingestion -handoff. - -Then open: +Open: ```text http://127.0.0.1:8000/docs +http://127.0.0.1:3000 ``` -Use `make api-reload` instead of `make api` when you want Uvicorn to watch files -and restart automatically during local development. +## Documentation -To start the optional local PostgreSQL service: +- `docs/DEMO_RUNBOOK.md` - current Docker/Demo-Factory runtime direction and + transition notes. +- `docs/demo/OPERATIONS_WORKBENCH_DEMO_RUNBOOK.md` - Workbench runtime notes + for the operator console. +- `docs/demo/MANUFACTURER_DEMO_RUNBOOK.md` - manufacturer discussion framing. +- `docs/demo/TROUBLESHOOTING.md` - runtime troubleshooting checks. +- `docs/DEVELOPMENT.md` - local development workflow. +- `docs/ARCHITECTURE.md` - platform architecture and connector boundaries. +- `docs/CONNECTOR_SAFETY_MODEL.md` - read-only connector safety model. +- `docs/TESTING.md` - test strategy and commands. +- `docs/START_HERE_FOR_CODEX.md` - Codex workflow entry point. -```bash -make dev-db -``` +## Validation -The default MVP commands still use JSONL storage under `.local/`; PostgreSQL is -present so durable storage work can evolve without changing the repo structure. - -Run validation commands before opening a pull request: +Run these before opening a pull request: ```bash make lint make typecheck make test -make test-unit -make test-integration -make test-contract -make test-e2e +cd apps/web +npm test +npm run lint +npm run typecheck ``` -`make test-e2e` runs the local Operations Workbench Playwright smoke test for -the simulator-backed demo path. - ## Working With Codex -Start here: - -1. Read `docs/START_HERE_FOR_CODEX.md`. -2. Copy this documentation pack into the repository root. -3. Run Codex from the repository root. -4. Ask Codex to inspect the repo and produce a plan before creating code. -5. Run prompts in `prompts/` sequentially. -6. Require tests and docs for every meaningful change. - -A good first Codex prompt is: - -```text -Read AGENTS.md, PLANS.md, CODE_REVIEW.md, docs/START_HERE_FOR_CODEX.md, docs/ARCHITECTURE.md, docs/MVP_SCOPE.md, and docs/TESTING.md. - -Do not write code yet. Inspect the repository and propose an execution plan for creating the initial Factory Intelligence Platform MVP skeleton. Include repo structure, first services, test strategy, and documentation updates. Ask me only for blockers that cannot be resolved from the docs. -``` +Start with `docs/START_HERE_FOR_CODEX.md`. Ask Codex to inspect the current +repository and issue before editing, keep work scoped to one issue, add or +update tests, and update `docs/LEARNING_LOG.md` after meaningful changes. ## Contributing -New contributors should start with [CONTRIBUTING.md](./CONTRIBUTING.md). It -explains the project mission, local setup, issue workflow, branch naming, pull -request expectations, test commands, and how to find beginner-friendly work. - -The current open-source foundation status is summarized in -[docs/PROJECT_FOUNDATION.md](./docs/PROJECT_FOUNDATION.md), including the files -that satisfy the foundation epic acceptance criteria. +New contributors should start with [CONTRIBUTING.md](./CONTRIBUTING.md). The +project foundation status is summarized in +[docs/PROJECT_FOUNDATION.md](./docs/PROJECT_FOUNDATION.md). ## License diff --git a/apps/web/README.md b/apps/web/README.md index 0babf4d..e74c82f 100644 --- a/apps/web/README.md +++ b/apps/web/README.md @@ -1,13 +1,15 @@ # Operations Workbench -Next.js operator-console shell for the simulator-backed Process Sentinel demo. -The shell uses a Demo Factory-style steel, dark-nav, and teal-accent palette, -and the Workbench reads from the local FastAPI backend through a small typed -API client in `lib/api-client.ts`. +Next.js operator-console shell for the Factory Intelligence Platform. + +The Workbench is moving toward the Docker/Demo-Factory runtime direction: +external Demo-Factory protocol services feed FIP read-only connectors, which +normalize readings into FactoryEvents for Process Sentinel, the API, and the +Workbench. ## Local Startup -Install dependencies from this directory: +Install dependencies: ```bash npm install @@ -42,164 +44,59 @@ Override without code changes: NEXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8001 npm run dev ``` -The API allows the local Workbench origins needed for browser-side demo actions -by default. Override the comma-separated `FACTORY_API_CORS_ORIGINS` value when -running the API on different local web origins. - -The API client currently covers: - -- `GET /health` -- `GET /connection-profiles` -- `POST /connection-profiles` -- `PUT /connection-profiles/{profile_id}` -- `POST /connection-profiles/{profile_id}/test` -- `GET /sites` -- `GET /areas` -- `GET /equipment` -- `GET /batches` -- `GET /sentinel/detections` -- `GET /sentinel/detections/{detection_id}` -- `GET /sentinel/detections/{detection_id}/evidence` -- `GET /recommendations` -- `GET /recommendations/{recommendation_id}` -- `GET /recommendations/{recommendation_id}/decisions` -- `GET /recommendations/{recommendation_id}/audit` -- `POST /recommendations/{recommendation_id}/approve` -- `POST /recommendations/{recommendation_id}/reject` -- `POST /recommendations/{recommendation_id}/defer` -- `GET /reports/rca-capa-drafts/{detection_id}` - -The Overview page shows the configured API target, `/health` status, and -simulator-backed demo source so a presenter can confirm the browser is reading -from the expected local backend. - -When the backend is unavailable, the demo pages render a readable API connection -message instead of failing silently. The recovery panel shows the configured -target and prompts the presenter to run `make demo`, start the API with -`make api`, and restart the Workbench with `NEXT_PUBLIC_API_BASE_URL` if the API -is running on a non-default port. - -## Routes +## Runtime Direction -The shell uses a persistent sidebar with an embedded status strip instead of a -horizontal top bar. Existing Sentinel demo routes remain available, and the -sidebar includes Connections, Protocol Diagnostics, and Tag/Source Browser -pages. Those surfaces do not add production writeback controls. - -- `/` - Factory overview dashboard with site, line, asset, work order, product, - active detection count, pending recommendation count, and primary detection CTA -- `/connections` - OPC-UA, MQTT, and BACnet connections page with a title, - top-right add button, table, modal profile definition form, display-name link - to reopen the modal with prefilled values, reference-only credential fields, - disabled profile support, local validation feedback, current health state, - and test-connection diagnostics that do not start ingestion or industrial - writeback -- `/tag-source-browser` - Read-only tag/source browser with compact filters, - filtered summary counts, Source/Connection/Asset/FactoryEvent/Flat grouping - modes, duplicate source consolidation by default, and a source details drawer; - redacts credential details and links mapped rows to normalized FactoryEvent - context when an event is available -- `/detections` - Process Sentinel detection list with summary, severity, - confidence, status, time window, work order, related assets, and detail links -- `/detections/{detection_id}` - Read-only Process Sentinel detection detail - with a plain-English `Why this was flagged` explanation, API-backed evidence - timeline, evidence severity/context fields, recommendation review link, and - RCA/CAPA draft link -- `/recommendations` - Governed recommendation review panel with reviewer, - reason, approve/reject/defer controls, and decision feedback -- `/recommendations?detection_id={detection_id}` - Recommendation review scoped - to the selected Process Sentinel detection -- `/rca-capa-draft` - RCA/CAPA draft preview for the first available demo detection -- `/rca-capa-draft?detection_id={detection_id}` - RCA/CAPA draft preview - scoped to the selected Process Sentinel detection - -The shell labels all demo content as simulator-backed data and states that the -scenario is synthetic local data, not real plant data. The pages include loading -states, empty states, and simple user-readable API error states. Empty and -missing demo-data states show a short `Next step:` line so a presenter can -recover without treating local setup drift as a product failure. - -## Governance Decision Feedback - -After a reviewer approves, rejects, or defers a recommendation, the Workbench -shows the reviewer, decision, reason, timestamp, recommendation ID, and refreshed -recommendation status without requiring a page refresh. This feedback is a demo -audit trail from the decision POST response, not a validated production audit -record, electronic signature, or external QMS/MES writeback. - -The backend also exposes simulator-backed read endpoints for a recommendation's -local decision history and local audit events. The current page still uses the -POST response for immediate feedback, but future Workbench updates can reload -the persisted demo audit trail from the API without changing the governed -decision workflow. - -## RCA/CAPA Draft Preview - -The RCA/CAPA draft page loads draft investigation language from -`GET /reports/rca-capa-drafts/{detection_id}`. Detection and recommendation -pages link to it with the selected `detection_id`, and the page shows title, -problem statement, evidence summary, recommended containment, and CAPA -placeholder sections. The copy button copies a plain-text draft for demo review. - -The draft is marked as demo-generated decision support that requires human -review. It does not create a CAPA, claim validation status, or submit records to -QMS or MES. - -## Manual API Check - -In one terminal, prepare and run the backend demo API: +Start Demo-Factory separately from its sibling checkout: ```bash -make demo-reset -make demo-data -make demo-ingest -make demo-sentinel-run -make api +cd ../../Demo-Factory +docker compose up -d --build ``` -In another terminal, run: +Start FIP from this repository: ```bash -cd apps/web -npm run dev +cd ../Factory-Intelligence-Platform +docker compose -f infra/docker/docker-compose.yml up --build ``` -Then open `http://127.0.0.1:3000` and verify Overview, Detections, -Recommendations, and RCA/CAPA Draft render against the configured API base URL. -For the Workbench-focused browser checklist, see -`../../docs/demo/OPERATIONS_WORKBENCH_DEMO_RUNBOOK.md`. For the -manufacturer-facing 8-10 minute checklist, talk track, demo boundaries, and -feedback prompts, see `../../docs/demo/MANUFACTURER_DEMO_RUNBOOK.md`. -For local demo failure recovery, see -`../../docs/demo/TROUBLESHOOTING.md`. - -## Checks +For focused Workbench development before all Compose services land, run the API +directly from the repository root: ```bash -npm run lint -npm run typecheck -npm test -npm run build +make api ``` -## Demo Smoke Test +## Routes -The local Playwright smoke test starts the demo API and Workbench, prepares the -deterministic simulator-backed demo state, and walks the Operations Workbench -demo path in a browser: +- `/` - overview dashboard +- `/connections` - OPC-UA, MQTT, and BACnet profile management with redacted + credential references +- `/protocol-diagnostics` - read-only connection health and mapping diagnostics +- `/tag-source-browser` - read-only source inventory and FactoryEvent context +- `/detections` - Process Sentinel detection list +- `/detections/{detection_id}` - detection detail with evidence timeline +- `/recommendations` - governed recommendation review +- `/rca-capa-draft` - draft investigation preview -```bash -npx playwright install chromium -npm run test:e2e -``` +## Safety Boundary + +The Workbench must not expose industrial writeback, arbitrary tag writes, +product disposition, QMS/MES writeback, or production CAPA creation. Local +Docker and Demo-Factory validation is not a production-readiness, validated-use, +electronic-signature, or compliance claim. + +## Related Docs + +- `../../docs/demo/OPERATIONS_WORKBENCH_DEMO_RUNBOOK.md` +- `../../docs/demo/MANUFACTURER_DEMO_RUNBOOK.md` +- `../../docs/demo/TROUBLESHOOTING.md` -From the repository root, the same smoke test is available as: +## Checks ```bash -make test-e2e +npm run lint +npm run typecheck +npm test +npm run build ``` - -The smoke test covers overview, detections, detection detail, evidence timeline, -governed recommendation review, approve/reject/defer decision feedback, and -RCA/CAPA draft preview. It is currently a reliable local test; GitHub Actions -integration is tracked in follow-up issue #165. diff --git a/apps/web/tests/app-shell.test.mjs b/apps/web/tests/app-shell.test.mjs index 5c687e4..574696b 100644 --- a/apps/web/tests/app-shell.test.mjs +++ b/apps/web/tests/app-shell.test.mjs @@ -505,10 +505,10 @@ test("operations workbench docs link to the demo runbook", async () => { assert.match(rootReadme, /docs\/demo\/OPERATIONS_WORKBENCH_DEMO_RUNBOOK\.md/); assert.match(appReadme, /OPERATIONS_WORKBENCH_DEMO_RUNBOOK\.md/); assert.match(manufacturerRunbook, /OPERATIONS_WORKBENCH_DEMO_RUNBOOK\.md/); - assert.match(operationsRunbook, /simulator-backed demo data/); - assert.match(operationsRunbook, /BATCH-DEMO-1007/); + assert.match(operationsRunbook, /external Demo-Factory protocol data/); + assert.match(operationsRunbook, /read-only connector worker/); assert.match(operationsRunbook, /governed recommendation/); - assert.match(operationsRunbook, /not production data/); + assert.match(operationsRunbook, /Do not claim production readiness/); }); test("demo polish keeps pages readable without raw JSON panels", () => { diff --git a/docs/API_DESIGN.md b/docs/API_DESIGN.md index d6ab70d..e4ef519 100644 --- a/docs/API_DESIGN.md +++ b/docs/API_DESIGN.md @@ -56,8 +56,8 @@ GET /investigations GET /investigations/{investigation_id} ``` -The current domain endpoints are read-only and return simulator/demo context for -the first Process Sentinel MVP. Investigation detail responses include linked +The current domain endpoints are read-only and return local demo context for the +first Process Sentinel MVP. Investigation detail responses include linked deviation, alert, quality result, and process signal records. ### Process Sentinel diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index db9db7d..c3328d2 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -9,7 +9,7 @@ The platform should be modular enough for contributors to use individual compone ## Architectural Goals 1. **Open-source and inspectable** -2. **Simulator-first development** +2. **External-source local development** 3. **Interoperable with industrial systems** 4. **Evidence-backed recommendations** 5. **Human-governed actions** @@ -24,13 +24,13 @@ The platform should be modular enough for contributors to use individual compone ```mermaid flowchart TB subgraph DevSources["Development Sources"] - S1["Synthetic Factory Simulator"] - S2["Seeded Scenarios"] + S1["Demo-Factory protocol services"] + S2["Configured OPC-UA, MQTT, and BACnet fixtures"] end subgraph Ingestion["Ingestion Layer"] - I1["Simulator Adapter"] - I2["MQTT Adapter Stub"] + I1["Read-only Connector Worker"] + I2["Protocol Adapters"] I3["CSV/API Adapter Stub"] end @@ -174,10 +174,10 @@ recommendations, RCA/CAPA drafts, or Workbench views consume it. The operator console may show connection health, mapping state, source inventory, and redacted diagnostics, but those screens remain read-only. -Local Demo-Factory endpoints are development and test fixtures only. They help -contributors compare protocol diagnostics against local simulator-backed -services, but they are not production dependencies and do not prove production -readiness. See `docs/CONNECTOR_SAFETY_MODEL.md` for the protocol profile +Local Demo-Factory endpoints are development and test fixtures only. They are +external local protocol sources for connector diagnostics; they are not +production dependencies and do not prove production readiness. See +`docs/CONNECTOR_SAFETY_MODEL.md` for the protocol profile assumptions, local fixture endpoints, and forbidden writeback classes. ## Future AI Architecture @@ -210,7 +210,7 @@ local-first, model-agnostic, and governed: The source layer represents systems that produce data: -- Synthetic simulator +- External Demo-Factory protocol services - MQTT brokers - OPC UA servers - MES APIs @@ -219,7 +219,8 @@ The source layer represents systems that produce data: - CSV uploads - Manual notes -The MVP should start with the simulator and adapter stubs. +The current runtime direction starts with external Demo-Factory fixtures and +read-only protocol adapters. ### 2. Ingestion Layer @@ -374,17 +375,8 @@ sequenceDiagram ### `services/simulator` -Generates synthetic factory data. - -Should support: - -- Assets -- Lines -- Work orders -- Process measurements -- Quality measurements -- Drift scenarios -- Seeded deterministic test scenarios +Legacy local fixture code retained for existing tests and compatibility checks. +It is not the default FIP runtime source. ### `services/ingestion` diff --git a/docs/CONNECTOR_SAFETY_MODEL.md b/docs/CONNECTOR_SAFETY_MODEL.md index 5a69549..4818402 100644 --- a/docs/CONNECTOR_SAFETY_MODEL.md +++ b/docs/CONNECTOR_SAFETY_MODEL.md @@ -83,11 +83,11 @@ Use these fixture endpoints only in local development, docs, tests, or demos: | Fixture | Endpoint or command | Purpose | | --- | --- | --- | -| FIP API | `http://127.0.0.1:8000` | Local FastAPI API for the simulator-backed platform state. | +| FIP API | `http://127.0.0.1:8000` | Local FastAPI API for platform state. | | FIP Workbench | `http://127.0.0.1:3000` | Local Next.js operator console. | | Playwright API | `http://127.0.0.1:8001` | Isolated API port used by browser tests. | | Playwright Workbench | `http://127.0.0.1:3001` | Isolated web port used by browser tests. | -| Demo OPC-UA server | `opc.tcp://localhost:4840/ofi/demo` | Local simulator-backed OPC-UA namespace for demo reads. | +| Demo-Factory OPC-UA server | `opc.tcp://localhost:4840/ofi/demo` | External local OPC-UA fixture for read-only connector validation. | | Local MQTT broker placeholder | `mqtt://localhost:1883` | Development broker URL documented in `.env.example`; use only with approved local fixtures. | | BACnet fixture profile | `192.0.2.20:47808` | Documentation/test profile address from the reserved TEST-NET range, not a real device. | | Demo-Factory checkout | `~/Documents/Open Factory Initiative/Demo-Factory` | Optional sibling repo used to compare local protocol service behavior. | @@ -107,7 +107,8 @@ pages read FIP API data. This means FIP does not inspect Demo-Factory runtime st ## Data Flow -Readings from protocol adapters must follow the same path as simulator events: +Readings from protocol adapters must follow the same path as other accepted +source events: ```text Configured protocol source diff --git a/docs/DEMO_RUNBOOK.md b/docs/DEMO_RUNBOOK.md index 613a97f..5d5f7eb 100644 --- a/docs/DEMO_RUNBOOK.md +++ b/docs/DEMO_RUNBOOK.md @@ -1,385 +1,88 @@ -# Demo Runbook +# Docker And Demo-Factory Runtime Runbook -## Demo Data Pack +## Purpose -The manufacturer demo uses the deterministic `fill_weight_drift_demo` simulator -scenario. The data is simulator-backed and safe for local development; it does -not represent a real plant, customer, or production system. +This runbook replaces the old in-repo generated-data demo path as the current +runtime direction for the Factory Intelligence Platform. -### One-Command Demo Setup +Demo-Factory is now the separate local source/simulation repository. FIP should +consume external Demo-Factory protocol data through read-only connectors, then +normalize readings into FactoryEvents for Process Sentinel, the API, and the +Operations Workbench. -Use the one-command demo runner from the repository root when preparing for a -manufacturer call: - -```bash -make demo -``` - -`make demo` runs the repeatable local demo preparation sequence: - -1. `make demo-reset` -2. `make demo-data` -3. `make demo-ingest` -4. `make demo-sentinel-run` -5. `make demo-api-smoke` - -It then prints the expected detection ID, recommendation ID, API startup -command, Workbench startup command, and expected URLs for the demo path. - -Expected IDs: - -```text -det_fill_weight_gradual_drift -rec_fill_weight_gradual_drift -``` - -After `make demo` completes, start the API in one terminal: - -```bash -make api \ - EVENTS_STORE=.local/storage/fill_weight_drift_demo_events.jsonl \ - SENTINEL_STATE_DIR=.local/storage/fill_weight_drift_demo_sentinel -``` - -Start the Workbench in another terminal: - -```bash -cd apps/web -npm run dev -``` - -Expected API URLs: - -```text -http://127.0.0.1:8000/docs -http://127.0.0.1:8000/sentinel/detections -http://127.0.0.1:8000/sentinel/detections/det_fill_weight_gradual_drift -http://127.0.0.1:8000/sentinel/detections/det_fill_weight_gradual_drift/evidence -http://127.0.0.1:8000/recommendations -http://127.0.0.1:8000/reports/rca-capa-drafts/det_fill_weight_gradual_drift -``` - -Expected Workbench URLs: - -```text -http://127.0.0.1:3000 -http://127.0.0.1:3000/detections/det_fill_weight_gradual_drift -http://127.0.0.1:3000/recommendations?detection_id=det_fill_weight_gradual_drift -http://127.0.0.1:3000/rca-capa-draft?detection_id=det_fill_weight_gradual_drift -``` - -`make demo-reset`, which runs as the first step, clears only the generated local -demo files: - -- `.local/events/fill_weight_drift_demo.jsonl` -- `.local/storage/fill_weight_drift_demo_events.jsonl` -- `.local/storage/fill_weight_drift_demo_dead_letter.jsonl` -- `.local/storage/fill_weight_drift_demo_sentinel/` - -Those paths live under `.local/`, which is ignored by Git. The reset target does -not drop a production database, remove source files, or clean real plant data. - -The individual demo targets are still useful for troubleshooting. They print the -next command to run after each step. After `make demo-sentinel-run`, run the -backend smoke test: - -```bash -make demo-api-smoke -``` - -The smoke test verifies that the demo event store exists, then exercises the API -app against a temporary copy of the demo state. It checks the expected detection, -evidence, recommendation, decision, and RCA/CAPA draft endpoints without -requiring the browser UI or mutating the prepared demo files. - -To inspect the API manually after the smoke test passes, start the API against -the demo state with: - -```bash -make api \ - EVENTS_STORE=.local/storage/fill_weight_drift_demo_events.jsonl \ - SENTINEL_STATE_DIR=.local/storage/fill_weight_drift_demo_sentinel -``` - -For failure-mode recovery, see `docs/demo/TROUBLESHOOTING.md`. It covers no -detections, API not running, frontend API connection issues, stale local state, -missing dependencies, empty RCA/CAPA drafts, and recommendation decision -feedback. - -### Seeded Scenario - -```bash -make demo-data -``` - -Expected simulator output: - -```text -wrote 70 events to .local/events/fill_weight_drift_demo.jsonl (scenario=fill_weight_drift_demo, seed=120, count=30) -``` - -### Demo Names And IDs - -| Concept | Demo value | -| --- | --- | -| Site | `greenville_demo_site` / Greenville Demo Site | -| Area | `packaging_area` / Packaging Area | -| Line | `line_2` / Line 2 | -| Product | `ofi_demo_beverage` / OFI Demo Beverage | -| Work order | `WO-DEMO-1007` | -| Batch | `BATCH-DEMO-1007` | -| Affected asset | `filler_f_201` / Filler F-201 | -| Quality check asset | `checkweigher_cw_201` / Checkweigher CW-201 | -| Process tags | `fill_weight`, `filler_nozzle_pressure` | -| Expected detection | `det_fill_weight_gradual_drift` | -| Expected recommendation | `rec_fill_weight_gradual_drift` | - -### Expected Event Shape - -The generated data includes process measurement events and quality measurement -events. Each event includes source metadata, simulator trace metadata, site, -area, line, work order, batch, asset context, and readable process or quality -payload fields for UI cards. - -### Local Demo Path - -```bash -make demo-ingest -make demo-sentinel-run -``` - -### Demo Ingestion Verification - -Run ingestion after `make demo-data`: - -```bash -make demo-ingest -``` - -Expected ingestion summary: - -```text -ingestion summary -input_file: .local/events/fill_weight_drift_demo.jsonl -accepted_events: 70 -rejected_events: 0 -dead_letter_count: 0 -accepted_output: .local/storage/fill_weight_drift_demo_events.jsonl -dead_letter_output: .local/storage/fill_weight_drift_demo_dead_letter.jsonl -``` - -Verify the local files: - -```bash -test -f .local/storage/fill_weight_drift_demo_events.jsonl -test -f .local/storage/fill_weight_drift_demo_dead_letter.jsonl -wc -l .local/storage/fill_weight_drift_demo_events.jsonl -wc -l .local/storage/fill_weight_drift_demo_dead_letter.jsonl -``` - -The accepted event store should contain 70 rows. The dead-letter file should -exist and contain 0 rows for the deterministic demo data. If an invalid row is -added during manual testing, ingestion should keep processing valid rows and -write the invalid row to the dead-letter file. - -Expected Process Sentinel output includes: +Target flow: ```text -sentinel complete: detections=1 evidence=2 recommendations=1 +Demo-Factory protocols +-> FIP read-only connector worker +-> FactoryEvent ingestion and storage +-> Process Sentinel worker +-> FastAPI API +-> Next.js Workbench ``` -### Expected Demo Detection +## Start Demo-Factory Separately -The deterministic demo run should produce one Process Sentinel detection: - -| Field | Expected value | -| --- | --- | -| Detection ID | `det_fill_weight_gradual_drift` | -| API list endpoint | `/sentinel/detections` | -| API detail endpoint | `/sentinel/detections/det_fill_weight_gradual_drift` | -| Summary | Advisory: fill weight is trending upward, which may move the affected work order toward the upper quality limit. | -| Severity | `medium` | -| Confidence | Greater than `0.7` | -| Related work order | `WO-DEMO-1007` | -| Related asset IDs | `filler_f_201` | - -After `make demo-sentinel-run`, start the API with the demo state and query the -detection: +From a sibling checkout: ```bash -make api \ - EVENTS_STORE=.local/storage/fill_weight_drift_demo_events.jsonl \ - SENTINEL_STATE_DIR=.local/storage/fill_weight_drift_demo_sentinel -``` - -Then open or request: - -```text -http://127.0.0.1:8000/sentinel/detections -http://127.0.0.1:8000/sentinel/detections/det_fill_weight_gradual_drift -``` - -### Expected Demo Evidence Timeline - -The demo detection evidence endpoint should return a chronological timeline: - -```text -http://127.0.0.1:8000/sentinel/detections/det_fill_weight_gradual_drift/evidence +cd ../Demo-Factory +docker compose up -d --build ``` -Expected evidence items: - -| Order | Evidence type | Title | What it shows | -| --- | --- | --- | --- | -| 1 | `process_signal` | Recent fill weight average is higher than baseline | Baseline and recent fill-weight averages, source process event IDs, and a score. | -| 2 | `quality_result` | Recent quality checks are near the upper spec limit | Recent quality checks moving in the same direction as the process signal, source quality event IDs, and a score. | - -Each evidence item includes: - -- `evidence_id` -- `detection_id` -- `evidence_type` -- `timestamp` -- `title` -- `description` -- `source_event_ids` -- `score` +Demo-Factory owns local protocol source behavior for OPC-UA, MQTT, and BACnet +validation. Do not copy Demo-Factory services into FIP as the default runtime. -The UI/API contract, empty state, and error state are documented in -`docs/EVIDENCE_TIMELINE_API_CONTRACT.md`. +## Start FIP -### Expected Demo Recommendation Review - -After `make demo-sentinel-run`, the demo should produce one recommendation: - -| Item | Expected value | -| --- | --- | -| Recommendation ID | `rec_fill_weight_gradual_drift` | -| Detection ID | `det_fill_weight_gradual_drift` | -| Status | `needs_review` | -| Risk level | `medium` | -| Requires approval | `true` | - -Query the recommendation queue and detail: - -```text -http://127.0.0.1:8000/recommendations -http://127.0.0.1:8000/recommendations/rec_fill_weight_gradual_drift -``` - -The recommendation includes `recommended_action`, `rationale`, `risk_level`, -`requires_approval`, `evidence_ids`, and `created_at`. - -Review decisions are explicit human actions: +From this repository: ```bash -curl -s -X POST \ - http://127.0.0.1:8000/recommendations/rec_fill_weight_gradual_drift/approve \ - -H 'content-type: application/json' \ - --data '{"reviewer":"quality_engineer","reason":"Evidence supports containment."}' -``` - -Use the same request shape with `/reject` or `/defer` to exercise those demo -paths in isolated local state. Each decision response includes -`recommendation_id`, `reviewer`, `decision`, `reason`, `created_at`, and -`timestamp` for UI confirmation. This records local demo state only; it does not -perform external writeback. - -### Expected Demo RCA/CAPA Draft - -The RCA/CAPA draft endpoint previews a quality-facing draft from the selected -detection, evidence, and recommendation: - -```text -http://127.0.0.1:8000/reports/rca-capa-drafts/det_fill_weight_gradual_drift +cd ../Factory-Intelligence-Platform +docker compose -f infra/docker/docker-compose.yml up --build +docker compose -f infra/docker/docker-compose.yml ps +docker compose -f infra/docker/docker-compose.yml logs -f +curl http://localhost:8000/health +docker compose -f infra/docker/docker-compose.yml down ``` -Expected draft fields: +The Compose runtime is being built across the Dockerized runtime epic. Some +services may be placeholders until the child issues for API/web images, +connector worker, Postgres-backed runtime storage, Sentinel worker, and Compose +smoke tests land. -| Field | Expected demo behavior | -| --- | --- | -| `title` | Starts with `RCA/CAPA draft for` and uses the detection summary. | -| `problem_statement` | Matches the advisory demo detection summary. | -| `evidence_summary` | Lists the evidence item descriptions from the evidence timeline. | -| `recommended_containment` | Matches the governed recommendation action. | -| `capa_placeholder` | Prompts for root cause, corrective action, preventive action, owner, and due date after human investigation. | -| `human_review_required` | `true` | +## Focused Local Development -Example response: - -```json -{ - "detection_id": "det_fill_weight_gradual_drift", - "title": "RCA/CAPA draft for Advisory: fill weight is trending upward, which may move the affected work order toward the upper quality limit.", - "problem_statement": "Advisory: fill weight is trending upward, which may move the affected work order toward the upper quality limit.", - "evidence_summary": [ - "Baseline average was 500.07 g; recent average was 506.37 g, a 6.30 g increase.", - "Recent final fill weight checks show the same upward direction as the process signal." - ], - "recommended_containment": "Inspect filler calibration and increase quality checks for the affected demo work order.", - "capa_placeholder": "Document root cause, corrective action, preventive action, owner, and due date after human investigation.", - "human_review_required": true -} -``` - -This is draft decision support only. It must be reviewed by a human before use -and does not create a CAPA, close a deviation, or write to QMS/MES systems. - -### Troubleshooting Demo Ingestion - -If no detections appear during demo prep, first confirm that the demo is using -the deterministic data path end to end: - -| Item | Expected value | -| --- | --- | -| Demo event file | `.local/events/fill_weight_drift_demo.jsonl` | -| Demo accepted event store | `.local/storage/fill_weight_drift_demo_events.jsonl` | -| Demo dead-letter file | `.local/storage/fill_weight_drift_demo_dead_letter.jsonl` | -| Demo Sentinel state | `.local/storage/fill_weight_drift_demo_sentinel/` | -| Generated event count | `70` | -| Accepted event count | `70` | -| Rejected event count | `0` | -| Dead-letter row count | `0` | -| Expected Sentinel result | `detections=1 evidence=2 recommendations=1` | - -Use a clean reset and retry when local state is stale, manually edited, or -pointing at an older scenario: +For focused API or Workbench development before the full Compose stack is +available: ```bash -make demo-reset -make demo-data -make demo-ingest -make demo-sentinel-run +make setup +make api +cd apps/web +npm run dev ``` -Then verify the generated files: +Use this direct process only as a local development loop. It is not the target +microservices runtime. -```bash -wc -l .local/events/fill_weight_drift_demo.jsonl -wc -l .local/storage/fill_weight_drift_demo_events.jsonl -wc -l .local/storage/fill_weight_drift_demo_dead_letter.jsonl -``` +## Safety Boundary -Common ingestion failure cases: +FIP connector work is read-only by default. The runtime must not perform +industrial writeback, arbitrary tag writes, product disposition, or QMS/MES +writeback. Local Docker and Demo-Factory validation does not prove production +readiness, validated GxP use, electronic signatures, or compliance status. -- The input file is missing because `make demo-data` was not run first. -- `accepted_events` is `0` or lower than `70` because the command used the - wrong input file, scenario, seed, or count. -- `rejected_events` or `dead_letter_count` is non-zero because at least one - JSONL row is malformed or does not match the shared factory event contracts. -- Process Sentinel reads the wrong event store because `EVENTS_STORE` points to - `.local/storage/events.jsonl` instead of the demo accepted event store. -- Old Sentinel state is being inspected after reruns instead of - `.local/storage/fill_weight_drift_demo_sentinel/` from the latest run. -- The local files were hand-edited. Reset and regenerate instead of repairing - generated `.local/` files by hand. +See `docs/CONNECTOR_SAFETY_MODEL.md` for the connector safety model. -When rejected events appear, inspect the summary and the dead-letter file: - -```bash -head -n 3 .local/storage/fill_weight_drift_demo_dead_letter.jsonl -``` +## Related Work -The demo reset only clears generated local demo files under `.local/`. It does -not remove source files, clean production databases, or interact with real plant -systems. +- #232 - Dockerized Factory Intelligence Platform microservices runtime. +- #234 - Add detailed Docker Compose runtime documentation. +- #235 - Add Postgres-backed runtime storage. +- #236 - Add Docker images for API, web, connector worker, and Sentinel worker. +- #237 - Add Demo-Factory protocol connection fixtures. +- #238 - Add connector worker runtime. +- #239 - Add scheduled Process Sentinel worker. +- #240 - Update API and Workbench runtime wording. +- #241 - Add Compose smoke test. diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 3bf9c1c..49be745 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -50,24 +50,43 @@ git checkout -b docs/codex-starter-kit For implementation: ```bash -git checkout -b feat/simulator-vertical-slice +git checkout -b feat/read-only-runtime-slice ``` ## Local Development Target -The current MVP skeleton supports a backend-first local loop: +The current direction is a Docker Compose runtime that consumes external +Demo-Factory protocol data through read-only connectors. + +Start Demo-Factory separately from a sibling checkout: + +```bash +cd ../Demo-Factory +docker compose up -d --build +``` + +Start FIP from this repository: + +```bash +cd ../Factory-Intelligence-Platform +docker compose -f infra/docker/docker-compose.yml up --build +docker compose -f infra/docker/docker-compose.yml ps +docker compose -f infra/docker/docker-compose.yml logs -f +curl http://localhost:8000/health +docker compose -f infra/docker/docker-compose.yml down +``` + +Some Compose services are still landing through the Dockerized runtime epic. For +focused API or Workbench development before the full composed stack is +available, run: ```bash make setup -make simulate SCENARIO=gradual_drift -make ingest INPUT=.local/events/gradual_drift.jsonl -make sentinel-run make api +cd apps/web +npm run dev ``` -This generates simulator events, validates and stores them in `.local/`, runs -Process Sentinel, and starts the FastAPI API. - Use `make api-reload` when you want the FastAPI process to restart after file changes. The default `make api` command avoids file watching so it is more reliable in constrained agent and CI environments. @@ -93,7 +112,7 @@ Open `http://127.0.0.1:3000` for the Workbench and Current skeleton components: - `packages/factory-events` — shared event contracts -- `services/simulator` — deterministic scenario generator +- `services/simulator` — legacy local fixture code, not the default runtime path - `services/ingestion` — validation and dead-letter handling - `services/process-sentinel` — drift rules, evidence, recommendations - `services/api` — FastAPI endpoints for the MVP state @@ -130,7 +149,6 @@ DATABASE_URL=postgresql://postgres:postgres@localhost:5432/factory_intelligence MQTT_BROKER_URL=mqtt://localhost:1883 APP_ENV=development LOG_LEVEL=info -SIMULATOR_SEED=42 FACTORY_EVENTS_STORE=.local/storage/events.jsonl SENTINEL_STATE_DIR=.local/storage/sentinel ``` diff --git a/docs/DOMAIN_MODEL.md b/docs/DOMAIN_MODEL.md index 8185ffe..fdf0fb2 100644 --- a/docs/DOMAIN_MODEL.md +++ b/docs/DOMAIN_MODEL.md @@ -185,8 +185,8 @@ The current demo context uses: - `alert_fill_weight_trend_WO_DEMO_1007` - `inv_fill_weight_drift_WO_DEMO_1007` -This data intentionally mirrors the simulator-backed Process Sentinel scenario: -fill weight trends upward, the related quality result fails the upper +This data intentionally mirrors the Process Sentinel demo scenario: fill weight +trends upward, the related quality result fails the upper specification, a deviation is opened, and the investigation links the quality outcome back to the process signals. diff --git a/docs/EVIDENCE_TIMELINE_API_CONTRACT.md b/docs/EVIDENCE_TIMELINE_API_CONTRACT.md index 819f5fd..df00c3f 100644 --- a/docs/EVIDENCE_TIMELINE_API_CONTRACT.md +++ b/docs/EVIDENCE_TIMELINE_API_CONTRACT.md @@ -3,9 +3,9 @@ ## Purpose This contract defines the evidence timeline shape the Operations Workbench can -expect for the simulator-backed Process Sentinel demo. It covers the MVP API -response only; it does not define charting, generated clients, advanced -analytics, or Factory Memory retrieval. +expect from the current Process Sentinel API. It covers the MVP API response +only; it does not define charting, generated clients, advanced analytics, or +Factory Memory retrieval. ## Endpoint diff --git a/docs/GOVERNED_ACTIONS.md b/docs/GOVERNED_ACTIONS.md index 063622f..31acb45 100644 --- a/docs/GOVERNED_ACTIONS.md +++ b/docs/GOVERNED_ACTIONS.md @@ -69,7 +69,7 @@ Every recommendation must include: ## MVP Review API -The simulator-backed demo exposes a human review workflow through the API: +The local MVP API exposes a human review workflow for governed recommendations: ```text GET /recommendations @@ -127,9 +127,9 @@ Audit events can be read from: GET /recommendations/{recommendation_id}/audit ``` -These endpoints expose the local simulator-backed decision and audit records for -demo review. They are not validated production audit records, electronic -signatures, or compliance exports. +These endpoints expose local decision and audit records for demo review. They +are not validated production audit records, electronic signatures, or +compliance exports. ## Approval States diff --git a/docs/LEARNING_LOG.md b/docs/LEARNING_LOG.md index c0c8ded..3f40612 100644 --- a/docs/LEARNING_LOG.md +++ b/docs/LEARNING_LOG.md @@ -22,6 +22,71 @@ This file should be updated by Codex after each meaningful change. ### What to learn next ``` +## 2026-05-26 - Runtime documentation direction cleanup + +### What changed + +Rewrote the current runtime docs and prompts so FIP no longer presents the +in-repo simulator as the default development or demo path. The current direction +is Docker Compose plus external Demo-Factory protocol data consumed through +read-only connectors. + +### Why it matters + +Demo-Factory now owns local source simulation. FIP docs should teach +contributors to treat protocol data as an external source, normalize readings +into FactoryEvents, and keep industrial writeback out of the MVP runtime. + +### How it works + +The docs now describe this flow: + +```text +Demo-Factory protocols +-> FIP read-only connector worker +-> FactoryEvent ingestion and storage +-> Process Sentinel worker +-> FastAPI API +-> Next.js Workbench +``` + +Historical entries were preserved so earlier simulator-backed learning context +remains available, but new contributor-facing docs point to the Docker and +external-source runtime direction. + +### How to run it + +```bash +cd ../Demo-Factory +docker compose up -d --build + +cd ../Factory-Intelligence-Platform +docker compose -f infra/docker/docker-compose.yml up --build +``` + +### How to test it + +```bash +.venv/bin/python -m pytest services/simulator/tests/test_runtime_docs_transition.py +make test +cd apps/web && npm test +make docs +``` + +### Key files + +- `README.md` +- `docs/DEMO_RUNBOOK.md` +- `docs/demo/` +- `prompts/03-synthetic-factory-simulator.md` +- `services/simulator/tests/test_runtime_docs_transition.py` + +### What to learn next + +Use the rewritten runtime docs as the baseline while implementing the remaining +Dockerized runtime issues for Postgres storage, connector workers, Sentinel +scheduling, and Compose smoke tests. + ## 2026-05-26 - Connector management epic readiness ### What changed diff --git a/docs/MVP_SCOPE.md b/docs/MVP_SCOPE.md index 85495f2..98f587c 100644 --- a/docs/MVP_SCOPE.md +++ b/docs/MVP_SCOPE.md @@ -6,40 +6,37 @@ Process Sentinel Vertical Slice ## Goal -Create a local, simulator-backed MVP that proves the platform can detect quality/process drift, explain it with evidence, and route a governed recommendation to a human. +Create a local Factory Intelligence Platform slice that consumes external +Demo-Factory protocol data through read-only connectors, normalizes observations +into FactoryEvents, detects quality/process drift, explains it with evidence, +and routes a governed recommendation to a human. ## MVP Flow ```text -1. Start local stack. -2. Simulator emits factory events. -3. Ingestion service validates and stores events. -4. Process Sentinel evaluates recent process windows. -5. Drift detection creates a detection. -6. Evidence service builds a timeline. -7. Recommendation service creates a proposed action. -8. User reviews recommendation in web UI. -9. User approves, rejects, or defers. -10. System writes audit event. -11. System creates RCA/CAPA draft. +1. Start Demo-Factory separately. +2. Start the FIP Docker Compose stack. +3. Read-only connector worker consumes configured protocol sources. +4. Ingestion service validates and stores FactoryEvents. +5. Process Sentinel evaluates recent process windows. +6. Drift detection creates a detection. +7. Evidence service builds a timeline. +8. Recommendation service creates a proposed action. +9. User reviews recommendation in web UI. +10. User approves, rejects, or defers. +11. System writes audit event. +12. System creates RCA/CAPA draft. ``` ## In Scope -### Simulator - -- One site -- One production area -- One line -- Three to five assets -- One product -- Work orders -- Process measurements -- Quality measurements -- At least three scenarios: - - normal operation - - gradual quality drift - - sudden process excursion +### External Source Fixtures + +- Demo-Factory runs outside this repository. +- OPC-UA, MQTT, and BACnet fixture endpoints are configured through read-only + connection profiles. +- Source readings are normalized into FactoryEvents before Process Sentinel + consumes them. ### Data Model @@ -96,8 +93,8 @@ Create a local, simulator-backed MVP that proves the platform can detect quality ## Out Of Scope -- Real OPC UA integration -- Real MQTT production broker +- Industrial write connectors +- Real MQTT command publishing - Real QMS writeback - User authentication - Multi-tenant support @@ -114,7 +111,7 @@ Create a local, simulator-backed MVP that proves the platform can detect quality AI/model work remains post-MVP expansion. The MVP may use deterministic rules and human-reviewed recommendation language, but it should not depend on an LLM, fine-tuned model, RAG index, or validation package to complete the first -simulator-backed Process Sentinel workflow. +read-only connector and Docker Compose runtime. ## MVP Milestones @@ -131,11 +128,11 @@ simulator-backed Process Sentinel workflow. - Sample events - Contract tests -### Milestone 3: Simulator +### Milestone 3: External Source Fixtures -- Deterministic scenarios -- Event emission -- Test fixtures +- Demo-Factory fixture assumptions +- OPC-UA/MQTT/BACnet connection profiles +- Redacted connection diagnostics ### Milestone 4: Ingestion + Storage diff --git a/docs/SECURITY_MODEL.md b/docs/SECURITY_MODEL.md index 8cca396..675aaaa 100644 --- a/docs/SECURITY_MODEL.md +++ b/docs/SECURITY_MODEL.md @@ -2,7 +2,9 @@ ## MVP Security Posture -The MVP is local-development-first and simulator-first. It should not be connected to real industrial control systems. +The MVP is local-development-first and external-source-first through +Demo-Factory fixtures and read-only connectors. It should not be connected to +real industrial control systems by default. ## Security Principles @@ -11,7 +13,7 @@ The MVP is local-development-first and simulator-first. It should not be connect - Use least privilege - Separate recommendations from actions - Audit human decisions -- Make demo/simulator behavior explicit +- Make local fixture behavior explicit - Do not build unsafe industrial writeback paths ## Secrets diff --git a/docs/START_HERE_FOR_CODEX.md b/docs/START_HERE_FOR_CODEX.md index 12de4ea..a0edccc 100644 --- a/docs/START_HERE_FOR_CODEX.md +++ b/docs/START_HERE_FOR_CODEX.md @@ -32,11 +32,11 @@ Use Codex to build small, tested, documented vertical slices. Use this first: ```text -Read AGENTS.md, PLANS.md, CODE_REVIEW.md, docs/START_HERE_FOR_CODEX.md, docs/ARCHITECTURE.md, docs/MVP_SCOPE.md, docs/DOMAIN_MODEL.md, docs/DATA_CONTRACTS.md, and docs/TESTING.md. +Read AGENTS.md, PLANS.md, CODE_REVIEW.md, docs/START_HERE_FOR_CODEX.md, docs/ARCHITECTURE.md, docs/MVP_SCOPE.md, docs/DOMAIN_MODEL.md, docs/DATA_CONTRACTS.md, docs/CONNECTOR_SAFETY_MODEL.md, and docs/TESTING.md. Do not write code yet. -Inspect the current repository. Create a practical implementation plan for the first MVP skeleton of the Factory Intelligence Platform. The plan should include the proposed repo structure, service boundaries, initial development commands, initial tests, and documentation updates. Keep the first implementation focused on a simulator-backed Process Sentinel workflow, not the full long-term platform. +Inspect the current repository. Create a practical implementation plan for the next issue. Keep the current runtime direction centered on Docker Compose, external Demo-Factory protocol sources, read-only connectors, FactoryEvent normalization, Process Sentinel, the API, and the Workbench. ``` ## Suggested Reasoning Levels @@ -57,21 +57,21 @@ Build in this order: 1. Repository skeleton 2. Local development environment 3. Shared factory event schemas -4. Synthetic factory simulator -5. Ingestion worker -6. API service -7. Process Sentinel drift rules -8. Evidence timeline -9. Governed recommendation queue +4. Read-only connector profiles and diagnostics +5. External Demo-Factory protocol fixtures +6. Connector ingestion worker +7. Postgres-backed event storage +8. Process Sentinel worker +9. API service 10. Web UI -11. RCA/CAPA draft export -12. Tests and documentation hardening +11. Compose smoke tests +12. Documentation hardening ## What Not To Build First Avoid these until the MVP works: -- Real plant connectors +- Real plant write connectors - Direct industrial writeback - Complex agent frameworks - Multi-tenant auth diff --git a/docs/TESTING.md b/docs/TESTING.md index 5fd8a33..4d57363 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -74,29 +74,17 @@ make typecheck ``` `make test-e2e` runs the local Operations Workbench Playwright smoke test. The -test starts the FastAPI demo API and Next.js Workbench, prepares deterministic -demo state with `make demo`, and walks the manufacturer demo path in a browser: -overview, detections, detection detail, evidence timeline, recommendation -review, approve/reject/defer decision feedback, and RCA/CAPA draft preview. +current test still exercises the direct local API and Workbench path while the +Dockerized runtime smoke path is being introduced under #241. The Playwright smoke test is local-only for now. CI integration is intentionally tracked separately in #165 so this issue can keep the first browser workflow reliable before hardening it for GitHub Actions. -`make demo-api-smoke` is a backend-only smoke test for the deterministic demo -path. Run it after: - -```bash -make demo-reset -make demo-data -make demo-ingest -make demo-sentinel-run -make demo-api-smoke -``` - -It verifies the generated demo event store, detection endpoint, evidence -endpoint, recommendation endpoint, decision endpoint, and RCA/CAPA draft -endpoint without requiring the browser UI. +`make demo-api-smoke` is a legacy backend-only smoke test for generated local +state. It is retained for compatibility while the Compose smoke path is being +introduced. Prefer the Docker/Demo-Factory runtime path for new documentation +and issue work. ## Backend Testing @@ -155,13 +143,14 @@ cd apps/web npm run test:e2e ``` -If the demo state cannot be prepared, the smoke test fails with the captured -`make demo` output so the missing simulator, ingestion, Sentinel, or API step is -visible in the failure log. +If local state cannot be prepared, the smoke test fails with captured setup +output so the missing ingestion, Sentinel, or API step is visible in the +failure log. -## Simulator Testing +## External Source And Legacy Fixture Testing -The simulator must be deterministic for tests. +Demo-Factory protocol fixtures and any retained legacy generated-data fixtures +must be deterministic for tests. Requirements: diff --git a/docs/demo/DEMO_SAFE_COPY_GUIDELINES.md b/docs/demo/DEMO_SAFE_COPY_GUIDELINES.md index d142faf..e83188c 100644 --- a/docs/demo/DEMO_SAFE_COPY_GUIDELINES.md +++ b/docs/demo/DEMO_SAFE_COPY_GUIDELINES.md @@ -1,168 +1,35 @@ -# Demo-Safe Copy Guidelines - -## Purpose - -These guidelines keep the simulator-backed manufacturer demo credible, -manufacturer-readable, advisory, and clear about its boundaries. Use them when -writing Operations Workbench UI copy, API-presented demo strings, runbook text, -empty states, error states, and demo talk tracks. - -The demo should help quality, process, manufacturing, plant, and controls -stakeholders understand the investigation workflow without implying root-cause -certainty, production readiness, autonomous action, real plant integration, or -regulatory/compliance status. +# Runtime Copy Guidelines ## Approved Terms -Use these terms consistently: - -| Concept | Approved Terms | -| --- | --- | -| Demo data | simulator-backed demo data, local demo state, deterministic demo scenario, synthetic factory events | -| Process Sentinel | Process Sentinel, advisory Process Sentinel finding, drift detection, simulator-backed Process Sentinel demo | -| Detections | detection, finding, flagged drift, advisory finding, Process Sentinel detection | -| Evidence | evidence timeline, simulator-backed evidence, traceable demo events, supporting evidence, process signal evidence, quality result evidence | -| Recommendations | advisory recommendation, governed recommendation, evidence-backed recommendation, proposed action, recommended containment | -| Human review | human-reviewed decision support, named human reviewer, reviewer and reason, approve/reject/defer | -| Decision feedback | local demo audit feedback, local demo audit trail, decision feedback, stored demo decision | -| RCA/CAPA drafts | RCA/CAPA draft preview, draft investigation language, draft for human review, CAPA placeholder | -| Compliance posture | validation-aware workflow direction, future site validation work would be required before production use | - -## Terms And Claims To Avoid +- external Demo-Factory protocol data +- local development fixture +- read-only connector diagnostics +- FactoryEvent normalization +- advisory Process Sentinel finding +- evidence-backed recommendation +- human-reviewed decision support +- governed recommendation +- Operations Workbench -Avoid these terms unless the sentence is explicitly saying not to claim them: +## Claims To Avoid - production-ready -- validated -- GxP-ready -- regulatory approved -- certified +- validated for GxP use - electronic signature -- validated audit record -- root cause found -- caused by -- autonomous action -- closed-loop control -- automatic containment -- QMS/MES submission +- autonomous industrial action +- arbitrary tag write +- product released or quarantined - QMS/MES writeback -- CAPA created -- deviation closed -- product released -- real plant integration -- real plant data -- customer data -- LLM-generated decision - -## Recommendation Copy Rules - -Recommendation language must stay advisory and human-reviewed: - -- Say "recommend", "suggest", "propose", or "route for review". -- Tie recommendations to evidence IDs, detection context, and risk level. -- Include reviewer action language such as "approve, reject, or defer". -- State that the recommendation does not act on equipment, release product, or - submit external records. -- Avoid "execute", "fix", "resolve", "automatically hold", or "automatically - create a CAPA" in demo-critical UI copy. - -## Evidence And Causality Rules - -Evidence copy should explain what supports the finding without overstating -causality: - -- Use "supports", "indicates", "is consistent with", "is trending", and - "matches the demo scenario". -- Avoid saying the system proved root cause. -- Distinguish process signal evidence from quality result evidence. -- Keep source event IDs visible when discussing traceability. -- Say "simulator-backed evidence" when the user is making an important judgment. - -## Regulatory And Compliance Rules - -The demo must not imply compliance status: - -- Say the workflow is "validation-aware" or "audit-friendly" only as future - direction. -- State that future site validation work would be required before production - use. -- Treat local audit feedback as a demo trail, not an electronic signature or - validated production audit record. -- Do not claim GxP readiness, certification, regulatory approval, or production - validation. - -## Sample Microcopy - -### Overview - -- "Current demo context" -- "Local demo state" -- "Open the primary Process Sentinel finding for the demo run." -- "Run `make demo` to prepare the deterministic local scenario." - -### Detection Detail - -- "Why this was flagged" -- "This is an advisory Process Sentinel finding based on simulator-backed demo - events." -- "The finding is linked to the demo work order, batch, and affected asset." - -### Evidence Timeline - -- "Simulator-backed evidence" -- "Read the timeline from oldest to newest to see how process and quality - context support this advisory finding." -- "No evidence is available for this detection in the current local demo state." - -### Recommendation Review - -- "Recommendations are advisory decision support." -- "A named human reviewer must approve, reject, or defer this recommendation." -- "This decision does not execute industrial writeback or submit QMS/MES - records." - -### Decision Feedback - -- "Demo audit feedback" -- "Local demo audit trail" -- "This is not a validated production audit record or electronic signature." -- "Decision recorded for the simulator-backed recommendation." - -### RCA/CAPA Draft - -- "RCA/CAPA draft preview" -- "Draft investigation language for human review." -- "This draft is not automatically submitted to QMS or MES." -- "CAPA placeholder" - -### Empty States - -- "No detections were returned for the current local demo state. Run `make demo` - and refresh the Workbench." -- "No recommendation is available for the selected demo detection." -- "No RCA/CAPA draft exists for the selected detection. Open the draft from a - detection detail page after running the demo state." - -### API Errors - -- "API connection issue" -- "The Workbench could not reach the local simulator-backed API." -- "Confirm the API is running on `http://127.0.0.1:8000` with the demo event - store and Sentinel state directory." - -## Alignment References - -Keep these docs aligned with this guidance: - -- `docs/demo/MANUFACTURER_DEMO_RUNBOOK.md` -- `docs/demo/OPERATIONS_WORKBENCH_DEMO_RUNBOOK.md` -- `docs/demo/MANUFACTURER_DEMO_USER_JOURNEY.md` -- `docs/demo/OPERATIONS_WORKBENCH_INFORMATION_ARCHITECTURE.md` -- `docs/demo/PRE_DEMO_CHECKLIST.md` -- `docs/demo/TROUBLESHOOTING.md` -- `docs/GOVERNED_ACTIONS.md` -- `apps/web/README.md` - -When copy changes create a new product claim, route, data need, or compliance -interpretation, create a follow-up issue instead of expanding the demo-critical -scope silently. +- CAPA created in production +- real plant connector certified + +## Sample Copy + +- "FIP is reading external local protocol fixtures through read-only + connectors." +- "This finding is advisory and should be reviewed by a human." +- "This decision feedback is local development state, not a validated + production audit record." +- "No industrial writeback, product disposition, or QMS/MES writeback is + performed." diff --git a/docs/demo/MANUFACTURER_DEMO_RUNBOOK.md b/docs/demo/MANUFACTURER_DEMO_RUNBOOK.md index 32e81dc..a135c7b 100644 --- a/docs/demo/MANUFACTURER_DEMO_RUNBOOK.md +++ b/docs/demo/MANUFACTURER_DEMO_RUNBOOK.md @@ -1,437 +1,37 @@ -# Manufacturer Demo Runbook +# Manufacturer Runtime Discussion Guide -## Purpose +## Status -This runbook prepares and guides an 8-10 minute manufacturer demo of the Factory -Intelligence Platform MVP. The demo is a simulator-backed Process Sentinel -workflow: +This document replaces the old manufacturer runbook for the in-repo +generated-data demo. FIP's current direction is to consume external Demo-Factory +protocol data through read-only connectors. -```text -Synthetic Factory Simulator --> Ingestion Worker --> Factory Event Store --> Process Sentinel Drift Detection --> Evidence Timeline --> Governed Recommendation Queue --> Operations Workbench --> RCA/CAPA Draft Preview -``` - -All data in this runbook is simulator-backed demo data. It does not represent a -real plant, customer, batch record, or production system. - -Recommendations shown in the demo are advisory and human-reviewed. The demo does -not perform autonomous control, QMS writeback, MES writeback, equipment -writeback, product disposition, or compliance validation. - -## Prerequisites - -- Git, Make, Python 3.12 or newer, and Node.js installed locally. -- Repository cloned and opened at the repository root. -- Python development environment installed with `make setup`. -- Web dependencies installed with `cd apps/web && npm install`. -- Local ports `8000` and `3000` available. -- No real plant systems, customer data, or production credentials connected. - -## Pre-Call Checklist - -Run the detailed pre-demo checklist 15-30 minutes before the call: - -```text -docs/demo/PRE_DEMO_CHECKLIST.md -``` - -At minimum, confirm: - -1. Pull the latest demo branch or main branch you plan to show. -2. Confirm the worktree has no unrelated local changes. -3. Run the one-command demo setup from the repository root. -4. Start the API. -5. Start the Operations Workbench. -6. Open the expected URLs in browser tabs. -7. Confirm the detection, evidence, recommendation, decision controls, and - RCA/CAPA draft preview load. -8. Keep terminal tabs ready with `docs/DEMO_RUNBOOK.md` for technical fallback - details and `docs/demo/TROUBLESHOOTING.md` for demo failure recovery. - -## Exact Demo Commands - -Run the full deterministic preparation command from the repository root: - -```bash -make demo -``` - -`make demo` runs: - -```bash -make demo-reset -make demo-data -make demo-ingest -make demo-sentinel-run -make demo-api-smoke -``` - -Expected output snippets: - -```text -wrote 70 events to .local/events/fill_weight_drift_demo.jsonl (scenario=fill_weight_drift_demo, seed=120, count=30) -ingestion summary -accepted_events: 70 -rejected_events: 0 -dead_letter_count: 0 -sentinel complete: detections=1 evidence=2 recommendations=1 -demo api smoke passed -``` - -Expected demo IDs: - -```text -Detection: det_fill_weight_gradual_drift -Recommendation: rec_fill_weight_gradual_drift -``` - -Start the API in a second terminal: - -```bash -make api \ - EVENTS_STORE=.local/storage/fill_weight_drift_demo_events.jsonl \ - SENTINEL_STATE_DIR=.local/storage/fill_weight_drift_demo_sentinel -``` - -Start the Workbench in a third terminal: - -```bash -cd apps/web -npm run dev -``` - -## Manual Step Commands - -Use these only when you need to explain or troubleshoot the pipeline one step at -a time: - -```bash -make demo-reset -make demo-data -make demo-ingest -make demo-sentinel-run -make demo-api-smoke -``` - -Expected files: +## Runtime Framing -- `.local/events/fill_weight_drift_demo.jsonl` -- `.local/storage/fill_weight_drift_demo_events.jsonl` -- `.local/storage/fill_weight_drift_demo_dead_letter.jsonl` -- `.local/storage/fill_weight_drift_demo_sentinel/` - -The accepted event store should contain 70 rows. The dead-letter file should -exist and contain 0 rows. - -## Expected URLs - -API: - -```text -http://127.0.0.1:8000/docs -http://127.0.0.1:8000/sentinel/detections -http://127.0.0.1:8000/sentinel/detections/det_fill_weight_gradual_drift -http://127.0.0.1:8000/sentinel/detections/det_fill_weight_gradual_drift/evidence -http://127.0.0.1:8000/recommendations -http://127.0.0.1:8000/recommendations/rec_fill_weight_gradual_drift -http://127.0.0.1:8000/reports/rca-capa-drafts/det_fill_weight_gradual_drift -``` - -Workbench: - -```text -http://127.0.0.1:3000 -http://127.0.0.1:3000/detections -http://127.0.0.1:3000/detections/det_fill_weight_gradual_drift -http://127.0.0.1:3000/recommendations?detection_id=det_fill_weight_gradual_drift -http://127.0.0.1:3000/rca-capa-draft?detection_id=det_fill_weight_gradual_drift -``` - -## Expected Demo Path - -Open the Workbench overview first: - -```text -http://127.0.0.1:3000 -``` - -Expected story: - -- The site is Greenville Demo Site. -- The line is Line 2 in Packaging Area. -- The work order is `WO-DEMO-1007`. -- The affected asset is `filler_f_201` / Filler F-201. -- Process Sentinel flags one medium-severity drift detection. -- The evidence timeline shows fill-weight drift and matching quality results. -- The governed recommendation is in `needs_review`. -- A reviewer can approve, reject, or defer the recommendation with a reason. -- The RCA/CAPA page shows draft investigation language for human review. - -## 8-10 Minute Talk Track - -### 0:00-1:00 - Set the boundary - -"This is a simulator-backed local demo of the Factory Intelligence Platform MVP. -It is not connected to a real plant. The goal is to show the decision-support -workflow: detect a process drift, explain it with evidence, route a governed -recommendation, and draft investigation language for a human to review." - -### 1:00-2:00 - Explain the generated scenario - -"The simulator generated a deterministic packaging-line scenario for Greenville -Demo Site. In this run, the fill-weight signal gradually trends upward during -work order `WO-DEMO-1007`, and quality checks begin moving in the same -direction." - -Show: - -```text -make demo -``` - -Call out the expected output: - -```text -accepted_events: 70 -sentinel complete: detections=1 evidence=2 recommendations=1 -``` - -### 2:00-3:30 - Show the overview and detection - -Open: +Explain the demo direction this way: ```text -http://127.0.0.1:3000 -http://127.0.0.1:3000/detections/det_fill_weight_gradual_drift -``` - -"Process Sentinel is not just showing an alert. It is tying the signal to the -site, area, line, work order, batch, and affected asset so the reviewer knows -where to investigate." - -### 3:30-5:00 - Explain the evidence timeline - -"The evidence timeline is what makes this reviewable. The first item compares -baseline and recent fill-weight averages. The second item shows that quality -checks are moving in the same direction. These are cited from the simulator -events generated for the demo." - -Emphasize that the evidence is traceable demo data, not a production batch -record. - -### 5:00-6:30 - Show the governed recommendation - -Open: - -```text -http://127.0.0.1:3000/recommendations?detection_id=det_fill_weight_gradual_drift -``` - -"The platform creates an advisory recommendation, but it does not act on the -factory. A human reviewer must approve, reject, or defer it and provide a -reason." - -Use a demo reviewer such as `quality_engineer_demo` and a reason such as: - -```text -Evidence supports checking filler calibration before release review. -``` - -### 6:30-7:30 - Show decision feedback - -"After the human review, the Workbench shows the decision feedback so the user -can see who reviewed it, what decision was made, and why. In the MVP this is a -local demo audit trail, not an electronic signature or production quality -record." - -### 7:30-8:30 - Show the RCA/CAPA draft preview - -Open: - -```text -http://127.0.0.1:3000/rca-capa-draft?detection_id=det_fill_weight_gradual_drift -``` - -"This page drafts investigation language from the detection, evidence, and -recommendation. It is meant to reduce blank-page work for the quality or -operations team. It still requires human review and does not create or close a -CAPA." - -### 8:30-10:00 - Ask for manufacturer feedback - -"The next useful discussion is not whether this exact simulated line is your -line. It is whether the workflow matches how your team investigates drift, what -evidence would make the recommendation credible, and what approvals would be -needed before any real site pilot." - -## What Not To Claim - -Do not claim: - -- The platform is production ready. -- The platform is validated, certified, regulatory approved, or GxP ready. -- The demo connects to real plant systems. -- The demo uses real customer, batch, or equipment data. -- Recommendations are automatically executed. -- The platform changes machine parameters, releases product, closes deviations, - creates CAPAs, or writes to QMS/MES systems. -- The demo replaces quality engineers, operators, maintenance teams, or site - approval workflows. -- The local audit trail is an electronic signature or a validated production - audit record. - -Use these phrases instead: - -- "simulator-backed demo data" -- "human-reviewed decision support" -- "governed recommendations" -- "advisory recommendation" -- "validation-aware workflow direction" -- "future site validation work would be required before production use" - -## Troubleshooting - -For the full demo troubleshooting guide, including no detections, API down, -frontend API connection issues, stale local state, missing dependencies, empty -RCA/CAPA drafts, and recommendation decision feedback, see -`docs/demo/TROUBLESHOOTING.md`. - -For the Workbench-only browser checklist, including each screen to confirm -during the local demo, see `docs/demo/OPERATIONS_WORKBENCH_DEMO_RUNBOOK.md`. - -For approved demo terminology, words to avoid, and sample Workbench microcopy, -see `docs/demo/DEMO_SAFE_COPY_GUIDELINES.md`. - -For the intentionally small OPC UA-style demo namespace and mapping from demo -tags to FactoryEvent process and quality events, see -`docs/demo/OPC_UA_DEMO_NAMESPACE.md`. - -For the Dockerized OPC UA demo simulator endpoint, startup command, and tag -list, see `services/simulator/README.md`. - -For the demo OPC UA ingestion worker command, output path, polling interval, and -demo-only boundary, see `services/ingestion/README.md`. - -For the screen-by-screen user goal, component, data dependency, safety language, -risk, and success-criteria map, see -`docs/demo/MANUFACTURER_DEMO_USER_JOURNEY.md`. - -### No detection appears - -Run a clean deterministic setup: - -```bash -make demo -``` - -Confirm the output includes: - -```text -accepted_events: 70 -dead_letter_count: 0 -sentinel complete: detections=1 evidence=2 recommendations=1 -``` - -If not, verify that the commands are using the demo event store: - -```text -.local/storage/fill_weight_drift_demo_events.jsonl -``` - -and the demo Sentinel state directory: - -```text -.local/storage/fill_weight_drift_demo_sentinel -``` - -### API page does not open - -Confirm the API is running with the demo state: - -```bash -make api \ - EVENTS_STORE=.local/storage/fill_weight_drift_demo_events.jsonl \ - SENTINEL_STATE_DIR=.local/storage/fill_weight_drift_demo_sentinel -``` - -Then open: - -```text -http://127.0.0.1:8000/docs -``` - -If port `8000` is already in use, stop the other process before the demo so the -expected URLs stay consistent. - -### Workbench shows an API connection message - -Confirm the API is running on: - -```text -http://127.0.0.1:8000 -``` - -Restart the Workbench after setting a custom API URL only if you intentionally -run the API on another port: - -```bash -NEXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8001 npm run dev -``` - -For the manufacturer demo, prefer the default `8000` API port. - -### Recommendation decision fails - -Confirm both services are running: - -```text -API: http://127.0.0.1:8000 -Workbench: http://127.0.0.1:3000 -``` - -Use a non-empty reviewer and reason. The demo API accepts local browser origins -for the Workbench by default. - -### RCA/CAPA page is empty - -Re-run: - -```bash -make demo -``` - -Then open: - -```text -http://127.0.0.1:3000/rca-capa-draft?detection_id=det_fill_weight_gradual_drift +Demo-Factory protocols +-> FIP read-only connector worker +-> FactoryEvents +-> Process Sentinel +-> Evidence and governed recommendations +-> Operations Workbench ``` -The draft depends on the demo detection, evidence, and recommendation state -created by Process Sentinel. - -## Post-Demo Discussion Prompts +The discussion should focus on whether the evidence, source context, and +governed recommendation workflow match real investigation needs. -Use these after the scripted demo: +## Safety Boundary -- Which process or quality signals would be most valuable for a first pilot? -- Which evidence would your team need before trusting a recommendation? -- Who should review, approve, reject, or defer recommendations? -- What would the investigation handoff look like in your current deviation, - RCA, or CAPA workflow? -- Which systems would be read-only sources in a first site pilot? -- What data must stay on site, and what data must never leave the site? -- What audit trail, validation package, or change-control evidence would your - quality team expect before production use? -- What would make the workflow useful even before any writeback integration? -- Which parts of the demo felt credible, and which felt unlike your operation? +FIP does not perform industrial writeback, arbitrary tag writes, equipment +control, product disposition, QMS/MES writeback, or production CAPA creation. +Local Docker and Demo-Factory validation is not a production-readiness, +validated-use, electronic-signature, or compliance claim. -## Related Technical Runbook +## Related Docs -For deeper command details, API payload examples, and generated file paths, see -`docs/DEMO_RUNBOOK.md`. +- `docs/DEMO_RUNBOOK.md` +- `docs/demo/OPERATIONS_WORKBENCH_DEMO_RUNBOOK.md` +- `docs/demo/DEMO_SAFE_COPY_GUIDELINES.md` +- `docs/demo/TROUBLESHOOTING.md` diff --git a/docs/demo/MANUFACTURER_DEMO_USER_JOURNEY.md b/docs/demo/MANUFACTURER_DEMO_USER_JOURNEY.md index cd31213..b13645f 100644 --- a/docs/demo/MANUFACTURER_DEMO_USER_JOURNEY.md +++ b/docs/demo/MANUFACTURER_DEMO_USER_JOURNEY.md @@ -1,144 +1,26 @@ -# Manufacturer Demo User Journey +# Manufacturer Runtime User Journey ## Purpose -This document defines the manufacturer-facing 8-10 minute Operations Workbench -demo journey for the simulator-backed Process Sentinel MVP. It is a screen map -for implementation, rehearsal, and review. It keeps the demo focused on one -vertical slice: +This journey describes the current direction for showing FIP with external +Demo-Factory protocol data. -```text -Overview --> Detection List --> Detection Detail --> Evidence Timeline --> Governed Recommendation Review --> Decision Feedback --> RCA/CAPA Draft --> Manufacturer-Facing Summary -``` +| Step | User goal | FIP surface | +| --- | --- | --- | +| 1 | Confirm source connectivity | Connections and Protocol Diagnostics | +| 2 | Review source inventory | Tag/Source Browser | +| 3 | See operational risk | Overview and Detections | +| 4 | Inspect evidence | Detection Detail with Evidence Timeline | +| 5 | Review recommendation | Recommendations | +| 6 | Preview investigation language | RCA/CAPA Draft | -The journey uses simulator-backed demo data. It is not a production enterprise -UI, a validated audit record, an electronic signature workflow, a real plant -integration, or an AI/model platform demo. +## Data Boundary -## Demo Boundaries +Demo-Factory provides local protocol fixtures. FIP consumes those sources +through read-only connectors and normalizes readings into FactoryEvents before +Process Sentinel consumes them. -### In Scope +## Safety Boundary -- Show what happened in the deterministic fill-weight drift scenario. -- Explain why Process Sentinel flagged the drift. -- Show evidence that supports the finding. -- Show the advisory recommendation and the human review gate. -- Show decision feedback from the local demo audit trail. -- Preview an RCA/CAPA draft for human review. -- End with manufacturer feedback questions about investigation fit. - -### Out Of Scope - -- Authentication/RBAC or enterprise access management. -- Real plant connectors, OPC UA/MQTT production brokers, historians, MES, QMS, - LIMS, ERP, or CMMS integrations. -- Production validation, GxP readiness claims, electronic signatures, validated - audit records, or compliance package generation. -- Local Model Gateway, RAG, SLM/LLM training, model registry, AI agents, or - model governance work. -- Autonomous execution, machine-parameter changes, product release decisions, - deviation closure, CAPA creation, or QMS/MES writeback. - -## Screen Map - -| Step | Screen or Moment | User Goal | Required Screen or Component | Backend/API Data | UX Copy and Safety Language | Risks or Confusion To Avoid | Demo Success Criteria | -| --- | --- | --- | --- | --- | --- | --- | --- | -| 1 | Overview dashboard | Understand the demo context before opening a finding. | `AppShell`, `ApiConnectionBanner`, overview cards, primary detection CTA, `LoadingState`, `ErrorState`, `EmptyState`. | `GET /health`, `GET /sites`, `GET /areas`, `GET /equipment`, `GET /batches`, `GET /sentinel/detections`, `GET /recommendations`. | Show local demo context, site, area, work order, affected asset, active detections, and pending recommendations. Use "local demo" and "human-reviewed decision support." | Do not make the app feel like a disconnected mockup. Avoid implying real plant connectivity, production monitoring, or authentication coverage. | Reviewer can identify Greenville Demo Site, Line 2, `WO-DEMO-1007`, `filler_f_201`, one active detection, and one pending recommendation. | -| 2 | Detection list | See the Process Sentinel finding in a short queue. | `DetectionCard`, status/severity badges, detection route link, loading/empty/error states. | `GET /sentinel/detections`. | Use "Process Sentinel detections from the local demo run" and keep severity/confidence readable. | Avoid raw JSON, alarm-wall density, or production alert-management language. | Reviewer can open `det_fill_weight_gradual_drift` from the list and see it is medium severity. | -| 3 | Detection detail | Understand what Process Sentinel flagged and where it applies. | Detection detail page, context fields, recommendation link, RCA/CAPA draft link. | `GET /sentinel/detections/{detection_id}`. | Use advisory language such as "Why this was flagged" and avoid saying the finding proves root cause. | Avoid root-cause certainty, quality disposition claims, or language that implies the system is closing an investigation. | Reviewer can explain the finding, affected work order, time window, related assets, confidence, and status. | -| 4 | Evidence timeline | Judge whether the finding is backed by traceable evidence. | `EvidenceTimeline`, evidence cards, source event IDs, related asset/batch/work-order fields. | `GET /sentinel/detections/{detection_id}/evidence`. | Use "Simulator-backed evidence" and "traceable demo events." Explain baseline versus recent fill weight and matching quality-result movement. | Avoid hiding the evidence behind a generic insight. Avoid presenting synthetic evidence as production batch-record evidence. | Reviewer can identify the process signal evidence, quality result evidence, source event IDs, `BATCH-DEMO-1007`, and `WO-DEMO-1007`. | -| 5 | Governed recommendation review | See the proposed action and the required human approval gate. | `RecommendationPanel`, `DecisionForm`, reviewer input, reason input, approve/reject/defer controls. | `GET /recommendations`, `GET /recommendations/{recommendation_id}`, recommendation decision POST endpoints. | Use "Recommendations are advisory decision support" and "A human reviewer must approve, reject, or defer." | Avoid autonomous action language. Avoid implying approval changes machine settings, releases product, or submits records externally. | Reviewer can see the recommendation rationale, linked evidence IDs, risk level, required approval flag, and enabled human decision controls. | -| 6 | Decision feedback | Confirm who reviewed the recommendation, what decision was made, and why. | `DecisionResultCard`, status badge, refreshed recommendation state. | Decision POST response, `GET /recommendations/{recommendation_id}`, future reads from `GET /recommendations/{recommendation_id}/decisions` and `GET /recommendations/{recommendation_id}/audit`. | Use "Demo audit feedback" and "local demo audit trail." State it is not a validated production audit record or electronic signature. | Avoid presenting the local decision as an enterprise audit trail, e-signature, QMS update, or CAPA approval. | Reviewer can see reviewer, decision, reason, timestamp, recommendation ID, and updated status. | -| 7 | RCA/CAPA draft preview | Preview investigation language that a human could refine after the review. | `RcaCapaDraftPreview`, draft sections, copy control, human-review requirement. | `GET /reports/rca-capa-drafts/{detection_id}`. | Use "draft investigation language" and "requires human review." State it does not create or close a CAPA. | Avoid implying automatic RCA, automatic CAPA creation, validated quality record generation, or regulatory approval. | Reviewer can see problem statement, evidence summary, recommended containment, CAPA placeholder, and human review language. | -| 8 | Manufacturer-facing summary | Close the demo by checking whether the workflow matches real investigation needs. | Presenter summary using `MANUFACTURER_DEMO_RUNBOOK.md` discussion prompts; no new production UI required. | No new API dependency; summarize the data already shown in the Workbench. | Use "Would this evidence be enough for your team?", "Who should review this?", and "What would be needed before a site pilot?" | Avoid adding a roadmap detour into auth, AI/model platforms, real connectors, or production validation during the critical demo path. | Stakeholder can state whether the flow matches investigation needs and identify evidence, approval, and integration gaps for post-demo planning. | - -## API/Data Checklist - -The demo path depends on these local API reads and human-triggered decision -posts: - -- `GET /health` -- `GET /sites` -- `GET /areas` -- `GET /equipment` -- `GET /batches` -- `GET /sentinel/detections` -- `GET /sentinel/detections/{detection_id}` -- `GET /sentinel/detections/{detection_id}/evidence` -- `GET /recommendations` -- `GET /recommendations/{recommendation_id}` -- `POST /recommendations/{recommendation_id}/approve` -- `POST /recommendations/{recommendation_id}/reject` -- `POST /recommendations/{recommendation_id}/defer` -- `GET /recommendations/{recommendation_id}/decisions` -- `GET /recommendations/{recommendation_id}/audit` -- `GET /reports/rca-capa-drafts/{detection_id}` - -Expected demo IDs: - -- Detection: `det_fill_weight_gradual_drift` -- Recommendation: `rec_fill_weight_gradual_drift` -- Work order: `WO-DEMO-1007` -- Batch: `BATCH-DEMO-1007` -- Primary asset: `filler_f_201` - -## Copy Requirements - -For the canonical terminology list, wording to avoid, and sample microcopy, use -`docs/demo/DEMO_SAFE_COPY_GUIDELINES.md`. - -Use these phrases in the demo UI and talk track: - -- "simulator-backed demo data" -- "advisory Process Sentinel finding" -- "evidence-backed recommendation" -- "human-reviewed decision support" -- "governed recommendation" -- "local demo audit trail" -- "RCA/CAPA draft preview for human review" -- "future site validation work would be required before production use" - -Avoid these claims: - -- "validated" -- "regulatory approved" -- "production ready" -- "autonomous control" -- "automatic CAPA" -- "QMS/MES writeback" -- "electronic signature" -- "real plant data" -- "AI agent investigation" - -## Demo Success Criteria - -The demo is successful when a manufacturer stakeholder can answer: - -1. What process drift happened? -2. Why did Process Sentinel flag it? -3. Which evidence supports the finding? -4. What recommendation is proposed? -5. Where does human review happen? -6. What decision feedback is visible? -7. What RCA/CAPA draft language is available for human review? -8. What would need to change before a real site pilot? - -## Relationship To Existing Runbooks - -- Use `docs/demo/MANUFACTURER_DEMO_RUNBOOK.md` for the timed talk track and - presenter script. -- Use `docs/demo/OPERATIONS_WORKBENCH_DEMO_RUNBOOK.md` for the browser - checklist. -- Use `docs/demo/OPERATIONS_WORKBENCH_INFORMATION_ARCHITECTURE.md` for the - minimum route/page structure, embedded Evidence Timeline decision, docs-only - Demo Runbook/Help decision, and page-state requirements. -- Use this document to check that each screen has a clear user goal, required - component, data dependency, safety language, risk boundary, and success - criterion. +The journey must not imply production readiness, writeback, product disposition, +QMS/MES writeback, electronic signatures, or autonomous action. diff --git a/docs/demo/OPC_UA_DEMO_NAMESPACE.md b/docs/demo/OPC_UA_DEMO_NAMESPACE.md index 45eb3b2..125b0e7 100644 --- a/docs/demo/OPC_UA_DEMO_NAMESPACE.md +++ b/docs/demo/OPC_UA_DEMO_NAMESPACE.md @@ -1,233 +1,28 @@ -# OPC UA Demo Namespace and Data Contract +# Demo-Factory OPC-UA Fixture Notes -## Purpose +## Status -This document defines the minimum OPC UA-style namespace for the -simulator-backed manufacturer demo. It is intentionally small and demo-specific: -it exists to make the fill-weight drift story concrete, reviewable, and aligned -with the current `FactoryEvent` contracts. +The OPC-UA namespace for local source simulation belongs in Demo-Factory. FIP +uses configured connection profiles and read-only adapters to consume external +OPC-UA sources. -This is not a production namespace-browsing design, not a general tag-mapping UI, -and not a full connector configuration framework. The demo remains read-only, -simulator-backed, advisory, and human-reviewed. +## FIP Expectations -## Namespace Boundary +- OPC-UA source access is configured through `ProtocolConnectionProfile`. +- `opcua.node_ids` is the allowed read list. +- Credentials and certificates are referenced and redacted in browser-facing + responses. +- Readings are normalized into FactoryEvents before Process Sentinel consumes + them. +- FIP does not browse arbitrary namespaces as a production claim. +- FIP does not write OPC-UA nodes. -- Namespace URI: `urn:open-factory-initiative:factory-intelligence-platform:demo` -- Example namespace index: `ns=2` -- Demo site: `greenville_demo_site` -- Demo area: `packaging_area` -- Demo line: `line_2` -- Work order: `WO-DEMO-1007` -- Batch: `BATCH-DEMO-1007` -- Product: `ofi_demo_beverage` / `OFI Demo Beverage` +## Local Fixture Endpoint -The namespace mirrors the current `fill_weight_drift_demo` scenario. Node IDs -are stable demo identifiers so a future OPC UA simulator can expose the same -source signals while preserving the existing event contract. +Use local endpoints only as development fixtures, for example: -## Context Nodes - -These nodes provide the line, work order, batch, and product context needed by -Process Sentinel and the demo talk track. - -| Context | Node ID | Example value | FactoryEvent mapping | -| --- | --- | --- | --- | -| Site | `ns=2;s=OFI.Demo.Greenville.SiteId` | `greenville_demo_site` | `context.site_id` | -| Area | `ns=2;s=OFI.Demo.Greenville.Packaging.AreaId` | `packaging_area` | `context.area_id` | -| Line | `ns=2;s=OFI.Demo.Greenville.Packaging.Line2.LineId` | `line_2` | `context.line_id` | -| Work order | `ns=2;s=OFI.Demo.Greenville.Packaging.Line2.WorkOrderId` | `WO-DEMO-1007` | `context.work_order_id` | -| Batch | `ns=2;s=OFI.Demo.Greenville.Packaging.Line2.BatchId` | `BATCH-DEMO-1007` | `context.batch_id` | -| Product ID | `ns=2;s=OFI.Demo.Greenville.Packaging.Line2.ProductId` | `ofi_demo_beverage` | production work order and batch event payloads | -| Product name | `ns=2;s=OFI.Demo.Greenville.Packaging.Line2.ProductName` | `OFI Demo Beverage` | production work order and batch event payloads | - -Process and quality measurement events carry site, area, line, asset, work -order, and batch context directly. Product context belongs to the demo scenario -and production work order or batch events rather than the process measurement -payload itself. - -## Process and Quality Tags - -| Demo tag name | Node ID | Browse name | Asset | Unit | Normal or spec range | Target | Normal example | Drifting example | Drift behavior | FactoryEvent mapping | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| `filler_f_201.fill_weight` | `ns=2;s=OFI.Demo.Greenville.Packaging.Line2.FillerF201.FillWeight` | `FillWeight` | `filler_f_201` | `g` | `495.0` to `505.0` | `500.0` | `500.12` | `506.81` | Stable baseline for the first 8 samples, then about `+0.33 g` per sample with small noise. | `process.measurement.recorded` with `payload.signal_id=fill_weight`, `payload.signal_name=Fill Weight`, and `payload.tag_name=filler_f_201.fill_weight` | -| `filler_f_201.filler_nozzle_pressure` | `ns=2;s=OFI.Demo.Greenville.Packaging.Line2.FillerF201.NozzlePressure` | `NozzlePressure` | `filler_f_201` | `bar` | `1.9` to `2.4` | `2.1` | `2.10` | `2.31` | Stable baseline for the first 8 samples, then about `+0.01 bar` per sample with small noise. | `process.measurement.recorded` with `payload.signal_id=filler_nozzle_pressure`, `payload.signal_name=Filler Nozzle Pressure`, and `payload.tag_name=filler_f_201.filler_nozzle_pressure` | -| `line_2.line_speed` | `ns=2;s=OFI.Demo.Greenville.Packaging.Line2.LineSpeed` | `LineSpeed` | `line_2` | `bottles_per_min` | `115.0` to `125.0` | `120.0` | `120.0` | `120.0` | Held steady at startup for normal-mode OPC UA demo operation. | OPC UA demo support tag; no current Process Sentinel rule consumes it. | -| `checkweigher_cw_201.final_fill_weight` | `ns=2;s=OFI.Demo.Greenville.Packaging.Line2.CheckweigherCW201.FinalFillWeight` | `FinalFillWeight` | `checkweigher_cw_201` | `g` | `495.0` to `505.0` | none | `500.20 pass` | `506.70 fail` | Recorded every third sample as the inline quality marker that confirms fill-weight drift is visible in quality results. | `quality.measurement.recorded` with `payload.quality_check_type=inline_check` and `payload.measurement_name=Final Fill Weight` | - -## Demo State Tags - -The Dockerized OPC UA demo server starts in normal operation mode and exposes -these state tags: - -| Demo tag name | Node ID | Startup value | Purpose | -| --- | --- | --- | --- | -| `scenario` | `ns=2;s=OFI.Demo.Greenville.Packaging.Line2.State.Scenario` | `fill_weight_drift_demo` | Identifies the deterministic manufacturer demo scenario. | -| `drift_active` | `ns=2;s=OFI.Demo.Greenville.Packaging.Line2.State.DriftActive` | `false` | Confirms the OPC UA server starts in normal mode before any future drift behavior is enabled. | - -## Demo Control Methods - -The demo server exposes explicit control methods on the State object. These are -demo-only controls, not a production OPC UA command model and not arbitrary tag -writes. - -| Method | Node ID | Result | -| --- | --- | --- | -| `StartDrift` | `ns=2;s=OFI.Demo.Greenville.Packaging.Line2.State.StartDrift` | Starts deterministic gradual fill-weight drift, sets `scenario=fill_weight_drift_demo_gradual_drift`, and sets `drift_active=true`. Repeated calls return `already_active` without resetting the drift clock. | -| `ResetDemo` | `ns=2;s=OFI.Demo.Greenville.Packaging.Line2.State.ResetDemo` | Restores normal-mode process and quality values, sets `scenario=fill_weight_drift_demo`, and sets `drift_active=false`. | - -Drift is compressed for the 8-10 minute manufacturer demo. After `StartDrift`, -the server updates once per second; fill weight increases by `0.45 g` per -update and filler nozzle pressure increases by `0.015 bar` per update. No drift -is applied before the start method is called. - -## Mapping Rules - -1. OPC UA source values are normalized into existing `FactoryEvent` envelopes. -2. Process tags become `process.measurement.recorded` events. -3. Quality marker values become `quality.measurement.recorded` events. -4. `context.site_id`, `context.area_id`, `context.line_id`, - `context.work_order_id`, and `context.batch_id` come from the context nodes. -5. `context.asset_id` comes from the owning asset in the tag table. -6. Process event `payload.tag_name` uses the current simulator tag format: - `.`. -7. Quality events do not have a `payload.tag_name` field in the current - contract, so the OPC UA tag name is documented as source mapping metadata and - the FactoryEvent payload uses `measurement_name`. -8. In the existing local simulator path, events still use - `source.system=factory-simulator` and `source.adapter=simulator`. A future - demo OPC UA adapter can keep the same context and payload fields while - preserving the OPC UA node ID in `source.source_event_id` or adapter metadata. - -## Example Normal Process Event - -```json -{ - "event_id": "evt_opcua_demo_fill_weight_normal", - "event_type": "process.measurement.recorded", - "schema_version": "1.0.0", - "timestamp": "2026-01-01T12:02:00Z", - "source": { - "system": "factory-simulator", - "adapter": "simulator", - "source_event_id": "ns=2;s=OFI.Demo.Greenville.Packaging.Line2.FillerF201.FillWeight@2026-01-01T12:02:00Z" - }, - "context": { - "site_id": "greenville_demo_site", - "area_id": "packaging_area", - "line_id": "line_2", - "asset_id": "filler_f_201", - "batch_id": "BATCH-DEMO-1007", - "work_order_id": "WO-DEMO-1007" - }, - "payload": { - "signal_id": "fill_weight", - "signal_name": "Fill Weight", - "tag_name": "filler_f_201.fill_weight", - "value": 500.12, - "unit": "g", - "quality": "good", - "normal_min": 495.0, - "normal_max": 505.0, - "target_value": 500.0 - }, - "metadata": { - "simulated": true, - "trace_id": "trace_opcua_demo_normal" - } -} -``` - -## Example Drifting Process Event - -```json -{ - "event_id": "evt_opcua_demo_fill_weight_drifting", - "event_type": "process.measurement.recorded", - "schema_version": "1.0.0", - "timestamp": "2026-01-01T12:28:00Z", - "source": { - "system": "factory-simulator", - "adapter": "simulator", - "source_event_id": "ns=2;s=OFI.Demo.Greenville.Packaging.Line2.FillerF201.FillWeight@2026-01-01T12:28:00Z" - }, - "context": { - "site_id": "greenville_demo_site", - "area_id": "packaging_area", - "line_id": "line_2", - "asset_id": "filler_f_201", - "batch_id": "BATCH-DEMO-1007", - "work_order_id": "WO-DEMO-1007" - }, - "payload": { - "signal_id": "fill_weight", - "signal_name": "Fill Weight", - "tag_name": "filler_f_201.fill_weight", - "value": 506.81, - "unit": "g", - "quality": "good", - "normal_min": 495.0, - "normal_max": 505.0, - "target_value": 500.0 - }, - "metadata": { - "simulated": true, - "trace_id": "trace_opcua_demo_drifting" - } -} +```text +opc.tcp://localhost:4840/ofi/demo ``` -## Example Drifting Quality Event - -```json -{ - "event_id": "evt_opcua_demo_quality_drifting", - "event_type": "quality.measurement.recorded", - "schema_version": "1.0.0", - "timestamp": "2026-01-01T12:29:20Z", - "source": { - "system": "factory-simulator", - "adapter": "simulator", - "source_event_id": "ns=2;s=OFI.Demo.Greenville.Packaging.Line2.CheckweigherCW201.FinalFillWeight@2026-01-01T12:29:20Z" - }, - "context": { - "site_id": "greenville_demo_site", - "area_id": "packaging_area", - "line_id": "line_2", - "asset_id": "checkweigher_cw_201", - "batch_id": "BATCH-DEMO-1007", - "work_order_id": "WO-DEMO-1007" - }, - "payload": { - "quality_check_type": "inline_check", - "measurement_name": "Final Fill Weight", - "value": 506.7, - "unit": "g", - "result_status": "fail", - "result": "fail", - "severity": "high", - "spec_min": 495.0, - "spec_max": 505.0 - }, - "metadata": { - "simulated": true, - "trace_id": "trace_opcua_demo_quality" - } -} -``` - -## Process Sentinel Sufficiency - -The namespace is sufficient for the current Process Sentinel fill-weight drift -detection because it exposes: - -- The required `fill_weight` process signal on `filler_f_201`. -- The supporting `filler_nozzle_pressure` process signal on the same asset. -- The inline `Final Fill Weight` quality marker on `checkweigher_cw_201`. -- The Greenville demo site, packaging area, Line 2, work order, batch, and - product identifiers needed to explain the scenario. -- Normal ranges, target values, and example drifting values for evidence and - RCA/CAPA draft support. - -No autonomous control, equipment writeback, QMS/MES writeback, production -namespace browsing, or production validation is implied by this demo namespace. +This endpoint is not a production dependency or validation artifact. diff --git a/docs/demo/OPERATIONS_WORKBENCH_DEMO_RUNBOOK.md b/docs/demo/OPERATIONS_WORKBENCH_DEMO_RUNBOOK.md index 5283833..97ba104 100644 --- a/docs/demo/OPERATIONS_WORKBENCH_DEMO_RUNBOOK.md +++ b/docs/demo/OPERATIONS_WORKBENCH_DEMO_RUNBOOK.md @@ -1,134 +1,54 @@ -# Operations Workbench Demo Runbook +# Operations Workbench Runtime Notes -## Purpose +## Status -This runbook is the Workbench-focused checklist for the simulator-backed -Process Sentinel demo. It covers the local browser flow that lets a reviewer see -factory context, inspect a detection, read the evidence timeline, review a -governed recommendation, record a human decision, and preview an RCA/CAPA draft. +This document replaces the old Workbench runbook for the in-repo generated-data +demo. The Workbench is moving toward the Dockerized FIP runtime where external +Demo-Factory protocol services provide local source data. -All content is simulator-backed demo data. The Workbench communicates that -through local API context, demo state language, and workflow safety copy rather -than a persistent demo-data card. It is not production data, a validated audit -record, an electronic signature, or an industrial writeback workflow. +## Current Direction -## Start The Demo - -From the repository root, prepare deterministic demo state: - -```bash -make demo +```text +Demo-Factory protocols +-> FIP read-only connector worker +-> FactoryEvent ingestion and storage +-> Process Sentinel worker +-> FastAPI API +-> Next.js Workbench ``` -Start the API: +Start Demo-Factory separately: ```bash -make api EVENTS_STORE=.local/storage/fill_weight_drift_demo_events.jsonl SENTINEL_STATE_DIR=.local/storage/fill_weight_drift_demo_sentinel +cd ../Demo-Factory +docker compose up -d --build ``` -Start the Workbench: +Start FIP: ```bash -cd apps/web -npm run dev +cd ../Factory-Intelligence-Platform +docker compose -f infra/docker/docker-compose.yml up --build ``` -Open: - -```text -http://127.0.0.1:3000 -``` - -## Browser Flow - -1. Open the overview page and confirm it shows current local demo context. -2. Confirm the overview shows the configured API target, `/health` status, and - simulator-backed demo API source. -3. Confirm the overview shows Greenville Demo Site, current factory context, - active detections, pending recommendations, and the primary detection CTA. -4. Open `/detections` and confirm the Process Sentinel detection list renders - from the local API. -5. Open `/detections/det_fill_weight_gradual_drift`. -6. Confirm the detection detail shows severity, confidence, status, time - window, work order, and related assets. -7. Inspect the evidence timeline and confirm each item shows a readable title, - timestamp, severity, relevance score, related asset, related batch, related - work order, and source event IDs. -8. Open the recommendation review page from the detection detail. -9. Enter a demo reviewer and reason, then approve, reject, or defer the - recommendation. -10. Confirm the decision feedback shows reviewer, decision, reason, timestamp, - recommendation ID, and updated status. -11. Open the RCA/CAPA draft preview and confirm the problem statement, - evidence summary, recommended containment, CAPA placeholder, and human - review requirement are visible. - -## Accessibility Baseline - -During rehearsal, do a quick keyboard and screen-reader semantics check: - -1. Press Tab on the overview page and confirm `Skip to main content` appears. -2. Use Tab to reach primary navigation, `Open detection`, evidence workflow - links, recommendation decision controls, and RCA/CAPA draft actions. -3. Confirm the recommendation decision buttons remain disabled until reviewer - and reason fields are filled, then can be triggered from the keyboard. -4. Confirm status, severity, and risk badges include visible text and are not - explained by color alone. -5. Confirm the evidence timeline reads oldest to newest and each item exposes a - timestamp, title, description, and related IDs. - -Remaining non-blocking accessibility debt: this MVP does not claim WCAG -certification, does not include a full automated accessibility suite, and has -not gone through formal assistive-technology testing across browsers. - -## API Recovery Check - -If the API is stopped or `NEXT_PUBLIC_API_BASE_URL` points at the wrong port, -the Workbench should show an API connection issue panel rather than raw stack -output. Use the panel to confirm the configured target, then run: +For focused Workbench development before all Compose services land: ```bash -make demo make api -``` - -Restart the Workbench with `NEXT_PUBLIC_API_BASE_URL` only when the API is -intentionally running on a non-default local port. - -## Expected Demo IDs - -```text -Detection: det_fill_weight_gradual_drift -Recommendation: rec_fill_weight_gradual_drift -Work order: WO-DEMO-1007 -Batch: BATCH-DEMO-1007 -Primary asset: filler_f_201 +cd apps/web +npm run dev ``` ## Safety Language -Use these phrases while showing the Workbench: +Use these phrases: -- simulator-backed demo data +- read-only connector diagnostics +- external Demo-Factory protocol data +- local development fixture - advisory Process Sentinel finding - human-reviewed decision support - governed recommendation -- local demo audit feedback -- RCA/CAPA draft preview for human review - -Do not claim that the Workbench is production ready, validated for GxP use, -connected to real plant systems, or executing autonomous industrial actions. - -## Related Runbooks -- `docs/demo/DEMO_SAFE_COPY_GUIDELINES.md` - approved demo terminology, - wording to avoid, and sample microcopy for Workbench UI states. -- `docs/demo/OPERATIONS_WORKBENCH_INFORMATION_ARCHITECTURE.md` - minimum - Workbench route/page structure, embedded evidence decision, docs-only help - decision, and page-state requirements. -- `docs/demo/MANUFACTURER_DEMO_USER_JOURNEY.md` - screen-by-screen user - journey, data dependencies, safety language, risks, and success criteria. -- `docs/demo/MANUFACTURER_DEMO_RUNBOOK.md` - full 8-10 minute manufacturer talk - track and pre-call framing. -- `docs/demo/PRE_DEMO_CHECKLIST.md` - final call-prep checklist. -- `docs/demo/TROUBLESHOOTING.md` - local demo failure recovery. +Do not claim production readiness, validated GxP use, electronic signatures, +industrial writeback, product disposition, or QMS/MES writeback. diff --git a/docs/demo/OPERATIONS_WORKBENCH_INFORMATION_ARCHITECTURE.md b/docs/demo/OPERATIONS_WORKBENCH_INFORMATION_ARCHITECTURE.md index 6349a70..dfd2b6d 100644 --- a/docs/demo/OPERATIONS_WORKBENCH_INFORMATION_ARCHITECTURE.md +++ b/docs/demo/OPERATIONS_WORKBENCH_INFORMATION_ARCHITECTURE.md @@ -1,123 +1,44 @@ -# Operations Workbench Information Architecture +# Operations Workbench Runtime Information Architecture ## Purpose -This document defines the minimum Operations Workbench information architecture -for the manufacturer-facing Process Sentinel demo. It keeps the Workbench aligned -to the 8-10 minute investigation workflow instead of expanding into a production -enterprise application. +This document replaces the old generated-data demo IA. The Workbench should +support the Docker/Demo-Factory runtime direction while keeping the existing +Sentinel workflows usable. -The IA supports this path: - -```text -Overview --> Detections --> Detection Detail with embedded Evidence Timeline --> Recommendation Review --> RCA/CAPA Draft --> Documentation-only Demo Runbook/Help -``` - -## IA Decisions - -### Evidence Timeline - -The Evidence Timeline stays embedded in Detection Detail for the demo-critical -slice. It should not become a separate route yet because the talk track needs -reviewers to move directly from a finding to supporting evidence without -learning a deeper investigation workspace. - -Follow-up work may add evidence drilldown or source-event detail routes, but -that should be tracked separately and should not block the manufacturer demo. - -### Demo Runbook/Help - -Demo Runbook/Help remains documentation-only for this slice. The current -runbooks under `docs/demo/` are sufficient for setup, rehearsal, and presenter -guidance. An in-app help panel would add navigation and maintenance surface that -is not needed for the 8-10 minute demo. - -### Navigation Boundary - -Primary navigation remains small and manufacturer-readable: +## Runtime Navigation - Overview +- Connections +- Protocol Diagnostics +- Tag/Source Browser - Detections - Recommendations - RCA/CAPA Draft -Do not add demo-critical navigation for production workspaces, multi-site -administration, auth/RBAC, connector configuration, model management, global -search, or enterprise settings. - -## Minimum Route Structure +## Runtime Data Path -| Route or Surface | Page Name | Demo IA Role | -| --- | --- | --- | -| `/` | Overview | Orient the viewer to the site, line, work order, active finding, and pending recommendation. | -| `/detections` | Detections | Show the short Process Sentinel finding queue for the demo run. | -| `/detections/{detection_id}` | Detection Detail | Explain what was flagged, where it applies, and show the embedded Evidence Timeline. | -| Embedded in Detection Detail | Evidence Timeline | Show traceable process and quality evidence without adding a separate route. | -| `/recommendations` | Recommendations | Provide a general governed recommendation review entry point. | -| `/recommendations?detection_id={detection_id}` | Recommendation Review | Review the recommendation linked to the selected detection and record a human decision. | -| `/rca-capa-draft` | RCA/CAPA Draft | Preview the first available demo RCA/CAPA draft. | -| `/rca-capa-draft?detection_id={detection_id}` | RCA/CAPA Draft | Preview the draft tied to the selected detection. | -| `docs/demo/*.md` | Demo Runbook/Help | Provide setup, talk track, screen map, troubleshooting, and rehearsal guidance outside the app. | - -## Page Requirements - -| Page | Purpose | Primary Action | Secondary Actions | Required API/Data | Empty State | Loading State | Error State | Demo Copy | Accessibility Considerations | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| Overview | Establish the local demo context and point to the most important finding. | Open the primary detection. | Scan active detections, pending recommendations, site, area, work order, product, and asset context. | `GET /health`, `GET /sites`, `GET /areas`, `GET /equipment`, `GET /batches`, `GET /sentinel/detections`, `GET /recommendations`. | Explain that no active demo detection is available and direct the user to rerun `make demo`. | Use `LoadingState` with readable "Loading demo overview" language. | Use `ApiConnectionBanner` or `ErrorState` with local API recovery guidance. | Use "Current demo context" and local demo recovery language. | Keep the primary CTA keyboard reachable; expose status counts as text, not only color. | -| Detections | Present a short Process Sentinel queue for the demo run. | Open a detection detail page. | Compare severity, confidence, status, time window, work order, and related assets. | `GET /sentinel/detections`. | State that no detections were returned for the current local demo state. | Use `LoadingState` for the detections list. | Explain that detections could not be loaded from the local API. | Use "Process Sentinel detections from the local demo run." | Detection cards need semantic links and readable badge text. | -| Detection Detail | Explain the selected finding and keep evidence close to the finding. | Review the embedded Evidence Timeline. | Navigate to Recommendation Review or RCA/CAPA Draft for the same detection. | `GET /sentinel/detections/{detection_id}`, `GET /sentinel/detections/{detection_id}/evidence`. | If evidence is empty, state that no evidence is available for the selected detection. | Use route-level `LoadingState` for detection detail. | Show detection-not-found or API connection recovery guidance. | Use "Why this was flagged" and "Simulator-backed evidence." Avoid root-cause certainty. | Keep heading order clear; evidence rows need readable labels for score, assets, batches, work orders, and source event IDs. | -| Evidence Timeline | Show traceable evidence for the detection without adding a separate route. | Read process and quality evidence in chronological order. | Use source event IDs and context fields to explain traceability. | `GET /sentinel/detections/{detection_id}/evidence`. | Keep the empty evidence message inside Detection Detail. | Covered by Detection Detail loading state. | Covered by Detection Detail error state. | Use "traceable demo events" and "baseline versus recent fill weight." | Timeline order should be conveyed by text and document order, not only visual styling. | -| Recommendations | Provide a direct entry point to governed recommendation review. | Open or review the selected recommendation. | Move to the linked RCA/CAPA Draft after decision review. | `GET /recommendations`, `GET /recommendations/{recommendation_id}`. | Explain that no recommendation is available for the selected detection. | Use `LoadingState` for the recommendation page. | Show recommendation-not-found or local API recovery guidance. | Use "Recommendations are advisory decision support." | Form controls and workflow links need accessible names and clear focus order. | -| Recommendation Review | Make human review visible before any high-impact action is considered. | Approve, reject, or defer with reviewer and reason. | Review risk level, linked evidence IDs, refreshed status, and local demo audit feedback. | Recommendation decision POST endpoints, `GET /recommendations/{recommendation_id}`, future reads from `GET /recommendations/{recommendation_id}/decisions` and `GET /recommendations/{recommendation_id}/audit`. | Disable decision workflow if no recommendation is loaded. | Use button pending states during decision submit. | Show a readable decision failure message without losing reviewer input. | Use "governed recommendation", "human reviewer", and "local demo audit trail." | Buttons must be reachable by keyboard; decision result text must not rely on color alone. | -| RCA/CAPA Draft | Preview draft investigation language generated from the demo detection and recommendation. | Copy or review the draft language. | Return to the linked detection or recommendation. | `GET /reports/rca-capa-drafts/{detection_id}`, fallback detection list from `GET /sentinel/detections`. | Explain that no draft exists for the selected detection and suggest opening from a detection detail page. | Use `LoadingState` for the draft preview. | Show draft-not-found or local API recovery guidance. | Use "draft investigation language" and "requires human review." | Draft sections need headings and the copy control needs a clear accessible name. | -| Demo Runbook/Help | Give presenters setup, rehearsal, screen map, and troubleshooting guidance outside the app. | Open documentation before or during rehearsal. | Use troubleshooting and pre-demo checklist docs. | No app API dependency. | Not applicable. | Not applicable. | Not applicable. | Use docs language that keeps the demo simulator-backed and not production validated. | Documentation should use headings, lists, and copyable commands. | - -For approved terms, claims to avoid, and sample microcopy, use -`docs/demo/DEMO_SAFE_COPY_GUIDELINES.md`. - -## Demo-Critical Scope Rejections - -The following are intentionally not part of the demo-critical IA: - -- Production enterprise IA. -- Multi-site administration. -- Auth/RBAC and user/role settings. -- Connector or integration configuration. -- Global search. -- Mobile app IA. -- Model gateway, RAG, agents, model registry, or AI governance screens. -- Production validation, e-signature, or compliance-package workflows. -- Direct industrial writeback, product disposition, deviation closure, or - automatic CAPA creation. - -## API/Data Follow-Up Policy - -The current Workbench API client is sufficient for this IA. If a proposed page -or panel requires new API data, create a follow-up issue instead of silently -adding it to the demo-critical UI scope. - -Known future candidates: - -- Source event drilldown from evidence timeline rows. -- Persisted decision/audit history display in the recommendation page. -- Demo context summary endpoint for the Overview page. -- Optional in-app help panel if documentation-only help proves insufficient - during rehearsals. +```text +External Demo-Factory protocol sources +-> read-only connector profiles and adapters +-> FactoryEvent ingestion and storage +-> Process Sentinel +-> API +-> Workbench +``` -## Alignment Checks +## Page Boundaries -Before changing the Workbench IA, compare the change against: +- Connections: define redacted OPC-UA, MQTT, and BACnet profiles. +- Protocol Diagnostics: test configured profiles with read-only checks. +- Tag/Source Browser: inspect source inventory, mapping state, quality, asset, + area, and FactoryEvent context. +- Sentinel pages: show detections, evidence, governed recommendations, and + RCA/CAPA draft previews. -- `docs/demo/MANUFACTURER_DEMO_USER_JOURNEY.md` -- `docs/demo/MANUFACTURER_DEMO_RUNBOOK.md` -- `docs/demo/OPERATIONS_WORKBENCH_DEMO_RUNBOOK.md` -- `apps/web/app/layout.tsx` -- `apps/web/e2e/operations-workbench-demo.spec.ts` +## Scope Rejections -Any route or label mismatch should become a small follow-up issue unless it is -required to keep the current 8-10 minute demo coherent. +Do not add production enterprise IA, auth/RBAC administration, arbitrary tag +writes, equipment control, product disposition, QMS/MES writeback, production +CAPA creation, electronic signatures, or production validation claims as part +of this runtime IA. diff --git a/docs/demo/PRE_DEMO_CHECKLIST.md b/docs/demo/PRE_DEMO_CHECKLIST.md index 6e9b196..df084c0 100644 --- a/docs/demo/PRE_DEMO_CHECKLIST.md +++ b/docs/demo/PRE_DEMO_CHECKLIST.md @@ -1,132 +1,17 @@ -# Pre-Demo Checklist - -## Purpose - -Use this checklist 15-30 minutes before a manufacturer call to confirm the -simulator-backed Process Sentinel demo is ready to show. - -This checklist is for the local manufacturer demo only. It is not a production readiness checklist. It is not a security review checklist. It is not a validation package or real plant integration checklist. - -It is not: - -- A production readiness checklist. -- A security review checklist. -- A validation package. -- A real plant integration checklist. - -## Demo Boundaries - -- [ ] Say up front that the demo uses simulator-backed demo data. -- [ ] Say recommendations are advisory, human-reviewed decision support. -- [ ] Say the demo does not connect to real plant systems. -- [ ] Say the demo does not change equipment parameters, release product, - create CAPAs, write to QMS/MES, or replace site approval workflows. - -## Environment - -- [ ] Repository is on the branch you plan to show. -- [ ] `git status --short` shows no unrelated local changes. -- [ ] Python dependencies are installed with `make setup`. -- [ ] Web dependencies are installed with `cd apps/web && npm install`. -- [ ] Ports `8000` and `3000` are available. - -## Demo State - -- [ ] Run the deterministic demo setup from the repository root: - -```bash -make demo -``` - -- [ ] Confirm the smoke test passed: - -```text -demo api smoke passed -``` - -- [ ] Confirm the expected generated state is visible in the output: - -```text -accepted_events: 70 -dead_letter_count: 0 -sentinel complete: detections=1 evidence=2 recommendations=1 -``` - -- [ ] Confirm the expected detection and recommendation IDs: - -```text -Detection: det_fill_weight_gradual_drift -Recommendation: rec_fill_weight_gradual_drift -``` - -## Services - -- [ ] Start the API from the repository root: - -```bash -make api \ - EVENTS_STORE=.local/storage/fill_weight_drift_demo_events.jsonl \ - SENTINEL_STATE_DIR=.local/storage/fill_weight_drift_demo_sentinel -``` - -- [ ] Open the API docs: - -```text -http://127.0.0.1:8000/docs -``` - -- [ ] Start the Operations Workbench: - -```bash -cd apps/web -npm run dev -``` - -- [ ] Open the Workbench: - -```text -http://127.0.0.1:3000 -``` - -## Demo Path - -- [ ] Expected detection is visible: - -```text -http://127.0.0.1:3000/detections/det_fill_weight_gradual_drift -``` - -- [ ] Evidence timeline is visible and tied to simulator-backed demo events. -- [ ] Governed recommendation is visible: - -```text -http://127.0.0.1:3000/recommendations?detection_id=det_fill_weight_gradual_drift -``` - -- [ ] Recommendation action was tested with a reviewer and reason. -- [ ] Decision feedback shows who reviewed it, the decision, and the reason. -- [ ] RCA/CAPA draft preview is visible: - -```text -http://127.0.0.1:3000/rca-capa-draft?detection_id=det_fill_weight_gradual_drift -``` - -- [ ] Demo script and talk track were reviewed in - `docs/demo/MANUFACTURER_DEMO_RUNBOOK.md`. -- [ ] `docs/demo/TROUBLESHOOTING.md` is open or easy to reach for recovery. - -## Ready-To-Show Summary - -The call is ready when: - -- [ ] The repo is clean. -- [ ] Dependencies are installed. -- [ ] Demo data is generated. -- [ ] The API is running. -- [ ] The frontend is running. -- [ ] The smoke test passed. -- [ ] The expected detection is visible. -- [ ] A recommendation action was tested. -- [ ] The RCA/CAPA draft is visible. -- [ ] The demo script has been reviewed. -- [ ] Simulator-backed and human-review caveats are part of the opening script. +# Runtime Pre-Check + +Use this checklist for the Docker/Demo-Factory runtime direction. + +- [ ] Demo-Factory is available as a separate sibling checkout. +- [ ] Demo-Factory protocol services can be started with Docker Compose. +- [ ] FIP is on a branch for one issue only. +- [ ] FIP local dependencies are installed. +- [ ] The API health endpoint can be checked with `curl http://localhost:8000/health` + when the runtime stack is available. +- [ ] Connector profiles use references for secrets and certificates. +- [ ] The Workbench shows read-only diagnostics and does not expose writeback + actions. +- [ ] Presenter language says local development fixture, read-only connectors, + advisory findings, and human-reviewed recommendations. +- [ ] Presenter language does not claim production readiness, validated GxP use, + industrial writeback, product disposition, or QMS/MES writeback. diff --git a/docs/demo/TROUBLESHOOTING.md b/docs/demo/TROUBLESHOOTING.md index 85271a4..a55fe7a 100644 --- a/docs/demo/TROUBLESHOOTING.md +++ b/docs/demo/TROUBLESHOOTING.md @@ -1,409 +1,45 @@ -# Demo Troubleshooting Guide +# Runtime Troubleshooting -## Purpose +This guide replaces the old generated-data demo troubleshooting path. -Use this guide when the simulator-backed manufacturer demo does not show the -expected Process Sentinel workflow. It covers local demo preparation, API and -Workbench startup, stale generated state, empty RCA/CAPA drafts, and governed -recommendation review feedback. +## Demo-Factory Is Not Running -This guide is only for simulator-backed demo issues. It does not cover -production incidents, real plant connectors, cloud deployment, validated audit -records, QMS/MES integrations, or site-specific compliance validation. - -## Known-Good Demo Reset - -When in doubt, return to the deterministic demo state from the repository root: - -```bash -make demo -``` - -That command runs: - -```bash -make demo-reset -make demo-data -make demo-ingest -make demo-sentinel-run -make demo-api-smoke -``` - -Expected output: - -```text -wrote 70 events to .local/events/fill_weight_drift_demo.jsonl (scenario=fill_weight_drift_demo, seed=120, count=30) -accepted_events: 70 -rejected_events: 0 -dead_letter_count: 0 -sentinel complete: detections=1 evidence=2 recommendations=1 -demo api smoke passed -``` - -Then start the API and Workbench: - -```bash -make api \ - EVENTS_STORE=.local/storage/fill_weight_drift_demo_events.jsonl \ - SENTINEL_STATE_DIR=.local/storage/fill_weight_drift_demo_sentinel -``` - -```bash -cd apps/web -npm run dev -``` - -## Quick Diagnosis - -Check the demo path in this order: - -1. `make demo` completes with 70 accepted events and one detection. -2. `http://127.0.0.1:8000/docs` opens. -3. `http://127.0.0.1:8000/sentinel/detections/det_fill_weight_gradual_drift` - returns the expected detection. -4. `http://127.0.0.1:3000` opens the Operations Workbench. -5. The recommendation page can approve, reject, or defer - `rec_fill_weight_gradual_drift` with a reviewer and reason. -6. The RCA/CAPA draft page opens for `det_fill_weight_gradual_drift`. - -## Missing Dependencies - -### Symptoms - -- `.venv/bin/python: no such file or directory` -- `No module named factory_simulator`, `factory_ingestion`, - `process_sentinel`, or `factory_api` -- `npm: command not found` -- `next: command not found` -- `Cannot find module` when starting the Workbench - -### Recovery - -From the repository root: - -```bash -make setup -``` - -From the Workbench directory: - -```bash -cd apps/web -npm install -``` - -Then retry: +Start Demo-Factory from its sibling checkout: ```bash -make demo +cd ../Demo-Factory +docker compose up -d --build +docker compose ps +docker compose logs -f ``` -## No Detections +FIP should treat Demo-Factory as an external local source, not as an in-repo +service. -### Symptoms +## FIP Stack Is Not Running -- `make demo-sentinel-run` prints `detections=0`. -- `/sentinel/detections` returns an empty list. -- The Workbench detection list shows `No detections returned`. -- The recommendation queue is empty because no detection was created. - -### Common causes - -- The demo data was not generated before ingestion. -- The ingested file is from the `normal` scenario instead of - `fill_weight_drift_demo`. -- `EVENTS_STORE` points to `.local/storage/events.jsonl` instead of - `.local/storage/fill_weight_drift_demo_events.jsonl`. -- `SENTINEL_STATE_DIR` points to an old or unrelated state directory. -- Local generated files under `.local/` were edited by hand. - -### Reproduce intentionally - -This creates a no-detection case by ingesting normal simulator data into the -demo event-store path: - -```bash -make demo-reset -make simulate SCENARIO=normal SEED=120 COUNT=30 OUTPUT=.local/events/fill_weight_drift_demo.jsonl -make demo-ingest -make demo-sentinel-run -``` - -Expected result: - -```text -sentinel complete: detections=0 evidence=0 recommendations=0 -``` - -### Recovery - -Regenerate the deterministic drift demo: - -```bash -make demo -``` - -Confirm: - -```text -sentinel complete: detections=1 evidence=2 recommendations=1 -``` - -Refresh the Workbench after the API is started. Empty-state panels indicate the -local API is reachable but the expected simulator-backed demo state is missing. - -## API Not Running - -### Symptoms - -- `http://127.0.0.1:8000/docs` does not open. -- `curl http://127.0.0.1:8000/health` cannot connect. -- Workbench pages show an API connection message. -- Recommendation decisions fail because the browser cannot reach the backend. - -### Reproduce intentionally - -Stop the API terminal, then run: +Start or inspect the FIP stack: ```bash -curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8000/docs +cd ../Factory-Intelligence-Platform +docker compose -f infra/docker/docker-compose.yml up --build +docker compose -f infra/docker/docker-compose.yml ps +docker compose -f infra/docker/docker-compose.yml logs -f +curl http://localhost:8000/health ``` -Expected result when the API is down: - -```text -000 -``` - -### Recovery - -Start the API from the repository root with the demo event store and Sentinel -state: - -```bash -make api \ - EVENTS_STORE=.local/storage/fill_weight_drift_demo_events.jsonl \ - SENTINEL_STATE_DIR=.local/storage/fill_weight_drift_demo_sentinel -``` - -Then open: - -```text -http://127.0.0.1:8000/docs -``` - -If port `8000` is already in use, stop the other local process before the demo -so the expected URLs stay consistent. - -## Frontend Cannot Reach API - -### Symptoms - -- `http://127.0.0.1:3000` opens, but pages show an API connection message. -- The Workbench API base URL does not match the running FastAPI port. -- Recommendation decision buttons submit but return a connection error. +## Focused API Or Workbench Development -### Common causes - -- API is not running. -- API is running on `8001`, but the Workbench is still configured for `8000`. -- Workbench was started before `NEXT_PUBLIC_API_BASE_URL` was changed. -- Browser-side calls are using a local origin not allowed by the API CORS - configuration. - -### Recovery - -For the manufacturer demo, prefer the default API port: - -```bash -make api \ - EVENTS_STORE=.local/storage/fill_weight_drift_demo_events.jsonl \ - SENTINEL_STATE_DIR=.local/storage/fill_weight_drift_demo_sentinel -``` - -Start the Workbench: +Before all Compose services are available, use the direct development loop: ```bash +make api cd apps/web npm run dev ``` -Only use a custom API port when you are intentionally debugging a port conflict: - -```bash -NEXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8001 npm run dev -``` - -Restart the Workbench after changing `NEXT_PUBLIC_API_BASE_URL`. - -## Stale Local State - -### Symptoms - -- The Workbench shows zero pending recommendations after a previous approval, - rejection, or deferral. -- `make demo-api-smoke` passes, but the browser is looking at older generated - state. -- Detection timestamps or recommendation status differ from the expected demo - story. -- Local files under `.local/` were edited during manual testing. - -### Reproduce intentionally - -Run a recommendation decision against the local API: - -```bash -curl -s -X POST \ - http://127.0.0.1:8000/recommendations/rec_fill_weight_gradual_drift/defer \ - -H 'content-type: application/json' \ - --data '{"reviewer":"quality_engineer_demo","reason":"Intentional stale-state check."}' -``` - -The recommendation status may no longer be `needs_review` in that running API -state. - -### Recovery - -Reset every generated demo file and rebuild the expected state: - -```bash -make demo -``` - -If an API server was already running, stop and restart it after the reset: - -```bash -make api \ - EVENTS_STORE=.local/storage/fill_weight_drift_demo_events.jsonl \ - SENTINEL_STATE_DIR=.local/storage/fill_weight_drift_demo_sentinel -``` - -Refresh the Workbench browser tab after the API restart. - -## Empty RCA/CAPA Draft - -### Symptoms - -- `/rca-capa-draft?detection_id=det_fill_weight_gradual_drift` shows an empty - state or API error. -- The API endpoint for the draft returns a not-found response. -- Detection detail loads, but the RCA/CAPA draft preview has no problem - statement, evidence summary, or recommended containment. - -### Common causes - -- Process Sentinel has not run yet. -- The detection ID is wrong. -- The API is reading a Sentinel state directory that does not contain evidence - or recommendation records for the demo detection. -- The demo data was replaced with a no-detection scenario. - -### Recovery - -Rebuild the demo state: - -```bash -make demo -``` - -Confirm the API endpoint returns a draft: - -```bash -curl -s http://127.0.0.1:8000/reports/rca-capa-drafts/det_fill_weight_gradual_drift -``` - -Expected fields include: - -- `problem_statement` -- `evidence_summary` -- `recommended_containment` -- `capa_placeholder` - -The draft is advisory decision support only. It does not create, close, or -submit a CAPA. - -## Workbench Missing-State Panels - -### Symptoms - -- A page shows `Detection not found`, `No evidence available`, - `No linked recommendation found`, or `No detection available for draft - preview`. -- The API target is reachable, but the selected ID or generated local state does - not match the current demo run. - -### Recovery - -Use the visible `Next step:` line in the Workbench panel first. For a clean -manufacturer demo reset, rebuild deterministic state: - -```bash -make demo -``` - -Restart the API if it was already running, then refresh the Workbench. These -missing-state panels are local demo recovery states; they are not production -incident handling or real plant failure modes. - -## Recommendation Decision Not Updating - -### Symptoms - -- Approve, reject, or defer appears to do nothing. -- The Workbench does not show reviewer, decision, reason, or timestamp feedback. -- The recommendation remains in the previous state after the reviewer submits a - decision. - -### Common causes - -- API is not running. -- Workbench is pointed at the wrong API base URL. -- Reviewer or reason is empty. -- The recommendation was already reviewed in stale local state. -- The browser is showing an older rendered page after local state changed. - -### Recovery - -Confirm the API can record a decision: - -```bash -curl -s -X POST \ - http://127.0.0.1:8000/recommendations/rec_fill_weight_gradual_drift/defer \ - -H 'content-type: application/json' \ - --data '{"reviewer":"quality_engineer_demo","reason":"Demo troubleshooting check."}' -``` - -Expected response fields: - -- `recommendation_id` -- `reviewer` -- `decision` -- `reason` -- `created_at` - -If the API decision works but the Workbench does not update, restart the -Workbench and refresh the browser. If the recommendation was already reviewed, -run: - -```bash -make demo -``` - -Then restart the API and Workbench. - -## Simulator-Backed Demo vs Production Concerns - -Keep these categories separate during troubleshooting: - -| Demo issue | Production concern | -| --- | --- | -| Missing local `.venv` or `node_modules` | Production dependency packaging | -| Port `8000` or `3000` conflict | Production ingress or service discovery | -| Stale `.local/` generated JSONL state | Durable database migration or backup | -| No detection because the wrong simulator scenario was used | Real process-model validation | -| Browser cannot reach local API | Site network, VPN, firewall, or identity controls | -| Local recommendation decision does not refresh | Validated audit trail or electronic signature | -| Empty local RCA/CAPA draft | QMS integration or production CAPA workflow | +## Safety Boundary -Do not present any local recovery step as a production incident response. The -manufacturer demo remains simulator-backed, advisory, and human-reviewed. +Troubleshooting should never recommend industrial writeback, arbitrary tag +writes, product disposition, or QMS/MES writeback. Local Docker and +Demo-Factory checks do not prove production readiness. diff --git a/prompts/03-synthetic-factory-simulator.md b/prompts/03-synthetic-factory-simulator.md index 37ec8ee..7d50d1f 100644 --- a/prompts/03-synthetic-factory-simulator.md +++ b/prompts/03-synthetic-factory-simulator.md @@ -1,30 +1,32 @@ -# Prompt 03: Synthetic Factory Simulator +# Prompt 03: External Source Runtime Fixtures -Act as a senior simulation engineer and manufacturing software engineer. +Act as a senior manufacturing software engineer building a read-only factory +intelligence runtime. -Goal: implement a deterministic synthetic factory simulator for local development and testing. +Goal: define the next narrow slice for consuming external Demo-Factory protocol +data through FIP connectors. -Simulator must support: +Runtime direction: -- One demo site -- One area -- One line -- Three to five assets -- Work orders -- Process measurements -- Quality measurements -- Seeded random generation -- Normal scenario -- Gradual drift scenario -- Sudden excursion scenario +```text +Demo-Factory protocols +-> FIP read-only connector worker +-> FactoryEvent ingestion and storage +-> Process Sentinel worker +-> FastAPI API +-> Next.js Workbench +``` Requirements: -- Use shared event contracts -- Provide CLI or script to generate scenario data -- Include unit tests -- Include fixture snapshots -- Document how to run scenarios -- Update `docs/LEARNING_LOG.md` +- Do not build or extend an in-repo source simulator as the default path. +- Treat Demo-Factory as a separate local source/simulation repository. +- Keep connector behavior read-only. +- Use existing `ProtocolConnectionProfile` and `FactoryEvent` contracts. +- Document fixture endpoints and environment assumptions. +- Add tests for redaction, source normalization, and safety boundaries. +- Update `docs/LEARNING_LOG.md`. -Do not implement ingestion service or UI in this prompt. +Do not implement industrial writeback, arbitrary tag writes, product +disposition, QMS/MES writeback, production validation, or cloud deployment in +this prompt. diff --git a/prompts/README.md b/prompts/README.md index 8c4e26f..b38b69a 100644 --- a/prompts/README.md +++ b/prompts/README.md @@ -19,7 +19,7 @@ Recommended pattern: 1. `00-repo-inspection-and-plan.md` 2. `01-repository-foundation.md` 3. `02-shared-event-contracts.md` -4. `03-synthetic-factory-simulator.md` +4. `03-synthetic-factory-simulator.md` - now covers external source runtime fixtures 5. `04-ingestion-service.md` 6. `05-api-service.md` 7. `06-process-sentinel.md` diff --git a/services/api/README.md b/services/api/README.md index 2389e48..842eddf 100644 --- a/services/api/README.md +++ b/services/api/README.md @@ -1,6 +1,6 @@ # API Service -FastAPI shell for the simulator-backed Process Sentinel MVP. +FastAPI shell for the Factory Intelligence Platform MVP. The API currently exposes: @@ -62,9 +62,9 @@ tracked as separate follow-up work. ## Governed Recommendation Audit Reads -The simulator-backed demo API records local governed recommendation decisions -and local audit events when a reviewer approves, rejects, or defers a -recommendation. These endpoints expose that local demo trail: +The local MVP API records governed recommendation decisions and local audit +events when a reviewer approves, rejects, or defers a recommendation. These +endpoints expose that local demo trail: - `GET /recommendations?status=needs_review` - list pending review items. - `GET /recommendations/{recommendation_id}/decisions` - read reviewer, @@ -88,8 +88,8 @@ http://localhost:3001 ``` Override the comma-separated list with `FACTORY_API_CORS_ORIGINS` when testing a -different local web origin. This is intended for the simulator-backed Workbench -demo path so client-side recommendation decisions can call the API directly. +different local web origin. This is intended for local Workbench development so +client-side recommendation decisions can call the API directly. Example: diff --git a/services/ingestion/README.md b/services/ingestion/README.md index 152bbdd..d98a566 100644 --- a/services/ingestion/README.md +++ b/services/ingestion/README.md @@ -1,516 +1,56 @@ # Ingestion Service -Validates simulator events against the shared contracts and persists accepted -events. The MVP includes a JSONL-backed local store for fast development and a -PostgreSQL schema for the durable event store path. +The ingestion service validates accepted factory readings against the shared +FactoryEvent contracts and persists normalized events for downstream services. -## JSONL Input +## Current Runtime Direction -The local ingestion path expects one complete factory event JSON object per -line. Simulator output already uses this format: - -```bash -make simulate SCENARIO=gradual_drift -``` - -By default, simulator output is written to: - -```text -.local/events/gradual_drift.jsonl -``` - -Each non-empty line is parsed, validated against `packages/factory-events`, and -then routed to either the accepted event store or the dead-letter file. - -## Local Simulator-to-Ingestion Workflow - -Run the default local flow from the repository root: - -```bash -make simulate SCENARIO=gradual_drift -make ingest INPUT=.local/events/gradual_drift.jsonl -``` - -The first command creates deterministic simulator events. The second command -validates those events, appends accepted records to local storage, and routes -invalid records to dead-letter storage without stopping the full file. - -Useful command variants: - -```bash -make simulate SCENARIO=normal OUTPUT=.local/events/normal.jsonl -make ingest INPUT=.local/events/normal.jsonl -make ingest INPUT=.local/events/normal.jsonl EVENTS_STORE=.local/storage/normal-events.jsonl -``` - -For the deterministic manufacturer demo, use the demo-specific command after -generating demo data: - -```bash -make demo-data -make demo-ingest -``` - -Expected demo ingestion output: +FIP is moving toward this Docker/Demo-Factory runtime path: ```text -ingestion summary -input_file: .local/events/fill_weight_drift_demo.jsonl -accepted_events: 70 -rejected_events: 0 -dead_letter_count: 0 -accepted_output: .local/storage/fill_weight_drift_demo_events.jsonl -dead_letter_output: .local/storage/fill_weight_drift_demo_dead_letter.jsonl +Demo-Factory protocols +-> FIP read-only connector worker +-> FactoryEvent ingestion and storage +-> Process Sentinel worker +-> FastAPI API +-> Next.js Workbench ``` -The documented scenarios are `normal`, `gradual_drift`, and -`sudden_excursion`. Use `SEED`, `COUNT`, `DURATION_MINUTES`, and `OUTPUT` when -you need reproducible inputs for manual testing. - -## Local Files - -Default generated and ingested files: - -| Purpose | Default path | -| --- | --- | -| Simulator output | `.local/events/.jsonl` | -| Accepted event store | `.local/storage/events.jsonl` | -| Dead-letter records | `.local/storage/dead_letter.jsonl` | -| Process Sentinel state | `.local/storage/sentinel/` | - -The `.local/` directory is generated developer data and is ignored by Git. - -## Validation Behavior - -Ingestion validates incoming records through the shared factory event schemas -before accepted events reach storage. Validation covers: - -- Base event envelope fields such as `event_id`, `event_type`, - `schema_version`, `timestamp`, `source`, `context`, `payload`, and - `metadata` -- Supported event types such as process measurements, quality measurements, - batch events, and work order events -- Payload-specific rules such as process normal ranges, quality specification - ranges, result compatibility, and batch/work-order context consistency +Demo-Factory owns local source simulation. The ingestion service should treat +protocol readings as external source data that has already passed through a +read-only adapter boundary. -## Local Command +## Safety Boundary -Run ingestion against simulator output: +Ingestion is a validation and persistence boundary. It must not perform +industrial writeback, arbitrary tag writes, product disposition, QMS/MES +writeback, or production CAPA creation. Rejected events should be routed to +dead-letter records with enough context for debugging. -```bash -make ingest INPUT=.local/events/gradual_drift.jsonl -``` - -The equivalent direct command is: - -```bash -.venv/bin/python -m factory_ingestion.cli \ - --input .local/events/gradual_drift.jsonl \ - --events-store .local/storage/events.jsonl \ - --dead-letter .local/storage/dead_letter.jsonl -``` - -## OPC UA Demo Ingestion Worker +## Focused Development -The demo OPC UA ingestion worker polls the local simulator-backed OPC UA server -and writes normalized FactoryEvents to the same JSONL event store used by the -existing ingestion and Process Sentinel path. This is demo infrastructure, not a -production OPC UA connector, browse-all-tags implementation, certificate model, -or high-availability worker. +The repository still contains legacy local fixture commands and tests while the +Dockerized runtime is being completed. Use them only for focused regression +work on event validation and Process Sentinel behavior. -Start the demo OPC UA server in one terminal: +Run the focused ingestion tests from the repository root: ```bash -docker compose -f infra/docker/docker-compose.yml up --build opcua-simulator +.venv/bin/python -m pytest services/ingestion/tests ``` -Poll the demo tags into FactoryEvents from another terminal: +Run the broader backend checks with: ```bash -make opcua-demo-ingest +make test +make lint +make typecheck ``` -Default worker settings: - -| Setting | Default | -| --- | --- | -| Endpoint | `opc.tcp://localhost:4840/ofi/demo` | -| Poll count | `6` | -| Poll interval | `1` second | -| Output path | `.local/storage/opcua_demo_events.jsonl` | - -The default six one-second polls are compressed for the 8-10 minute demo. They -produce enough process and quality events to inspect locally without flooding -the JSONL store. - -Useful command variants: - -```bash -make opcua-demo-ingest \ - OPCUA_ENDPOINT=opc.tcp://localhost:4840/ofi/demo \ - OPCUA_POLL_COUNT=10 \ - OPCUA_POLL_INTERVAL=1 \ - OPCUA_EVENTS_STORE=.local/storage/opcua_demo_events.jsonl -``` - -The equivalent direct command is: - -```bash -.venv/bin/python -m factory_ingestion.opcua_demo_worker \ - --endpoint opc.tcp://localhost:4840/ofi/demo \ - --poll-count 6 \ - --poll-interval 1 \ - --events-store .local/storage/opcua_demo_events.jsonl -``` - -The worker reads the configured demo namespace tags for Greenville Demo Site, -Line 2, Filler F-201, and Checkweigher CW-201. It writes: - -- One work-order started event and one batch started event on the first poll so - product context is present in the accepted store. -- Process measurement events for `filler_f_201.fill_weight`, - `filler_f_201.filler_nozzle_pressure`, and `line_2.line_speed`. -- A quality measurement event for `Final Fill Weight`. - -Expected summary: - -```text -opc ua demo ingestion summary -endpoint: opc.tcp://localhost:4840/ofi/demo -poll_count: 6 -poll_interval_seconds: 1 -emitted_events: 26 -accepted_output: .local/storage/opcua_demo_events.jsonl -demo_boundary: simulator-backed demo OPC UA source; not a production connector -``` - -If the OPC UA server is unavailable, the worker exits with a readable error -that includes the endpoint and the Docker Compose startup command. - -## OPC-UA Read-Only Adapter Foundation - -The read-only OPC-UA adapter foundation reads enabled OPC-UA -`ProtocolConnectionProfile` records from the local connection profile store, -connects to each configured endpoint, reads only the node IDs listed in the -profile, and emits normalized `process.measurement.recorded` FactoryEvents into -an event store. - -This is the first production-oriented adapter foundation. It is not a -browse-all-tags tool, writeback path, PLC/DCS/SCADA control surface, or full -source-to-FactoryEvent mapping engine. Until a connector ADR explicitly expands -the behavior, the adapter is limited to: - -- enabled profiles where `protocol = "opcua"`; -- `profile.opcua.node_ids` as the complete read list; -- read-only connect and configured-node read operations; -- numeric node values that can be normalized as process measurements. - -The adapter captures source metadata in each emitted event: - -- `source.system` is `opcua:`; -- `source.adapter` is `opcua-read-only-adapter`; -- `source.source_event_id` includes the connection profile ID, configured node - ID, and poll index; -- `payload.tag_name` stores the configured OPC-UA node ID. - -The current foundation accepts default event context values because the -connection profile contract only carries `mapping_reference`, not the mapping -document itself. A future tag/source mapping issue should replace those defaults -with an explicit mapping lookup. - -Run the focused tests: - -```bash -.venv/bin/python -m pytest services/ingestion/tests/test_opcua_read_only_adapter.py -``` - -The test suite covers fake reader behavior, bad node and unavailable server -errors, FactoryEvent source metadata, and a practical integration test against -the local simulator-backed OPC-UA server. - -## MQTT Read-Only Adapter Foundation - -The read-only MQTT adapter foundation reads enabled MQTT -`ProtocolConnectionProfile` records from the local connection profile store, -uses an injected message source to consume configured topic filters, and emits -normalized `process.measurement.recorded` FactoryEvents into an event store. - -This foundation does not add a broker runtime, command publisher, arbitrary -topic discovery, writeback path, or UI ingestion controls. Until a connector ADR -explicitly expands the behavior, the adapter is limited to: - -- enabled profiles where `protocol = "mqtt"`; -- the configured broker URL in `endpoint`; -- the configured MQTT client ID, topic filters, QoS, TLS flag, and secret or - certificate references already present in the profile; -- read-only consumption from configured topic filters; -- Sparkplug-style JSON demo payloads with a non-empty `metrics` list. - -The first supported payload shape is a JSON object with optional `context` and a -`metrics` array. Each metric object must include `name` and a numeric `value`. -Optional metric fields include `signal_id`, `signal_name`, `unit`, `quality`, -and `context`. Context from the payload and metric is used to populate the -FactoryEvent context; adapter defaults fill any missing site, area, line, asset, -batch, work order, or unit values. - -The adapter captures source metadata in each emitted event: - -- `source.system` is `mqtt:`; -- `source.adapter` is `mqtt-read-only-adapter`; -- `source.source_event_id` includes the connection profile ID, topic, metric - name, and poll index; -- `payload.tag_name` stores `:`. - -Run the focused tests: - -```bash -.venv/bin/python -m pytest services/ingestion/tests/test_mqtt_read_only_adapter.py -``` - -The test suite covers fake MQTT messages, topic wildcard matching, profile -configuration access, Sparkplug-style JSON payload mapping, broker-unavailable -errors, malformed payload errors, unmapped topic errors, and the absence of a -publish/writeback surface. - -## BACnet Read-Only Adapter Foundation +## Contracts -The read-only BACnet adapter foundation reads enabled BACnet -`ProtocolConnectionProfile` records from the local connection profile store, -uses an injected object reader to poll configured object references, and emits -normalized `process.measurement.recorded` FactoryEvents into an event store. - -This foundation does not add a BACnet/IP runtime, object discovery, write path, -commandable-property changes, or UI ingestion controls. Until a connector ADR -explicitly expands the behavior, the adapter is limited to: - -- enabled profiles where `protocol = "bacnet"`; -- the configured device address in `endpoint`; -- configured `device_instance` and optional `network_number`; -- configured object references such as `analogInput:1.presentValue`; -- the configured polling interval in `acquisition.poll_interval_seconds`; -- read-only object polling through an injected reader. - -Each object reading supplies the object reference, numeric present value, unit, -optional object name, quality, and optional read timestamp. The adapter maps -each reading into a process measurement event and captures source metadata: - -- `source.system` is `bacnet:`; -- `source.adapter` is `bacnet-read-only-adapter`; -- `source.source_event_id` includes the connection profile ID, network/device - descriptor, object reference, and poll index; -- `payload.tag_name` stores the configured BACnet object reference. - -Stale readings can be rejected by passing `max_reading_age_seconds`; stale -flags from the object reader are rejected immediately. Unavailable devices, -missing objects, unconfigured objects, stale readings, and nonnumeric values -raise clear adapter errors. - -### Docker Desktop BACnet demo limitations - -Local BACnet/IP demos are UDP based and can behave differently under Docker -Desktop networking than they do on a real plant network. Broadcast discovery, -BBMD/foreign-device registration, host networking, routing between VLANs, and -UDP port exposure can all vary by host OS and Docker configuration. Treat any -Docker Desktop BACnet demo as a local smoke path only. - -Production BACnet/IP expectations are separate: configured devices and objects -should be read through network-approved routes, with explicit device addressing, -object mapping, polling intervals, and operations approval. This foundation does -not claim production readiness and does not write BACnet properties. - -Run the focused tests: - -```bash -.venv/bin/python -m pytest services/ingestion/tests/test_bacnet_read_only_adapter.py -``` - -The test suite covers fake BACnet clients, device address/network handling, -object references, polling interval access, units/object mapping, -device-unavailable errors, missing object errors, stale reading errors, and the -absence of write or commandable-property surfaces. - -## Accepted Event Storage - -Accepted events are written to the local JSONL event store: - -```text -.local/storage/events.jsonl -``` - -This is the path used by `make ingest`, `make sentinel-run`, and the local API -by default. Each line is one validated factory event serialized as JSON, so the -file can be inspected with standard command-line tools and read back through -`JsonlEventStore`. - -Inspect the accepted store: - -```bash -wc -l .local/storage/events.jsonl -head -n 3 .local/storage/events.jsonl -``` - -The local store is idempotent by `event_id`: ingesting the same deterministic -simulator output more than once validates the events again but does not append -duplicate accepted-event rows. To reset local accepted-event storage, remove the -file and run ingestion again: - -```bash -rm .local/storage/events.jsonl -make ingest INPUT=.local/events/gradual_drift.jsonl -``` - -The command prints a summary such as: - -```text -ingestion summary -input_file: .local/events/gradual_drift.jsonl -accepted_events: 56 -rejected_events: 0 -dead_letter_count: 0 -accepted_output: .local/storage/events.jsonl -dead_letter_output: .local/storage/dead_letter.jsonl -``` - -Malformed JSON, unsupported event types, and schema-invalid events are rejected -without stopping the file. The default dead-letter output path is predictable: - -```text -.local/storage/dead_letter.jsonl -``` - -Dead-letter rows include the source file, source line number, rejection -timestamp, validation error, structured error details, parsed original payload -when JSON parsing succeeds, and the raw input line so contributors can inspect -bad input without losing the rest of the batch. - -Common validation errors include: - -- Missing required base fields, such as `event_id` or `timestamp` -- Unsupported `event_type` values -- Invalid enum values, such as an unknown process `quality` -- Invalid ranges, such as `normal_min` greater than `normal_max` -- Mismatched quality `result` and `result_status` values - -Dead-letter records also include an `errors` list with structured validation -details: - -```json -{ - "line_number": 3, - "source_path": ".local/events/gradual_drift.jsonl", - "recorded_at": "2026-05-19T01:23:45.678901+00:00", - "error": "event failed shared factory event schema validation", - "errors": [ - { - "path": "quality", - "message": "Input should be 'good', 'uncertain' or 'bad'", - "type": "literal_error", - "input": "offline" - } - ], - "payload": { - "event_id": "..." - }, - "raw": "{\"event_id\": \"...\"}" -} -``` - -Malformed JSON rows cannot include a parsed `payload`, so those dead-letter -records keep `payload` as `null` and preserve the original text in `raw`. - -When ingestion rejects records, the summary includes a few validation examples -from the dead-letter file: - -```text -ingestion summary -input_file: .local/events/gradual_drift.jsonl -accepted_events: 55 -rejected_events: 1 -dead_letter_count: 1 -accepted_output: .local/storage/events.jsonl -dead_letter_output: .local/storage/dead_letter.jsonl -validation_error_examples: -- line 12: quality: Input should be 'good', 'uncertain' or 'bad' -``` - -Inspect dead-letter output: - -```bash -wc -l .local/storage/dead_letter.jsonl -head -n 3 .local/storage/dead_letter.jsonl -``` - -## Reset Local Generated Data - -To reset only the ingestion outputs and keep simulator input files: - -```bash -rm -f .local/storage/events.jsonl .local/storage/dead_letter.jsonl -rm -rf .local/storage/sentinel -``` - -To reset the full local MVP generated data set: - -```bash -rm -rf .local/events .local/storage -make simulate SCENARIO=gradual_drift -make ingest INPUT=.local/events/gradual_drift.jsonl -``` - -## Troubleshooting - -If `make ingest` cannot find the input file, generate simulator output first or -pass the exact input path: - -```bash -make simulate SCENARIO=gradual_drift -make ingest INPUT=.local/events/gradual_drift.jsonl -``` - -If `make ingest` fails because `.venv/bin/python` is missing, install the local -development environment: - -```bash -make setup -``` - -If accepted counts look lower than expected after re-running the same scenario, -the local store is likely skipping duplicate `event_id` values. Reset -`.local/storage/events.jsonl` or write to a new `EVENTS_STORE` path for a clean -manual run. - -If ingestion fails while reading `.local/storage/events.jsonl`, the local store -may contain stale or hand-edited generated data that no longer matches the -shared event contracts. Reset local generated storage, then re-run simulator and -ingestion. - -If rejected counts are non-zero, inspect `.local/storage/dead_letter.jsonl` and -the `validation_error_examples` section in the ingestion summary. Common causes -are malformed JSON, missing required event fields, unsupported `event_type` -values, invalid enum values, invalid process or quality ranges, and mismatched -quality result fields. - -## Demo Ingestion Troubleshooting - -For the manufacturer demo, use the demo-specific local paths instead of the -generic defaults: - -| Purpose | Demo path | -| --- | --- | -| Simulator output | `.local/events/fill_weight_drift_demo.jsonl` | -| Accepted event store | `.local/storage/fill_weight_drift_demo_events.jsonl` | -| Dead-letter records | `.local/storage/fill_weight_drift_demo_dead_letter.jsonl` | -| Process Sentinel state | `.local/storage/fill_weight_drift_demo_sentinel/` | - -If no detections appear after ingestion, run the clean demo sequence: - -```bash -make demo-reset -make demo-data -make demo-ingest -make demo-sentinel-run -``` +FactoryEvent schemas live in `packages/factory-events`. Fixture payloads live +under `packages/test-fixtures`. -The expected deterministic demo counts are 70 generated events, 70 accepted -events, 0 rejected events, and 0 dead-letter rows. See -`docs/DEMO_RUNBOOK.md` for the full troubleshooting checklist. +The ingestion boundary should remain stable across legacy generated fixtures, +Demo-Factory protocol fixtures, and future read-only production connectors. diff --git a/services/process-sentinel/README.md b/services/process-sentinel/README.md index 8dc1a9d..2f35298 100644 --- a/services/process-sentinel/README.md +++ b/services/process-sentinel/README.md @@ -1,9 +1,9 @@ # Process Sentinel -Explainable drift detection for the simulator-backed MVP. +Explainable drift detection for FactoryEvents. Process Sentinel reads accepted unified factory events and creates local, -queryable demo state: +queryable MVP state: - `detections.json` - `evidence.json` @@ -16,7 +16,24 @@ The API exposes those outputs through: - `/sentinel/detections/{detection_id}/evidence` - `/recommendations` -## Run +## Current Runtime Direction + +The Dockerized runtime direction is: + +```text +Demo-Factory protocols +-> FIP read-only connector worker +-> FactoryEvent ingestion and storage +-> Process Sentinel worker +-> FastAPI API +-> Next.js Workbench +``` + +Process Sentinel should consume normalized FactoryEvents from storage. It should +not know whether those events originated from Demo-Factory fixtures, legacy +generated local fixtures, or future read-only connectors. + +## Focused Development From the repository root: @@ -33,7 +50,7 @@ python -m process_sentinel.cli --events-store .local/storage/events.jsonl --stat ## MVP Detection Assumptions The MVP rules are deterministic and explainable. They are designed for local -simulator-backed development, not production release decisions. +development and reviewable behavior, not production release decisions. - `normal` should not produce drift findings or high-severity false positives. - `gradual_drift` should produce a medium-severity quality drift detection when @@ -42,7 +59,7 @@ simulator-backed development, not production release decisions. - `sudden_excursion` should produce a high-severity process excursion detection when `filler_nozzle_pressure` exceeds the MVP control limit of `2.6`. - Findings include evidence items with `source_event_ids` so contributors can - trace each finding back to simulator events. + trace each finding back to accepted FactoryEvents. - Evidence items also carry severity, relevance score, and related asset, batch, and work order IDs derived from their source events. - Process Sentinel recommendations remain advisory and require human review. @@ -53,5 +70,5 @@ simulator-backed development, not production release decisions. - The rules use fixed MVP thresholds instead of learned limits. - The local state store is JSON file based for development. -- The demo API makes findings queryable, but this is not a validated production +- The API makes findings queryable, but this is not a validated production audit trail. diff --git a/services/simulator/README.md b/services/simulator/README.md index e4b9125..67cdca6 100644 --- a/services/simulator/README.md +++ b/services/simulator/README.md @@ -1,339 +1,26 @@ -# Synthetic Factory Simulator +# Simulator Service Status -Generates deterministic simulator events for the Process Sentinel MVP. +`services/simulator` is no longer the default Factory Intelligence Platform +runtime source. -The simulator is the first source in the local MVP path: +The current runtime direction is: ```text -Synthetic Factory Simulator --> Ingestion Worker --> Factory Event Store --> Process Sentinel +Demo-Factory protocols +-> FIP read-only connector worker +-> FactoryEvent ingestion and storage +-> Process Sentinel worker +-> FastAPI API +-> Next.js Workbench ``` -Use it to create repeatable Factory Event JSONL files for local development, -tests, demos, and Process Sentinel workflows without connecting to real plant -systems. +Demo-Factory is the separate local source/simulation repository. Keep the code +in this directory available for legacy tests, historical fixtures, and narrowly +scoped compatibility checks, but do not present it as the default FIP +development or demo path. -## Available Scenarios +For current runtime instructions, use `docs/DEMO_RUNBOOK.md`. -Current generated scenarios: - -| Scenario | Purpose | Typical output path | -| --- | --- | --- | -| `normal` | Stable baseline operation with process and quality values inside limits. | `.local/events/normal.jsonl` | -| `gradual_drift` | Process signals gradually move away from baseline before a delayed quality concern. | `.local/events/gradual_drift.jsonl` | -| `sudden_excursion` | Short process excursion that creates an out-of-spec quality result. | `.local/events/sudden_excursion.jsonl` | -| `fill_weight_drift_demo` | Manufacturer-ready demo story: one product, one work order, one affected filler, baseline operation, gradual fill-weight drift, and delayed quality concern. | `.local/events/fill_weight_drift_demo.jsonl` | - -The scenario type format also reserves `noisy_sensor` for a future simulator -scenario, but event generation for that scenario is not implemented yet. - -## Run With Make - -The recommended local entry point is `make simulate`, which sets the required -Python import path for this monorepo: - -```bash -make simulate \ - SCENARIO=gradual_drift \ - SEED=42 \ - COUNT=24 \ - OUTPUT=.local/events/gradual_drift.jsonl -``` - -Useful variables: - -- `SCENARIO`: `normal`, `gradual_drift`, `sudden_excursion`, or - `fill_weight_drift_demo`. Defaults to `gradual_drift`. -- `SEED`: deterministic random seed. Defaults to `42`. -- `COUNT`: number of simulated samples. Defaults to `24`. -- `DURATION_MINUTES`: duration-style sample count. When set, it overrides `COUNT`. -- `OUTPUT`: JSONL output path. Defaults to `.local/events/$(SCENARIO).jsonl`. - -Examples: - -```bash -make simulate SCENARIO=normal OUTPUT=.local/events/normal.jsonl -make simulate SCENARIO=gradual_drift SEED=42 COUNT=24 OUTPUT=.local/events/gradual_drift.jsonl -make simulate SCENARIO=sudden_excursion SEED=7 DURATION_MINUTES=15 OUTPUT=.local/events/sudden_excursion.jsonl -make simulate SCENARIO=fill_weight_drift_demo SEED=120 COUNT=30 OUTPUT=.local/events/fill_weight_drift_demo.jsonl -``` - -## Direct CLI Usage - -Direct module execution is available when `PYTHONPATH` includes the shared event -contracts and simulator service: - -```bash -PYTHONPATH=packages/factory-events:services/simulator \ - .venv/bin/python -m factory_simulator.cli \ - --scenario gradual_drift \ - --seed 42 \ - --count 24 \ - --output .local/events/gradual_drift.jsonl -``` - -CLI options: - -- `--scenario`: one of `normal`, `gradual_drift`, `sudden_excursion`, or - `fill_weight_drift_demo`. -- `--output`: JSONL output path. Parent directories are created automatically. -- `--seed`: deterministic random seed. Defaults to `42`. -- `--count`: number of simulated samples to generate. Defaults to `24`. -- `--duration-minutes`: duration-style sample count; overrides `--count`. - -The CLI prints a summary with the event count, output path, scenario, seed, and -effective count. Invalid scenario names are rejected before any output file is -written. - -## Seeded Output - -Use `SEED` through `make simulate` or `--seed` through the CLI to reproduce -simulator output exactly for tests, demos, and issue debugging: - -```bash -make simulate SCENARIO=gradual_drift SEED=42 OUTPUT=.local/events/gradual_drift.jsonl -``` - -The same scenario, seed, count, and start time produce identical event payloads. -Changing the seed changes the generated noise while preserving valid Factory -Event payloads and the scenario's expected behavior. - -## Output Format - -Simulator output is newline-delimited JSON (`.jsonl`). Each line is one -serialized Factory Event envelope that validates against -`packages/factory-events`. - -For each simulated sample, the generator emits: - -- Two process measurement events: fill weight and filler nozzle pressure. -- One quality measurement event every third sample. - -For example, `COUNT=24` writes 56 events: 48 process measurements and 8 quality -measurements. - -Output directories are created automatically. The default local convention is -`.local/events/.jsonl`. - -## Manufacturer Demo Scenario - -Use `fill_weight_drift_demo` for the polished Process Sentinel demo path. It is -designed to tell a clear manufacturer story: - -- One site: `greenville_demo_site` / Greenville Demo Site -- One area: `packaging_area` / Packaging Area -- One line: `line_2` / Line 2 -- One product: `ofi_demo_beverage` / OFI Demo Beverage -- One work order: `WO-DEMO-1007` -- One batch: `BATCH-DEMO-1007` -- One affected asset: `filler_f_201` / Filler F-201 -- One quality check asset: `checkweigher_cw_201` / Checkweigher CW-201 -- Process tags: `fill_weight` and `filler_nozzle_pressure` -- Baseline fill-weight behavior before drift starts -- Gradual fill-weight and nozzle-pressure drift -- Delayed quality concern after the process trend is visible - -The recommended demo commands reset generated local state, generate deterministic -data, ingest it, and run Process Sentinel: - -```bash -make demo-reset -make demo-data -make demo-ingest -make demo-sentinel-run -``` - -`make demo-reset` removes only generated demo files under `.local/`, which is -ignored by Git. It does not clean source files, production databases, or real -plant data. - -Expected simulator output from `make demo-data`: - -```text -wrote 70 events to .local/events/fill_weight_drift_demo.jsonl (scenario=fill_weight_drift_demo, seed=120, count=30) -``` - -The equivalent generic simulator command is: - -```bash -make simulate \ - SCENARIO=fill_weight_drift_demo \ - SEED=120 \ - COUNT=30 \ - OUTPUT=.local/events/fill_weight_drift_demo.jsonl -``` - -Expected Process Sentinel output includes: - -```text -sentinel complete: detections=1 evidence=2 recommendations=1 -``` - -The expected demo detection lookup is: - -```text -.local/storage/fill_weight_drift_demo_sentinel/detections.json -det_fill_weight_gradual_drift -``` - -## Dockerized OPC UA Demo Simulator - -The local Docker Compose stack includes a small OPC UA demo simulator for the -manufacturer demo. It exposes normal-mode demo tags for one site, one line, one -filler asset, and one checkweigher asset. It is simulator-backed demo -infrastructure, not a production OPC UA connector. - -Start only the OPC UA demo server: - -```bash -docker compose -f infra/docker/docker-compose.yml up --build opcua-simulator -``` - -Endpoint from the host: - -```text -opc.tcp://localhost:4840/ofi/demo -``` - -The service starts in normal operation mode: - -- `scenario`: `fill_weight_drift_demo` -- `drift_active`: `false` - -Required demo process tags: - -| Tag | Node ID | Normal value | Unit | -| --- | --- | --- | --- | -| `filler_f_201.fill_weight` | `ns=2;s=OFI.Demo.Greenville.Packaging.Line2.FillerF201.FillWeight` | `500.12` | `g` | -| `filler_f_201.filler_nozzle_pressure` | `ns=2;s=OFI.Demo.Greenville.Packaging.Line2.FillerF201.NozzlePressure` | `2.1` | `bar` | -| `line_2.line_speed` | `ns=2;s=OFI.Demo.Greenville.Packaging.Line2.LineSpeed` | `120.0` | `bottles_per_min` | - -Required demo state tags: - -| Tag | Node ID | Startup value | -| --- | --- | --- | -| `scenario` | `ns=2;s=OFI.Demo.Greenville.Packaging.Line2.State.Scenario` | `fill_weight_drift_demo` | -| `drift_active` | `ns=2;s=OFI.Demo.Greenville.Packaging.Line2.State.DriftActive` | `false` | - -Demo control methods: - -| Method | Node ID | Behavior | -| --- | --- | --- | -| `StartDrift` | `ns=2;s=OFI.Demo.Greenville.Packaging.Line2.State.StartDrift` | Switches to `fill_weight_drift_demo_gradual_drift`, sets `drift_active=true`, and begins deterministic fill-weight drift. Repeated calls are safe and return `already_active`. | -| `ResetDemo` | `ns=2;s=OFI.Demo.Greenville.Packaging.Line2.State.ResetDemo` | Returns the simulator to normal-mode values, sets `scenario=fill_weight_drift_demo`, and sets `drift_active=false`. | - -Drift timing is compressed for an 8-10 minute demo. After `StartDrift`, the -server updates once per second. Fill weight increases by `0.45 g` per update, -filler nozzle pressure increases by `0.015 bar` per update, and final fill -weight follows the drifting fill-weight value. Normal-mode values do not drift -until `StartDrift` is called. - -Startup logs include the endpoint, namespace URI, scenario name, drift flag, and -a warning that the service is simulator-backed demo infrastructure. - -## Connect Simulator Output To Ingestion - -After generating JSONL, pass the same path to ingestion: - -```bash -make simulate SCENARIO=gradual_drift OUTPUT=.local/events/gradual_drift.jsonl -make ingest INPUT=.local/events/gradual_drift.jsonl -``` - -Ingestion validates each line against the shared Factory Event contracts, -appends accepted events to the local JSONL event store, and writes invalid -records to the dead-letter file. The accepted event store is then used by -Process Sentinel: - -```bash -make sentinel-run -``` - -The default paths are: - -- Generated simulator output: `.local/events/.jsonl` -- Accepted event store: `.local/storage/events.jsonl` -- Dead-letter records: `.local/storage/dead_letter.jsonl` - -## Scenario Definition Format - -Scenario definitions live in `factory_simulator.scenarios`. They describe the -demo factory context before events are generated, so scenario behavior can stay -consistent across tests, docs, CLI usage, and future fixture work. - -The format includes: - -- Scenario metadata: name, type, description, default seed, default count, and duration. -- Line context: site, area, line, work order, and optional batch. -- Assets: stable asset IDs, names, and asset types. -- Process tags: signal ID, signal name, asset, unit, baseline value, normal range, target, drift or excursion parameters, and noise band. -- Quality markers: measurement name, asset, unit, specification limits, sample cadence, and failure severity. -- Output settings: current output format and default local path. - -Example scenario definition shape: - -```json -{ - "metadata": { - "name": "gradual_drift", - "scenario_type": "gradual_drift", - "description": "Fill weight and nozzle pressure drift upward after a stable baseline.", - "default_seed": 42, - "default_count": 24, - "duration_minutes": 24 - }, - "line_context": { - "site_id": "site_demo", - "area_id": "area_packaging", - "line_id": "line_1", - "work_order_id": "wo_1001" - }, - "product": { - "product_id": "prod_demo_tablets", - "product_name": "Demo Tablets" - }, - "assets": [ - { - "asset_id": "asset_filler_1", - "asset_name": "Filler 1", - "asset_type": "filler" - }, - { - "asset_id": "asset_checkweigher_1", - "asset_name": "Checkweigher 1", - "asset_type": "checkweigher" - } - ], - "process_tags": [ - { - "signal_id": "fill_weight", - "signal_name": "Fill Weight", - "asset_id": "asset_filler_1", - "unit": "g", - "baseline_value": 500.0, - "normal_min": 495.0, - "normal_max": 505.0, - "target_value": 500.0, - "drift_per_step": 0.33, - "noise_band": 0.25 - } - ], - "quality_markers": [ - { - "measurement_name": "Final Fill Weight", - "asset_id": "asset_checkweigher_1", - "unit": "g", - "spec_min": 495.0, - "spec_max": 505.0, - "every_n_samples": 3, - "severity_on_fail": "high" - } - ], - "output": { - "format": "jsonl", - "default_path": ".local/events/gradual_drift.jsonl" - } -} -``` +Safety boundary: FIP remains read-only by default. This legacy fixture is not a +production OPC-UA connector, and FIP must not perform industrial writeback, +arbitrary tag writes, product disposition, or QMS/MES writeback. diff --git a/services/simulator/factory_simulator/opcua_server.py b/services/simulator/factory_simulator/opcua_server.py index 3dddfab..0af6378 100644 --- a/services/simulator/factory_simulator/opcua_server.py +++ b/services/simulator/factory_simulator/opcua_server.py @@ -234,7 +234,7 @@ async def run_server( ) -> None: endpoint = _endpoint(host, port, endpoint_path) LOGGER.warning( - "OPC UA demo simulator is simulator-backed demo infrastructure, " + "OPC UA demo simulator is legacy local fixture infrastructure, " "not a production OPC UA connector." ) LOGGER.info( diff --git a/services/simulator/tests/test_demo_safe_copy_guidelines_docs.py b/services/simulator/tests/test_demo_safe_copy_guidelines_docs.py deleted file mode 100644 index e9734e5..0000000 --- a/services/simulator/tests/test_demo_safe_copy_guidelines_docs.py +++ /dev/null @@ -1,137 +0,0 @@ -from pathlib import Path - -REPO_ROOT = Path(__file__).resolve().parents[3] -GUIDELINES = REPO_ROOT / "docs" / "demo" / "DEMO_SAFE_COPY_GUIDELINES.md" -GOVERNED_ACTIONS = REPO_ROOT / "docs" / "GOVERNED_ACTIONS.md" -MANUFACTURER_RUNBOOK = REPO_ROOT / "docs" / "demo" / "MANUFACTURER_DEMO_RUNBOOK.md" -OPERATIONS_RUNBOOK = REPO_ROOT / "docs" / "demo" / "OPERATIONS_WORKBENCH_DEMO_RUNBOOK.md" -JOURNEY = REPO_ROOT / "docs" / "demo" / "MANUFACTURER_DEMO_USER_JOURNEY.md" -IA_DOC = REPO_ROOT / "docs" / "demo" / "OPERATIONS_WORKBENCH_INFORMATION_ARCHITECTURE.md" -WEB_APP = REPO_ROOT / "apps" / "web" / "app" - - -def _content() -> str: - return GUIDELINES.read_text(encoding="utf-8") - - -def test_demo_safe_copy_guidelines_exist_and_are_linked() -> None: - assert GUIDELINES.exists() - - doc_path = "docs/demo/DEMO_SAFE_COPY_GUIDELINES.md" - linked_docs = [ - GOVERNED_ACTIONS, - MANUFACTURER_RUNBOOK, - OPERATIONS_RUNBOOK, - JOURNEY, - IA_DOC, - ] - - for linked_doc in linked_docs: - assert doc_path in linked_doc.read_text(encoding="utf-8") - - -def test_demo_safe_copy_guidelines_define_approved_terms() -> None: - content = _content() - - approved_terms = [ - "simulator-backed demo data", - "local demo state", - "advisory Process Sentinel finding", - "drift detection", - "detection", - "finding", - "evidence timeline", - "simulator-backed evidence", - "traceable demo events", - "advisory recommendation", - "governed recommendation", - "evidence-backed recommendation", - "human-reviewed decision support", - "named human reviewer", - "local demo audit feedback", - "local demo audit trail", - "RCA/CAPA draft preview", - "draft investigation language", - "future site validation work would be required before production use", - ] - - for term in approved_terms: - assert term in content - - -def test_demo_safe_copy_guidelines_define_claims_to_avoid() -> None: - content = _content() - - avoided_claims = [ - "production-ready", - "validated", - "GxP-ready", - "electronic signature", - "root cause found", - "caused by", - "autonomous action", - "QMS/MES submission", - "QMS/MES writeback", - "real plant integration", - "CAPA created", - "LLM-generated decision", - ] - - for claim in avoided_claims: - assert claim in content - - -def test_demo_safe_copy_guidelines_include_sample_microcopy_for_workbench_states() -> None: - content = _content() - - required_sections = [ - "### Overview", - "### Detection Detail", - "### Evidence Timeline", - "### Recommendation Review", - "### Decision Feedback", - "### RCA/CAPA Draft", - "### Empty States", - "### API Errors", - "API connection issue", - "The Workbench could not reach the local simulator-backed API.", - ] - - for section in required_sections: - assert section in content - - -def test_current_workbench_copy_keeps_demo_safe_terms_visible() -> None: - app_files = [ - WEB_APP / "components" / "demo-state.tsx", - WEB_APP / "page.tsx", - WEB_APP / "detections" / "[detectionId]" / "page.tsx", - WEB_APP / "recommendations" / "page.tsx", - WEB_APP / "recommendations" / "recommendation-review-panel.tsx", - WEB_APP / "rca-capa-draft" / "page.tsx", - ] - app_copy = "\n".join(path.read_text(encoding="utf-8") for path in app_files) - - required_safe_copy = [ - "simulator-backed demo", - "API connection issue", - "Why this was flagged", - "Simulator-backed evidence", - "Recommendations are advisory decision support", - "Demo audit feedback", - "validated production audit record or electronic signature", - "not automatically submitted to QMS or MES", - ] - forbidden_overclaims = [ - "root cause found", - "production-ready", - "GxP-ready", - "QMS/MES submission", - "CAPA created", - "real plant integration", - ] - - for phrase in required_safe_copy: - assert phrase in app_copy - for phrase in forbidden_overclaims: - assert phrase not in app_copy diff --git a/services/simulator/tests/test_demo_troubleshooting_docs.py b/services/simulator/tests/test_demo_troubleshooting_docs.py deleted file mode 100644 index 4020db5..0000000 --- a/services/simulator/tests/test_demo_troubleshooting_docs.py +++ /dev/null @@ -1,89 +0,0 @@ -from pathlib import Path - -REPO_ROOT = Path(__file__).resolve().parents[3] -TROUBLESHOOTING = REPO_ROOT / "docs" / "demo" / "TROUBLESHOOTING.md" -MANUFACTURER_RUNBOOK = REPO_ROOT / "docs" / "demo" / "MANUFACTURER_DEMO_RUNBOOK.md" -TECHNICAL_RUNBOOK = REPO_ROOT / "docs" / "DEMO_RUNBOOK.md" -README = REPO_ROOT / "README.md" -WEB_README = REPO_ROOT / "apps" / "web" / "README.md" - - -def _content() -> str: - return TROUBLESHOOTING.read_text(encoding="utf-8") - - -def test_demo_troubleshooting_guide_exists_and_is_linked() -> None: - assert TROUBLESHOOTING.exists() - - expected_links = [ - (README, "docs/demo/TROUBLESHOOTING.md"), - (WEB_README, "../../docs/demo/TROUBLESHOOTING.md"), - (MANUFACTURER_RUNBOOK, "docs/demo/TROUBLESHOOTING.md"), - (TECHNICAL_RUNBOOK, "docs/demo/TROUBLESHOOTING.md"), - ] - - for path, link in expected_links: - assert link in path.read_text(encoding="utf-8") - - -def test_demo_troubleshooting_guide_covers_required_failure_modes() -> None: - content = _content() - - required_sections = [ - "Missing Dependencies", - "No Detections", - "API Not Running", - "Frontend Cannot Reach API", - "Stale Local State", - "Empty RCA/CAPA Draft", - "Recommendation Decision Not Updating", - "Simulator-Backed Demo vs Production Concerns", - ] - - for section in required_sections: - assert section in content - - -def test_demo_troubleshooting_guide_includes_reset_and_recovery_commands() -> None: - content = _content() - - required_commands = [ - "make setup", - "npm install", - "make demo", - "make demo-reset", - "make demo-data", - "make demo-ingest", - "make demo-sentinel-run", - "make demo-api-smoke", - "make api", - "EVENTS_STORE=.local/storage/fill_weight_drift_demo_events.jsonl", - "SENTINEL_STATE_DIR=.local/storage/fill_weight_drift_demo_sentinel", - "cd apps/web", - "npm run dev", - "NEXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8001 npm run dev", - "curl -s -o /dev/null -w \"%{http_code}\" http://127.0.0.1:8000/docs", - "curl -s -X POST", - ] - - for command in required_commands: - assert command in content - - -def test_demo_troubleshooting_guide_labels_demo_boundaries() -> None: - content = _content() - - required_terms = [ - "simulator-backed demo issues", - "production incidents", - "real plant connectors", - "cloud deployment", - "validated audit", - "QMS/MES integrations", - "site-specific compliance validation", - "advisory", - "human-reviewed", - ] - - for term in required_terms: - assert term in content diff --git a/services/simulator/tests/test_manufacturer_demo_runbook_docs.py b/services/simulator/tests/test_manufacturer_demo_runbook_docs.py deleted file mode 100644 index dd39e33..0000000 --- a/services/simulator/tests/test_manufacturer_demo_runbook_docs.py +++ /dev/null @@ -1,107 +0,0 @@ -from pathlib import Path - -REPO_ROOT = Path(__file__).resolve().parents[3] -RUNBOOK = REPO_ROOT / "docs" / "demo" / "MANUFACTURER_DEMO_RUNBOOK.md" -README = REPO_ROOT / "README.md" -WEB_README = REPO_ROOT / "apps" / "web" / "README.md" - - -def _content() -> str: - return RUNBOOK.read_text(encoding="utf-8") - - -def test_manufacturer_demo_runbook_exists_and_is_linked() -> None: - assert RUNBOOK.exists() - - runbook_path = "docs/demo/MANUFACTURER_DEMO_RUNBOOK.md" - assert runbook_path in README.read_text(encoding="utf-8") - assert "../../docs/demo/MANUFACTURER_DEMO_RUNBOOK.md" in WEB_README.read_text( - encoding="utf-8" - ) - - -def test_manufacturer_demo_runbook_covers_required_demo_commands_and_outputs() -> None: - content = _content() - - expected_terms = [ - "make demo", - "make demo-reset", - "make demo-data", - "make demo-ingest", - "make demo-sentinel-run", - "make demo-api-smoke", - "make api", - "EVENTS_STORE=.local/storage/fill_weight_drift_demo_events.jsonl", - "SENTINEL_STATE_DIR=.local/storage/fill_weight_drift_demo_sentinel", - "cd apps/web", - "npm run dev", - "wrote 70 events", - "accepted_events: 70", - "rejected_events: 0", - "dead_letter_count: 0", - "sentinel complete: detections=1 evidence=2 recommendations=1", - "demo api smoke passed", - ] - - for term in expected_terms: - assert term in content - - -def test_manufacturer_demo_runbook_covers_expected_urls_and_artifacts() -> None: - content = _content() - - expected_terms = [ - "http://127.0.0.1:8000/docs", - "http://127.0.0.1:8000/sentinel/detections", - "http://127.0.0.1:8000/sentinel/detections/det_fill_weight_gradual_drift", - ( - "http://127.0.0.1:8000/sentinel/detections/" - "det_fill_weight_gradual_drift/evidence" - ), - "http://127.0.0.1:8000/recommendations", - "http://127.0.0.1:8000/recommendations/rec_fill_weight_gradual_drift", - ( - "http://127.0.0.1:8000/reports/rca-capa-drafts/" - "det_fill_weight_gradual_drift" - ), - "http://127.0.0.1:3000", - "http://127.0.0.1:3000/detections", - "http://127.0.0.1:3000/detections/det_fill_weight_gradual_drift", - ( - "http://127.0.0.1:3000/recommendations?detection_id=" - "det_fill_weight_gradual_drift" - ), - ( - "http://127.0.0.1:3000/rca-capa-draft?detection_id=" - "det_fill_weight_gradual_drift" - ), - "det_fill_weight_gradual_drift", - "rec_fill_weight_gradual_drift", - "WO-DEMO-1007", - "filler_f_201", - ] - - for term in expected_terms: - assert term in content - - -def test_manufacturer_demo_runbook_covers_safety_script_and_feedback_requirements() -> None: - content = _content() - - required_phrases = [ - "simulator-backed demo data", - "advisory and human-reviewed", - "Pre-Call Checklist", - "8-10 Minute Talk Track", - "What Not To Claim", - "Troubleshooting", - "Post-Demo Discussion Prompts", - "human-reviewed decision support", - "governed recommendations", - "future site validation work would be required before production use", - "not perform autonomous control", - "does not create or close", - ] - - for phrase in required_phrases: - assert phrase in content diff --git a/services/simulator/tests/test_manufacturer_demo_user_journey_docs.py b/services/simulator/tests/test_manufacturer_demo_user_journey_docs.py deleted file mode 100644 index a01a0f6..0000000 --- a/services/simulator/tests/test_manufacturer_demo_user_journey_docs.py +++ /dev/null @@ -1,120 +0,0 @@ -from pathlib import Path - -REPO_ROOT = Path(__file__).resolve().parents[3] -JOURNEY = REPO_ROOT / "docs" / "demo" / "MANUFACTURER_DEMO_USER_JOURNEY.md" -MANUFACTURER_RUNBOOK = REPO_ROOT / "docs" / "demo" / "MANUFACTURER_DEMO_RUNBOOK.md" -OPERATIONS_RUNBOOK = REPO_ROOT / "docs" / "demo" / "OPERATIONS_WORKBENCH_DEMO_RUNBOOK.md" - - -def _content() -> str: - return JOURNEY.read_text(encoding="utf-8") - - -def test_manufacturer_demo_user_journey_exists_and_is_linked() -> None: - assert JOURNEY.exists() - - journey_path = "docs/demo/MANUFACTURER_DEMO_USER_JOURNEY.md" - assert journey_path in MANUFACTURER_RUNBOOK.read_text(encoding="utf-8") - assert journey_path in OPERATIONS_RUNBOOK.read_text(encoding="utf-8") - - -def test_manufacturer_demo_user_journey_covers_required_screens() -> None: - content = _content() - - required_terms = [ - "Overview dashboard", - "Detection list", - "Detection detail", - "Evidence timeline", - "Governed recommendation review", - "Decision feedback", - "RCA/CAPA draft preview", - "Manufacturer-facing summary", - ] - - for term in required_terms: - assert term in content - - -def test_manufacturer_demo_user_journey_maps_goal_component_data_risk_and_success() -> None: - content = _content() - - required_terms = [ - "User Goal", - "Required Screen or Component", - "Backend/API Data", - "UX Copy and Safety Language", - "Risks or Confusion To Avoid", - "Demo Success Criteria", - "AppShell", - "ApiConnectionBanner", - "DetectionCard", - "EvidenceTimeline", - "RecommendationPanel", - "DecisionForm", - "DecisionResultCard", - "RcaCapaDraftPreview", - "EmptyState", - "ErrorState", - "LoadingState", - ] - - for term in required_terms: - assert term in content - - -def test_manufacturer_demo_user_journey_covers_api_dependencies_and_demo_ids() -> None: - content = _content() - - required_terms = [ - "GET /health", - "GET /sites", - "GET /areas", - "GET /equipment", - "GET /batches", - "GET /sentinel/detections", - "GET /sentinel/detections/{detection_id}", - "GET /sentinel/detections/{detection_id}/evidence", - "GET /recommendations", - "GET /recommendations/{recommendation_id}", - "POST /recommendations/{recommendation_id}/approve", - "POST /recommendations/{recommendation_id}/reject", - "POST /recommendations/{recommendation_id}/defer", - "GET /recommendations/{recommendation_id}/decisions", - "GET /recommendations/{recommendation_id}/audit", - "GET /reports/rca-capa-drafts/{detection_id}", - "det_fill_weight_gradual_drift", - "rec_fill_weight_gradual_drift", - "WO-DEMO-1007", - "BATCH-DEMO-1007", - "filler_f_201", - ] - - for term in required_terms: - assert term in content - - -def test_manufacturer_demo_user_journey_preserves_demo_boundaries() -> None: - content = _content() - - required_terms = [ - "simulator-backed demo data", - "advisory Process Sentinel finding", - "human-reviewed decision support", - "governed recommendation", - "local demo audit trail", - "RCA/CAPA draft preview for human review", - "Authentication/RBAC", - "Real plant connectors", - "Production validation", - "Local Model Gateway", - "RAG", - "SLM/LLM training", - "model registry", - "AI agents", - "Autonomous execution", - "QMS/MES writeback", - ] - - for term in required_terms: - assert term in content diff --git a/services/simulator/tests/test_opc_ua_demo_namespace_docs.py b/services/simulator/tests/test_opc_ua_demo_namespace_docs.py deleted file mode 100644 index 1757a83..0000000 --- a/services/simulator/tests/test_opc_ua_demo_namespace_docs.py +++ /dev/null @@ -1,143 +0,0 @@ -from __future__ import annotations - -import json -import re -from pathlib import Path - -from factory_events import FactoryEvent, validate_event -from factory_simulator.scenarios import scenario_definition_for - -REPO_ROOT = Path(__file__).resolve().parents[3] -NAMESPACE_DOC = REPO_ROOT / "docs" / "demo" / "OPC_UA_DEMO_NAMESPACE.md" -MANUFACTURER_RUNBOOK = REPO_ROOT / "docs" / "demo" / "MANUFACTURER_DEMO_RUNBOOK.md" - - -def _content() -> str: - return NAMESPACE_DOC.read_text(encoding="utf-8") - - -def _factory_event_examples() -> list[FactoryEvent]: - content = _content() - blocks = re.findall(r"```json\n(.*?)\n```", content, flags=re.DOTALL) - return [validate_event(json.loads(block)) for block in blocks] - - -def test_opc_ua_demo_namespace_exists_and_is_linked() -> None: - assert NAMESPACE_DOC.exists() - assert "docs/demo/OPC_UA_DEMO_NAMESPACE.md" in MANUFACTURER_RUNBOOK.read_text( - encoding="utf-8" - ) - - -def test_opc_ua_demo_namespace_defines_demo_boundary_and_context() -> None: - content = _content() - scenario = scenario_definition_for("fill_weight_drift_demo") - - required_terms = [ - "intentionally small and demo-specific", - "not a production namespace-browsing design", - "not a general tag-mapping UI", - "not a full connector configuration framework", - "simulator-backed", - "human-reviewed", - "urn:open-factory-initiative:factory-intelligence-platform:demo", - "ns=2", - scenario.line_context.site_id, - scenario.line_context.area_id, - scenario.line_context.line_id, - scenario.line_context.work_order_id, - scenario.line_context.batch_id, - scenario.product.product_id, - scenario.product.product_name, - "Product context belongs to the demo scenario", - ] - - for term in required_terms: - assert term in content - - -def test_opc_ua_demo_namespace_defines_required_tags_and_node_ids() -> None: - content = _content() - - expected_terms = [ - "filler_f_201.fill_weight", - "ns=2;s=OFI.Demo.Greenville.Packaging.Line2.FillerF201.FillWeight", - "FillWeight", - "Fill Weight", - "495.0", - "505.0", - "500.0", - "+0.33 g", - "filler_f_201.filler_nozzle_pressure", - "ns=2;s=OFI.Demo.Greenville.Packaging.Line2.FillerF201.NozzlePressure", - "NozzlePressure", - "Filler Nozzle Pressure", - "1.9", - "2.4", - "2.1", - "+0.01 bar", - "checkweigher_cw_201.final_fill_weight", - "ns=2;s=OFI.Demo.Greenville.Packaging.Line2.CheckweigherCW201.FinalFillWeight", - "FinalFillWeight", - "Final Fill Weight", - "inline_check", - "quality.measurement.recorded", - "process.measurement.recorded", - ] - - for term in expected_terms: - assert term in content - - -def test_opc_ua_demo_namespace_examples_validate_against_factory_event_contracts() -> None: - events = _factory_event_examples() - - assert len(events) == 3 - assert [event.event_type for event in events] == [ - "process.measurement.recorded", - "process.measurement.recorded", - "quality.measurement.recorded", - ] - assert all(event.schema_version == "1.0.0" for event in events) - assert all(event.metadata.simulated is True for event in events) - - -def test_opc_ua_demo_namespace_examples_cover_fill_weight_drift_detection_needs() -> None: - events = _factory_event_examples() - fill_weight_events = [ - event - for event in events - if event.event_type == "process.measurement.recorded" - and event.payload.signal_id == "fill_weight" - ] - quality_events = [ - event for event in events if event.event_type == "quality.measurement.recorded" - ] - - assert len(fill_weight_events) == 2 - assert len(quality_events) == 1 - - for event in fill_weight_events: - assert event.context.site_id == "greenville_demo_site" - assert event.context.area_id == "packaging_area" - assert event.context.line_id == "line_2" - assert event.context.asset_id == "filler_f_201" - assert event.context.work_order_id == "WO-DEMO-1007" - assert event.context.batch_id == "BATCH-DEMO-1007" - assert event.payload.tag_name == "filler_f_201.fill_weight" - assert event.payload.normal_min == 495.0 - assert event.payload.normal_max == 505.0 - assert event.payload.target_value == 500.0 - - assert fill_weight_events[0].payload.value == 500.12 - assert fill_weight_events[1].payload.value == 506.81 - assert fill_weight_events[1].payload.value > fill_weight_events[1].payload.normal_max - - quality_event = quality_events[0] - assert quality_event.context.asset_id == "checkweigher_cw_201" - assert quality_event.context.work_order_id == "WO-DEMO-1007" - assert quality_event.context.batch_id == "BATCH-DEMO-1007" - assert quality_event.payload.quality_check_type == "inline_check" - assert quality_event.payload.measurement_name == "Final Fill Weight" - assert quality_event.payload.result_status == "fail" - assert quality_event.payload.severity == "high" diff --git a/services/simulator/tests/test_opcua_demo_server.py b/services/simulator/tests/test_opcua_demo_server.py index e5560eb..23cdc87 100644 --- a/services/simulator/tests/test_opcua_demo_server.py +++ b/services/simulator/tests/test_opcua_demo_server.py @@ -76,16 +76,18 @@ def test_opcua_demo_compose_service_is_documented_and_demo_scoped() -> None: assert term in compose assert '"python", "-m", "factory_simulator.opcua_server"' in dockerfile - assert "docker compose -f infra/docker/docker-compose.yml up --build opcua-simulator" in readme - assert "opc.tcp://localhost:4840/ofi/demo" in readme - assert "simulator-backed demo" in readme - assert "not a production OPC UA connector" in readme + assert "no longer the default Factory Intelligence Platform" in readme + assert "runtime source" in readme + assert "legacy tests" in readme + assert "docs/DEMO_RUNBOOK.md" in readme + assert "not a" in readme + assert "production OPC-UA connector" in readme for term in [ - "line_2.line_speed", - "drift_active", - "scenario", - "normal operation mode", + "Demo-Factory OPC-UA Fixture Notes", + "read-only adapters", + "opc.tcp://localhost:4840/ofi/demo", + "FIP does not write OPC-UA nodes", ]: assert term in namespace_doc @@ -143,7 +145,7 @@ async def exercise_server() -> None: } asyncio.run(exercise_server()) - assert "simulator-backed demo infrastructure" in caplog.text + assert "legacy local fixture infrastructure" in caplog.text assert "OPC UA demo simulator listening endpoint=opc.tcp://127.0.0.1:" in caplog.text assert "scenario=fill_weight_drift_demo" in caplog.text diff --git a/services/simulator/tests/test_operations_workbench_ia_docs.py b/services/simulator/tests/test_operations_workbench_ia_docs.py deleted file mode 100644 index 546ce04..0000000 --- a/services/simulator/tests/test_operations_workbench_ia_docs.py +++ /dev/null @@ -1,111 +0,0 @@ -from pathlib import Path - -REPO_ROOT = Path(__file__).resolve().parents[3] -IA_DOC = REPO_ROOT / "docs" / "demo" / "OPERATIONS_WORKBENCH_INFORMATION_ARCHITECTURE.md" -JOURNEY = REPO_ROOT / "docs" / "demo" / "MANUFACTURER_DEMO_USER_JOURNEY.md" -OPERATIONS_RUNBOOK = REPO_ROOT / "docs" / "demo" / "OPERATIONS_WORKBENCH_DEMO_RUNBOOK.md" -APP_LAYOUT = REPO_ROOT / "apps" / "web" / "app" / "layout.tsx" -E2E_SPEC = REPO_ROOT / "apps" / "web" / "e2e" / "operations-workbench-demo.spec.ts" - - -def _content() -> str: - return IA_DOC.read_text(encoding="utf-8") - - -def test_operations_workbench_ia_doc_exists_and_is_linked() -> None: - assert IA_DOC.exists() - - doc_path = "docs/demo/OPERATIONS_WORKBENCH_INFORMATION_ARCHITECTURE.md" - assert doc_path in JOURNEY.read_text(encoding="utf-8") - assert doc_path in OPERATIONS_RUNBOOK.read_text(encoding="utf-8") - - -def test_operations_workbench_ia_matches_current_route_map() -> None: - content = _content() - layout = APP_LAYOUT.read_text(encoding="utf-8") - e2e_spec = E2E_SPEC.read_text(encoding="utf-8") - - required_routes = [ - "`/`", - "`/detections`", - "`/detections/{detection_id}`", - "`/recommendations`", - "`/recommendations?detection_id={detection_id}`", - "`/rca-capa-draft`", - "`/rca-capa-draft?detection_id={detection_id}`", - "`docs/demo/*.md`", - ] - nav_labels = ["Overview", "Detections", "Recommendations", "RCA/CAPA Draft"] - - for route in required_routes: - assert route in content - for label in nav_labels: - assert label in content - assert label in layout - assert "Evidence timeline" in e2e_spec - assert "Recommendation review" in e2e_spec - - -def test_operations_workbench_ia_covers_required_pages_and_states() -> None: - content = _content() - - required_terms = [ - "Overview", - "Detections", - "Detection Detail", - "Evidence Timeline", - "Recommendations", - "Recommendation Review", - "RCA/CAPA Draft", - "Demo Runbook/Help", - "Purpose", - "Primary Action", - "Secondary Actions", - "Required API/Data", - "Empty State", - "Loading State", - "Error State", - "Demo Copy", - "Accessibility Considerations", - ] - - for term in required_terms: - assert term in content - - -def test_operations_workbench_ia_records_embedded_evidence_and_docs_only_help() -> None: - content = _content() - - required_terms = [ - "Evidence Timeline stays embedded in Detection Detail", - "should not become a separate route yet", - "Demo Runbook/Help remains documentation-only", - "An in-app help panel would add navigation", - "The current Workbench API client is sufficient for this IA", - "create a follow-up issue", - ] - - for term in required_terms: - assert term in content - - -def test_operations_workbench_ia_rejects_non_demo_critical_scope() -> None: - content = _content() - - rejected_scope = [ - "Production enterprise IA", - "Multi-site administration", - "Auth/RBAC", - "Connector or integration configuration", - "Global search", - "Mobile app IA", - "Model gateway", - "RAG", - "AI governance screens", - "Production validation", - "Direct industrial writeback", - "CAPA creation", - ] - - for term in rejected_scope: - assert term in content diff --git a/services/simulator/tests/test_pre_demo_checklist_docs.py b/services/simulator/tests/test_pre_demo_checklist_docs.py deleted file mode 100644 index 27c89be..0000000 --- a/services/simulator/tests/test_pre_demo_checklist_docs.py +++ /dev/null @@ -1,62 +0,0 @@ -from pathlib import Path - -REPO_ROOT = Path(__file__).resolve().parents[3] -CHECKLIST = REPO_ROOT / "docs" / "demo" / "PRE_DEMO_CHECKLIST.md" -MANUFACTURER_RUNBOOK = REPO_ROOT / "docs" / "demo" / "MANUFACTURER_DEMO_RUNBOOK.md" - - -def _content() -> str: - return CHECKLIST.read_text(encoding="utf-8") - - -def test_pre_demo_checklist_exists_and_is_linked_from_runbook() -> None: - assert CHECKLIST.exists() - assert "docs/demo/PRE_DEMO_CHECKLIST.md" in MANUFACTURER_RUNBOOK.read_text( - encoding="utf-8" - ) - - -def test_pre_demo_checklist_covers_required_readiness_items() -> None: - content = _content() - - required_items = [ - "git status --short", - "make setup", - "cd apps/web && npm install", - "make demo", - "demo api smoke passed", - "make api", - "EVENTS_STORE=.local/storage/fill_weight_drift_demo_events.jsonl", - "SENTINEL_STATE_DIR=.local/storage/fill_weight_drift_demo_sentinel", - "npm run dev", - "det_fill_weight_gradual_drift", - "rec_fill_weight_gradual_drift", - "Recommendation action was tested with a reviewer and reason.", - "Decision feedback shows who reviewed it, the decision, and the reason.", - "RCA/CAPA draft preview is visible", - "docs/demo/MANUFACTURER_DEMO_RUNBOOK.md", - "The demo script has been reviewed.", - ] - - for item in required_items: - assert item in content - - -def test_pre_demo_checklist_covers_demo_scope_and_human_review_caveats() -> None: - content = _content() - - required_caveats = [ - "simulator-backed demo data", - "advisory, human-reviewed decision support", - "does not connect to real plant systems", - "change equipment parameters", - "release product", - "create CAPAs", - "write to QMS/MES", - "site approval workflows", - "not a production readiness checklist", - "not a security review checklist", - ] - - for caveat in required_caveats: - assert caveat in content diff --git a/services/simulator/tests/test_readme_demo_workflow_docs.py b/services/simulator/tests/test_readme_demo_workflow_docs.py deleted file mode 100644 index 8fffeb8..0000000 --- a/services/simulator/tests/test_readme_demo_workflow_docs.py +++ /dev/null @@ -1,82 +0,0 @@ -from pathlib import Path - -REPO_ROOT = Path(__file__).resolve().parents[3] -README = REPO_ROOT / "README.md" - - -def _content() -> str: - return README.read_text(encoding="utf-8") - - -def test_readme_has_run_the_demo_section_with_commands_and_links() -> None: - content = _content() - - required_terms = [ - "## Run the demo", - "make demo", - "make demo-reset", - "make demo-data", - "make demo-ingest", - "make demo-sentinel-run", - "make demo-api-smoke", - ( - "make api EVENTS_STORE=.local/storage/fill_weight_drift_demo_events.jsonl " - "SENTINEL_STATE_DIR=.local/storage/fill_weight_drift_demo_sentinel" - ), - "cd apps/web", - "npm run dev", - "http://127.0.0.1:3000", - "docs/DEMO_RUNBOOK.md", - "docs/demo/MANUFACTURER_DEMO_RUNBOOK.md", - "docs/demo/PRE_DEMO_CHECKLIST.md", - "docs/demo/TROUBLESHOOTING.md", - ] - - for term in required_terms: - assert term in content - - -def test_readme_demo_section_explains_expected_outputs_and_proof_points() -> None: - content = _content() - - required_terms = [ - "accepted_events: 70", - "dead_letter_count: 0", - "sentinel complete: detections=1 evidence=2 recommendations=1", - "demo api smoke passed", - "det_fill_weight_gradual_drift", - "rec_fill_weight_gradual_drift", - "deterministic synthetic factory scenario is generated and ingested", - "one clear fill-weight drift detection", - "readable evidence timeline", - "governed recommendation", - "human approve, reject, or defer", - "RCA/CAPA draft preview", - ] - - for term in required_terms: - assert term in content - - -def test_readme_demo_section_preserves_demo_boundaries_and_post_demo_scope() -> None: - content = _content() - - required_terms = [ - "simulator-backed", - "does not connect to a real plant", - "Recommendations are advisory and human-reviewed", - "does not perform autonomous control", - "QMS/MES writeback", - "compliance validation", - "Post-demo work is still separate", - "does not prove production readiness", - "validated GxP use", - "authentication/RBAC", - "enterprise audit controls", - "real plant integration", - "cloud deployment", - "closed-loop industrial writeback", - ] - - for term in required_terms: - assert term in content diff --git a/services/simulator/tests/test_runtime_docs_transition.py b/services/simulator/tests/test_runtime_docs_transition.py new file mode 100644 index 0000000..ccc3e7f --- /dev/null +++ b/services/simulator/tests/test_runtime_docs_transition.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[3] + +CURRENT_RUNTIME_DOCS = [ + REPO_ROOT / "README.md", + REPO_ROOT / "FILE_INDEX.md", + REPO_ROOT / "apps" / "web" / "README.md", + REPO_ROOT / "services" / "api" / "README.md", + REPO_ROOT / "services" / "ingestion" / "README.md", + REPO_ROOT / "services" / "process-sentinel" / "README.md", + REPO_ROOT / "services" / "simulator" / "README.md", + REPO_ROOT / "docs" / "ARCHITECTURE.md", + REPO_ROOT / "docs" / "CONNECTOR_SAFETY_MODEL.md", + REPO_ROOT / "docs" / "DEMO_RUNBOOK.md", + REPO_ROOT / "docs" / "DEVELOPMENT.md", + REPO_ROOT / "docs" / "MVP_SCOPE.md", + REPO_ROOT / "docs" / "SECURITY_MODEL.md", + REPO_ROOT / "docs" / "START_HERE_FOR_CODEX.md", + REPO_ROOT / "prompts" / "03-synthetic-factory-simulator.md", + REPO_ROOT / "docs" / "demo" / "DEMO_SAFE_COPY_GUIDELINES.md", + REPO_ROOT / "docs" / "demo" / "MANUFACTURER_DEMO_RUNBOOK.md", + REPO_ROOT / "docs" / "demo" / "MANUFACTURER_DEMO_USER_JOURNEY.md", + REPO_ROOT / "docs" / "demo" / "OPC_UA_DEMO_NAMESPACE.md", + REPO_ROOT / "docs" / "demo" / "OPERATIONS_WORKBENCH_DEMO_RUNBOOK.md", + REPO_ROOT / "docs" / "demo" / "OPERATIONS_WORKBENCH_INFORMATION_ARCHITECTURE.md", + REPO_ROOT / "docs" / "demo" / "PRE_DEMO_CHECKLIST.md", + REPO_ROOT / "docs" / "demo" / "TROUBLESHOOTING.md", +] + + +def _read(path: Path) -> str: + return path.read_text(encoding="utf-8") + + +def _combined() -> str: + return "\n".join(_read(path) for path in CURRENT_RUNTIME_DOCS) + + +def test_current_docs_describe_docker_demo_factory_runtime() -> None: + content = _combined() + + required_terms = [ + "Demo-Factory protocols", + "read-only connector worker", + "FactoryEvent", + "Process Sentinel", + "FastAPI API", + "Next.js Workbench", + "docker compose -f infra/docker/docker-compose.yml up --build", + ] + + for term in required_terms: + assert term in content + + +def test_current_docs_do_not_present_in_repo_generation_as_default_path() -> None: + forbidden_terms = [ + "Run the simulator-backed Process Sentinel flow", + "simulator-backed demo data", + "make demo-data", + "make demo-ingest", + "make demo-sentinel-run", + "make simulate SCENARIO=gradual_drift", + "Synthetic Factory Simulator\n", + "simulator-first", + ] + + for path in CURRENT_RUNTIME_DOCS: + content = _read(path) + for term in forbidden_terms: + assert term not in content, f"{term!r} should not appear in {path}" + + +def test_runtime_docs_preserve_read_only_safety_boundary() -> None: + content = _combined() + + required_terms = [ + "read-only", + "industrial writeback", + "arbitrary tag writes", + "product disposition", + "QMS/MES writeback", + "production readiness", + ] + + for term in required_terms: + assert term in content + + +def test_learning_log_documents_runtime_direction_without_rewriting_history() -> None: + learning_log = _read(REPO_ROOT / "docs" / "LEARNING_LOG.md") + + assert "## 2026-05-26 - Runtime documentation direction cleanup" in learning_log + assert "external Demo-Factory protocol data" in learning_log + assert "Historical entries were preserved" in learning_log