Two files, one repository
bbartling/open-fdd ships 2 formats across 6 indexed files. The question worth asking is whether the second one says anything the first does not.
CompareAGENTS.md ↔ Cursor rules
| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 0 | 8 | 6 | 0% |
| Commands | 1 | 2 | 0 | 33% |
| Section tags | 3 | 1 | 3 | 43% |
What each file covers
Sections
0 shared · 8 only in A · 6 only in B- − Agent Guide (container stack + external agents)
- − Start session
- − or: react (no fieldbus) / csv
- − Safe scripts
- − External agent workflow
- − Product UI / FDD agent notes
- − Low-RAM hosts (bensbench)
- − Never
- + Open-FDD agent workspace — orientation
- + Product vs libraries
- + AI agent quick rules (read first)
- + Authority order
- + Bootstrap reading order
- + Skills
Commands
1 shared · 2 only in A · 0 only in B- − docker compose down -v
- − docker volume prune
- docker build
Section tags
3 shared · 1 only in A · 3 only in B- − ui
- + security
- + dependencies
- + monorepo
- code-style
- do-not
- agent-behaviour
Line diff
bbartling/open-fdd · AGENTS.md
@@ −1 @@
1# Agent Guide (container stack + external agents)
2
3Open-FDD ships as a **container stack**: `openfdd-central`, `openfdd-web` (React),
4`openfdd-fieldbus`, `openfdd-mqtt`, plus optional `openfdd-mcp`. It does **not**
5ship an embedded AI chatbot. External orchestrators — Codex CLI, Cursor,
6OpenClaw, Claude Desktop, or any MCP host — connect via **JWT REST** and optional
7**`openfdd-mcp` stdio**.
8
9| Layer | Responsibility |
10| --- | --- |
11| **central** | MQTTS ingest, Feather/Parquet historian, DataFusion SQL FDD + `/api/analytics/*`, REST + JWT (Rust image — no Python) |
12| **web** | React product UI (`frontend/web`) — sole product UI ([ADR-001](docs/architecture/adr-001-react-rust-modernization.md)) |
13| **fieldbus** | BACnet / Modbus / Haystack OT drivers |
14| **mqtt** | Mosquitto MQTTS broker |
15| **mcp** | Optional read-first stdio tools → central (`OPENFDD_API_BASE`) |
16
17**Docs:** [Build recipes](docs/operations/build-recipes.md) · [External agents](docs/examples/external-agents.md) · [MCP README](mcp/README.md) · [ECM engineering (PyPI)](docs/ecm/README.md)
18
19**Software-engineering agent OS:** [`openfdd_agent_spec/`](openfdd_agent_spec/) — architecture locks, skills, Milestone A.
20
21**Active recovery / Vibe 21 twin program:** [`tools/open-fdd-vibe21-production/`](tools/open-fdd-vibe21-production/README.md) · capability ledger [`docs/migration/react-rust/capabilities.yaml`](docs/migration/react-rust/capabilities.yaml).
22
23**PyPI (`open-fdd`):** ECM engineering + pandas oracle (`open_fdd.rules` / `analytics` / `reporting`) for **third-party tooling** outside the product app. Product FDD is DataFusion on GHCR.
24
25Dual expression cookbooks (permanent): `docs/rules/cookbook/` (SQL + pandas).
26
27## Start session
28
29Unmerged UI is **not** on GHCR. Resolve newest published images by OCI
30`created` (`./scripts/ghcr_newest_by_created.py`), and never paste a Caddy
31URL until `./scripts/openfdd_demo_gate.sh` exits 0. See
32[`CONTAINER_AGENT.md`](openfdd_agent_spec/CONTAINER_AGENT.md).
33
34```bash
35./scripts/openfdd_stack_up.sh react-ot # React SPA + mqtt + central + fieldbus
36# or: react (no fieldbus) / csv
37TOKEN="$(curl -s -X POST http://127.0.0.1:8080/api/auth/login \
38 -H 'Content-Type: application/json' \
39 -d '{"username":"admin","password":"'"$OPENFDD_ADMIN_PASSWORD"'"}' \
40 | jq -r '.token // .access_token')"
41```
42
43Discover routes: `curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8080/api/agent/tools | jq '.tools | length'`
44
45## Safe scripts
46
47```bash
48./scripts/openfdd_stack_pull.sh react-ot
49./scripts/openfdd_stack_up.sh react-ot
50./scripts/nightly-ot-bench/run_all.sh # pull sha-* + OT/API gates
51./scripts/openfdd_stack_up.sh csv
52```
53
54## External agent workflow
55
561. Stack healthy on LAN/VPN only — never expose on public internet.
572. JWT for admin/operator/viewer (see central auth env).
583. `openfdd-mcp` stdio outside the web UI, or REST `/api/agent/tools`.
594. Read-first; writes need `OPENFDD_MCP_ALLOW_WRITES=1` and `confirm:true`.
605. Never print secrets. BACnet writes need explicit human approval.
61
62## Product UI / FDD agent notes
63
64- **Overview plots:** plot Expanders default **open** so charts are not hidden behind carets (`OverviewPopulated`).
65- **Lab → FDD Plots:** `session_config` `confirm_min` (and rule params) apply to the series overlay (`sql_detail_session`). After **Update this rule**, Reports/FDD Plots must refetch on `RULES_UPDATED`.
66- **SCHED-1 occupancy:** treat numeric `0` / `0.0` / `false` **and** string `unoccupied` (and related tokens) as unoccupied — SQL + pandas cookbook stay aligned.
67- **Synthetic-59:** soak via `scripts/synthetic_59_*.py` under `reports/wattlab-parity/fixtures/synthetic_59/`. Do not greenwash `expected_faults.csv`. B100 dump-parity remains **paused**.
68- **Units:** FDD SQL is °F canonical. Metric CSVs convert at query (`unit_system=metric|si`). Lab sliders show °C when metric is selected; Run all rules after switching.
69- **Hourly append:** seed with `POST /api/csv/import/package`, then `POST /api/csv/import/package/append` (JWT, `confirm:true`). Custom appenders stay outside the repo.
70- **Overview layout:** full width beside sidebar (Streamlit-like); named Plotly PNG stems via `downloadFilename`; Lab rule menu A–Z; FDD series = required∪optional roles.
71- **Mech OAT bins:** status/cmd before amps; prefer web/weather OAT. Analytics envelopes: `scripts/synthetic_59_overview_analytics_soak.py`.
72
73## Low-RAM hosts (bensbench)
74
75- **Never** local `docker build` / heavy Rust compile for stack images. Ship via PR → GH Actions → GHCR `nightly` / `sha-*`.
76- Before pulling new images: prune unused/old digests first, then `./scripts/openfdd_stack_pull.sh …` and `./scripts/openfdd_stack_up.sh … --no-pull`.
77- Details: [`openfdd_agent_spec/CONTAINER_AGENT.md`](openfdd_agent_spec/CONTAINER_AGENT.md).
78
79## Never
80
81- delete `workspace/`
82- run `docker compose down -v`
83- run `docker volume prune`
84- print secrets or tokens
85- expose API on public internet
86- write BACnet without explicit human approval
87- embed vendor chat relays or model API keys in the stack
88- add Python to the product central/web request path
89- local stack image builds on low-RAM hosts (use GHCR)
90
91See [docs/agent/index.md](docs/agent/index.md) for external-agent architecture.
92
93For library/migration/PR missions (Milestone A), start at [openfdd_agent_spec/AGENTS.md](openfdd_agent_spec/AGENTS.md).
94
bbartling/open-fdd · openfdd_agent_spec/AGENTS.md
@@ +1 @@
1# Open-FDD agent workspace — orientation
2
3Plain Markdown on disk is the source of truth for **Cursor**, **Codex CLI**, and
4similar agents. Product code lives in `services/`, `sql_rules/`, `frontend/`,
5`mcp/`, `edge/`, `os/`. PyPI libraries live in `open_fdd/`. Orchestration lives in
6**`openfdd_agent_spec/`**.
7
8**Primary agent prompt (paste into new sessions):** [`../AGENTS.md`](../AGENTS.md)
9
10**Software-engineering mission:** [`MILESTONE_A.md`](MILESTONE_A.md)
11
12**Ops / edge soak prompts:** [`../docs/agent/`](../docs/agent/) — GHCR bench, nightly
13retest. Do not confuse those with this engineering OS.
14
15---
16
17## Product vs libraries
18
19| Layer | Owns |
20| --- | --- |
21| `services/` + `sql_rules/` + `frontend/web` | **Product:** central DataFusion SQL FDD + analytics, React SPA (`openfdd-web`), fieldbus, mqtt |
22| `open_fdd/` | **PyPI libraries** for third-party tooling: ECM + pandas oracle (`rules` / `analytics` / `reporting`) — not the product runtime |
23| `mcp/` | Optional read-first MCP → central |
24| `edge/`, `os/` | Future concepts — **never delete** |
25| `docs/rules/cookbook/` | Dual expression cookbooks (SQL + pandas) + parity matrix |
26| `tools/open-fdd-vibe21-production/` | Active recovery + Vibe 21 twin program |
27| `docs/migration/react-rust/capabilities.yaml` | Machine-readable capability ledger |
28| `openfdd_agent_spec/` | Agent law, Milestone A, skills, session log |
29| Playground vibe19/20 | External demos; consumers of PyPI |
30
31**Naming (code truth):**
32
33| Concept | Module / extra |
34| --- | --- |
35| Pandas oracle (PyPI) | `open_fdd.rules` (+ `open_fdd.analytics`) |
36| Pip extras | `oracle`, `analytics`, `reporting` (`vibe19` deprecated alias through 4.3) |
37| ECM math | `open_fdd.ecm_engineering` |
38| Shared contracts | `open_fdd.contracts` — Phase 2 target (not shipped) |
39
40---
41
42## AI agent quick rules (read first)
43
441. Product FDD + Overview analytics = **DataFusion SQL** on GHCR. Never silent pandas fallback in central.
452. Pandas oracle stays forever on **PyPI** + cookbooks + vibe19 — never delete the pandas cookbook because production uses SQL.
463. Never delete the SQL cookbook because pandas remains the oracle.
474. **Product UI:** React SPA (`frontend/web` → `openfdd-web`, `compose.react.yml`) only. Overview = central `/api/analytics/*` (DataFusion) + client Plotly. Browser → central Rust `/api` only — **no Python in the product request path**.
485. **Internet-facing auth/UI hygiene:** Never put bench/dev secrets, credential file paths, default passwords, or JWT dumps on login or other product surfaces. Generic login errors only.
496. Test containers on **`OPENFDD_IMAGE_TAG=nightly`**, but **pin/run `sha-*`** per [`CONTAINER_AGENT.md`](CONTAINER_AGENT.md).
507. Playground images: `ghcr.io/bbartling/vibe19:develop`, `vibe20:develop` (external).
518. `edge/` and `os/` are future concepts — never delete.
529. Bounded PRs only — see [`PR_PROTOCOL.md`](PR_PROTOCOL.md).
5310. Prefer exact wheel install tests over editable-only validation for packaging PRs.
5411. Never trust a moving GHCR tag alone — resolve/pull immutable `sha-*`, recreate containers, then record the digest.
5512. Append [`SESSION_LOG.md`](SESSION_LOG.md) after non-trivial work.
5613. Update [`BUILD_CHECKPOINTS.md`](BUILD_CHECKPOINTS.md) when Milestone A or product capability status changes.
5714. CodeRabbit: fix actionable defects; reject suggestions that violate architecture.
5815. vibe21 = separate plan — not Milestone A.
5916. When blocked (secrets, permissions), finish non-blocked work and record the exact error.
6017. Bound each PR to its declared scope.
6118. **Active program:** [`tools/open-fdd-vibe21-production/`](../tools/open-fdd-vibe21-production/README.md) Master Loop. Keep [`capabilities.yaml`](../docs/migration/react-rust/capabilities.yaml) honest.
6219. For `frontend/web` work: follow [`openfdd-react-spa`](skills/openfdd-react-spa/SKILL.md).
6320. Central product image is **debian + Rust binaries only** — no Python. WattLab AFDD zip export is optional offline tooling (`OPENFDD_WATTLAB_PYTHON_EXPORT=1`).
6421. **Site lock:** Overview + sidebar Active site are the only editors of `?site=`. `SectionTabs` (and sidebar App pages) must `navigate` with `hrefWithSession` so `site` + `eq` survive. FDD / RCx / Results / WattLab show a locked `zip:BUILDING_*` caption — no Building `<Select>`.
6522. **FDD Plots = vibe19 `rule_result_chart`:** auto-load series; last y-axis title is `fault`; `confirmed_fault` is the last trace on the bottom domain (`domain[0] < 0.4`). A successful rule run with an empty overlay is a bug (fail the test), not a soft banner.
6623. **RCx catalog freeze:** every `REQUIRED_RCX_PRESET_IDS` id must stay listed. Family picker order is `RCX_FAMILY_ORDER` (Zones first) plus empty Heat pump / Weather placeholders. Auto-run the selected preset when site+preset are set.
6724. **Actions housekeeping:** default `GET /api/actions?limit=10`; `DELETE /api/actions/:id` and `DELETE /api/actions`; JSONL prune cap 50.
6825. **Section radios:** left, horizontal, **after** hero + Equipment on Overview (never inside `.oracle-hero`, never centered in the logo column). Other pages: same left radio row at the top of the page body.
6926. **Overview plot Expanders** default **open** — do not hide motor / mech / econ / BAS plot sections behind carets (`Expander` unmounts children when closed).
7027. **FDD Plots series overlay** honors Lab/`session_config` `confirm_min` (and typed rule params); source may be `sql_detail_session`. After Update-this-rule, listen for `RULES_UPDATED` and refetch results + series.
7128. **SCHED-1 portable occupancy:** numeric/boolean falsey (`0`, `0.0`, `false`) **and** string `unoccupied` (plus related tokens) — keep SQL (`sched1_unoccupied_runtime.sql`) and pandas `sched1` aligned.
7229. **Low-RAM / bensbench:** never local stack `docker build`; prune old images before pull; wait for GHCR publish then pull `sha-*` / `nightly`. Synthetic-59 soaks use `scripts/synthetic_59_*.py`; B100 dump-parity stays paused; never edit goldens to hide misses.
7330. **Plot PNG downloads:** `PlotlyHost` must pass `toImageButtonOptions.filename` (Overview/Reports). Default Plotly `newplot.png` is a regression.
7431. **Full-width UI:** `.app-content` / `.overview-populated` stretch (`max-width: none`) like Streamlit — do not reintroduce a rem content cap on Overview plots.
7532. **Rule Lab menu:** `RuleTuningPanel` sorts visible rules A–Z by `rule_id` (registry YAML order is engine priority only).
7633. **FDD Plots series roles:** `series_response` SELECT = `required_roles ∪ optional_roles` (SV-*/PID-HUNT keep required empty). Soft-empty when none present on equipment.
7734. **Mech cooling OAT bins:** status/cmd proof **before** amps (never OR amps when status exists); prefer web/`dry_bulb_f`/weather OAT over site-averaged AHU BAS `oa_t`. Version `mechanical-cooling-oat-bins-v2`.
7835. **Synthetic analytics soak:** `scripts/synthetic_59_overview_analytics_soak.py` asserts runtime + mech bin envelopes (separate from FDD pair scores).
7936. **Metric CSVs:** store as-uploaded; convert temperature roles C→F at run-rules/historian query. Do not duplicate 59 SQL files. Sliders display user units.
8037. **Package append:** `POST /api/csv/import/package/append` is the IoT hourly path (JWT + confirm). Vendor pullers are out-of-repo.
81
82---
83
84## Authority order
85
861. Root [`AGENTS.md`](../AGENTS.md)
872. Machine manifests: [`ownership.yaml`](ownership.yaml), [`capabilities.yaml`](../docs/migration/react-rust/capabilities.yaml)
883. Current phase docs under [`tools/open-fdd-vibe21-production/`](../tools/open-fdd-vibe21-production/)
894. Generated OpenAPI / MCP / rule catalogs
905. Workflow guides and examples
91
92Nested instructions may specialize but never contradict a higher authority.
93
94## Bootstrap reading order
95
961. [`../AGENTS.md`](../AGENTS.md)
972. This file
983. [`ARCHITECTURE.md`](ARCHITECTURE.md) + [`ownership.yaml`](ownership.yaml)
994. [`BUILD_CHECKPOINTS.md`](BUILD_CHECKPOINTS.md)
1005. [`tools/open-fdd-vibe21-production/prompts/MASTER_PRODUCTION_LOOP.md`](../tools/open-fdd-vibe21-production/prompts/MASTER_PRODUCTION_LOOP.md)
1016. [`MILESTONE_A.md`](MILESTONE_A.md) if executing Milestone A
1027. [`PR_PROTOCOL.md`](PR_PROTOCOL.md) before opening a PR
1038. Matching skill
1049. Cookbooks under `docs/rules/cookbook/`
105
106---
107
108## Skills
109
110| Skill | Use when |
111| --- | --- |
112| [`openfdd-architecture`](skills/openfdd-architecture/SKILL.md) | Ownership / engine boundaries |
113| [`openfdd-react-spa`](skills/openfdd-react-spa/SKILL.md) | Product SPA (`frontend/web`) |
114| [`openfdd-sql-fdd`](skills/openfdd-sql-fdd/SKILL.md) | DataFusion SQL rules |
115| [`openfdd-pypi-oracle`](skills/openfdd-pypi-oracle/SKILL.md) | PyPI pandas oracle packaging |
116| [`openfdd-cookbook-parity`](skills/openfdd-cookbook-parity/SKILL.md) | Dual cookbook honesty |
117| [`openfdd-stack-ghcr`](skills/openfdd-stack-ghcr/SKILL.md) | GHCR pull / recreate |
118| [`openfdd-ecm-engineering`](skills/openfdd-ecm-engineering/SKILL.md) | ECM math library |
119| [`openfdd-milestone-a-pr`](skills/openfdd-milestone-a-pr/SKILL.md) | Milestone A PR loop |
120
@@ −1 +1 @@
1−# Agent Guide (container stack + external agents)
1+# Open-FDD agent workspace — orientation
22
3−Open-FDD ships as a **container stack**: `openfdd-central`, `openfdd-web` (React),
4−`openfdd-fieldbus`, `openfdd-mqtt`, plus optional `openfdd-mcp`. It does **not**
5−ship an embedded AI chatbot. External orchestrators — Codex CLI, Cursor,
6−OpenClaw, Claude Desktop, or any MCP host — connect via **JWT REST** and optional
7−**`openfdd-mcp` stdio**.
3+Plain Markdown on disk is the source of truth for **Cursor**, **Codex CLI**, and
4+similar agents. Product code lives in `services/`, `sql_rules/`, `frontend/`,
5+`mcp/`, `edge/`, `os/`. PyPI libraries live in `open_fdd/`. Orchestration lives in
6+**`openfdd_agent_spec/`**.
87
9−| Layer | Responsibility |
10−| --- | --- |
11−| **central** | MQTTS ingest, Feather/Parquet historian, DataFusion SQL FDD + `/api/analytics/*`, REST + JWT (Rust image — no Python) |
12−| **web** | React product UI (`frontend/web`) — sole product UI ([ADR-001](docs/architecture/adr-001-react-rust-modernization.md)) |
13−| **fieldbus** | BACnet / Modbus / Haystack OT drivers |
14−| **mqtt** | Mosquitto MQTTS broker |
15−| **mcp** | Optional read-first stdio tools → central (`OPENFDD_API_BASE`) |
8+**Primary agent prompt (paste into new sessions):** [`../AGENTS.md`](../AGENTS.md)
169
17−**Docs:** [Build recipes](docs/operations/build-recipes.md) · [External agents](docs/examples/external-agents.md) · [MCP README](mcp/README.md) · [ECM engineering (PyPI)](docs/ecm/README.md)
10+**Software-engineering mission:** [`MILESTONE_A.md`](MILESTONE_A.md)
1811
19−**Software-engineering agent OS:** [`openfdd_agent_spec/`](openfdd_agent_spec/) — architecture locks, skills, Milestone A.
12+**Ops / edge soak prompts:** [`../docs/agent/`](../docs/agent/) — GHCR bench, nightly
13+retest. Do not confuse those with this engineering OS.
2014
21−**Active recovery / Vibe 21 twin program:** [`tools/open-fdd-vibe21-production/`](tools/open-fdd-vibe21-production/README.md) · capability ledger [`docs/migration/react-rust/capabilities.yaml`](docs/migration/react-rust/capabilities.yaml).
15+---
2216
23−**PyPI (`open-fdd`):** ECM engineering + pandas oracle (`open_fdd.rules` / `analytics` / `reporting`) for **third-party tooling** outside the product app. Product FDD is DataFusion on GHCR.
17+## Product vs libraries
2418
25−Dual expression cookbooks (permanent): `docs/rules/cookbook/` (SQL + pandas).
19+| Layer | Owns |
20+| --- | --- |
21+| `services/` + `sql_rules/` + `frontend/web` | **Product:** central DataFusion SQL FDD + analytics, React SPA (`openfdd-web`), fieldbus, mqtt |
22+| `open_fdd/` | **PyPI libraries** for third-party tooling: ECM + pandas oracle (`rules` / `analytics` / `reporting`) — not the product runtime |
23+| `mcp/` | Optional read-first MCP → central |
24+| `edge/`, `os/` | Future concepts — **never delete** |
25+| `docs/rules/cookbook/` | Dual expression cookbooks (SQL + pandas) + parity matrix |
26+| `tools/open-fdd-vibe21-production/` | Active recovery + Vibe 21 twin program |
27+| `docs/migration/react-rust/capabilities.yaml` | Machine-readable capability ledger |
28+| `openfdd_agent_spec/` | Agent law, Milestone A, skills, session log |
29+| Playground vibe19/20 | External demos; consumers of PyPI |
2630
27−## Start session
31+**Naming (code truth):**
2832
29−Unmerged UI is **not** on GHCR. Resolve newest published images by OCI
30−`created` (`./scripts/ghcr_newest_by_created.py`), and never paste a Caddy
31−URL until `./scripts/openfdd_demo_gate.sh` exits 0. See
32−[`CONTAINER_AGENT.md`](openfdd_agent_spec/CONTAINER_AGENT.md).
33+| Concept | Module / extra |
34+| --- | --- |
35+| Pandas oracle (PyPI) | `open_fdd.rules` (+ `open_fdd.analytics`) |
36+| Pip extras | `oracle`, `analytics`, `reporting` (`vibe19` deprecated alias through 4.3) |
37+| ECM math | `open_fdd.ecm_engineering` |
38+| Shared contracts | `open_fdd.contracts` — Phase 2 target (not shipped) |
3339
34−```bash
35−./scripts/openfdd_stack_up.sh react-ot # React SPA + mqtt + central + fieldbus
36−# or: react (no fieldbus) / csv
37−TOKEN="$(curl -s -X POST http://127.0.0.1:8080/api/auth/login \
38− -H 'Content-Type: application/json' \
39− -d '{"username":"admin","password":"'"$OPENFDD_ADMIN_PASSWORD"'"}' \
40− | jq -r '.token // .access_token')"
41−```
40+---
4241
43−Discover routes: `curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8080/api/agent/tools | jq '.tools | length'`
42+## AI agent quick rules (read first)
4443
45−## Safe scripts
44+1. Product FDD + Overview analytics = **DataFusion SQL** on GHCR. Never silent pandas fallback in central.
45+2. Pandas oracle stays forever on **PyPI** + cookbooks + vibe19 — never delete the pandas cookbook because production uses SQL.
46+3. Never delete the SQL cookbook because pandas remains the oracle.
47+4. **Product UI:** React SPA (`frontend/web` → `openfdd-web`, `compose.react.yml`) only. Overview = central `/api/analytics/*` (DataFusion) + client Plotly. Browser → central Rust `/api` only — **no Python in the product request path**.
48+5. **Internet-facing auth/UI hygiene:** Never put bench/dev secrets, credential file paths, default passwords, or JWT dumps on login or other product surfaces. Generic login errors only.
49+6. Test containers on **`OPENFDD_IMAGE_TAG=nightly`**, but **pin/run `sha-*`** per [`CONTAINER_AGENT.md`](CONTAINER_AGENT.md).
50+7. Playground images: `ghcr.io/bbartling/vibe19:develop`, `vibe20:develop` (external).
51+8. `edge/` and `os/` are future concepts — never delete.
52+9. Bounded PRs only — see [`PR_PROTOCOL.md`](PR_PROTOCOL.md).
53+10. Prefer exact wheel install tests over editable-only validation for packaging PRs.
54+11. Never trust a moving GHCR tag alone — resolve/pull immutable `sha-*`, recreate containers, then record the digest.
55+12. Append [`SESSION_LOG.md`](SESSION_LOG.md) after non-trivial work.
56+13. Update [`BUILD_CHECKPOINTS.md`](BUILD_CHECKPOINTS.md) when Milestone A or product capability status changes.
57+14. CodeRabbit: fix actionable defects; reject suggestions that violate architecture.
58+15. vibe21 = separate plan — not Milestone A.
59+16. When blocked (secrets, permissions), finish non-blocked work and record the exact error.
60+17. Bound each PR to its declared scope.
61+18. **Active program:** [`tools/open-fdd-vibe21-production/`](../tools/open-fdd-vibe21-production/README.md) Master Loop. Keep [`capabilities.yaml`](../docs/migration/react-rust/capabilities.yaml) honest.
62+19. For `frontend/web` work: follow [`openfdd-react-spa`](skills/openfdd-react-spa/SKILL.md).
63+20. Central product image is **debian + Rust binaries only** — no Python. WattLab AFDD zip export is optional offline tooling (`OPENFDD_WATTLAB_PYTHON_EXPORT=1`).
64+21. **Site lock:** Overview + sidebar Active site are the only editors of `?site=`. `SectionTabs` (and sidebar App pages) must `navigate` with `hrefWithSession` so `site` + `eq` survive. FDD / RCx / Results / WattLab show a locked `zip:BUILDING_*` caption — no Building `<Select>`.
65+22. **FDD Plots = vibe19 `rule_result_chart`:** auto-load series; last y-axis title is `fault`; `confirmed_fault` is the last trace on the bottom domain (`domain[0] < 0.4`). A successful rule run with an empty overlay is a bug (fail the test), not a soft banner.
66+23. **RCx catalog freeze:** every `REQUIRED_RCX_PRESET_IDS` id must stay listed. Family picker order is `RCX_FAMILY_ORDER` (Zones first) plus empty Heat pump / Weather placeholders. Auto-run the selected preset when site+preset are set.
67+24. **Actions housekeeping:** default `GET /api/actions?limit=10`; `DELETE /api/actions/:id` and `DELETE /api/actions`; JSONL prune cap 50.
68+25. **Section radios:** left, horizontal, **after** hero + Equipment on Overview (never inside `.oracle-hero`, never centered in the logo column). Other pages: same left radio row at the top of the page body.
69+26. **Overview plot Expanders** default **open** — do not hide motor / mech / econ / BAS plot sections behind carets (`Expander` unmounts children when closed).
70+27. **FDD Plots series overlay** honors Lab/`session_config` `confirm_min` (and typed rule params); source may be `sql_detail_session`. After Update-this-rule, listen for `RULES_UPDATED` and refetch results + series.
71+28. **SCHED-1 portable occupancy:** numeric/boolean falsey (`0`, `0.0`, `false`) **and** string `unoccupied` (plus related tokens) — keep SQL (`sched1_unoccupied_runtime.sql`) and pandas `sched1` aligned.
72+29. **Low-RAM / bensbench:** never local stack `docker build`; prune old images before pull; wait for GHCR publish then pull `sha-*` / `nightly`. Synthetic-59 soaks use `scripts/synthetic_59_*.py`; B100 dump-parity stays paused; never edit goldens to hide misses.
73+30. **Plot PNG downloads:** `PlotlyHost` must pass `toImageButtonOptions.filename` (Overview/Reports). Default Plotly `newplot.png` is a regression.
74+31. **Full-width UI:** `.app-content` / `.overview-populated` stretch (`max-width: none`) like Streamlit — do not reintroduce a rem content cap on Overview plots.
75+32. **Rule Lab menu:** `RuleTuningPanel` sorts visible rules A–Z by `rule_id` (registry YAML order is engine priority only).
76+33. **FDD Plots series roles:** `series_response` SELECT = `required_roles ∪ optional_roles` (SV-*/PID-HUNT keep required empty). Soft-empty when none present on equipment.
77+34. **Mech cooling OAT bins:** status/cmd proof **before** amps (never OR amps when status exists); prefer web/`dry_bulb_f`/weather OAT over site-averaged AHU BAS `oa_t`. Version `mechanical-cooling-oat-bins-v2`.
78+35. **Synthetic analytics soak:** `scripts/synthetic_59_overview_analytics_soak.py` asserts runtime + mech bin envelopes (separate from FDD pair scores).
79+36. **Metric CSVs:** store as-uploaded; convert temperature roles C→F at run-rules/historian query. Do not duplicate 59 SQL files. Sliders display user units.
80+37. **Package append:** `POST /api/csv/import/package/append` is the IoT hourly path (JWT + confirm). Vendor pullers are out-of-repo.
4681
47−```bash
48−./scripts/openfdd_stack_pull.sh react-ot
49−./scripts/openfdd_stack_up.sh react-ot
50−./scripts/nightly-ot-bench/run_all.sh # pull sha-* + OT/API gates
51−./scripts/openfdd_stack_up.sh csv
52−```
82+---
5383
54−## External agent workflow
84+## Authority order
5585
56−1. Stack healthy on LAN/VPN only — never expose on public internet.
57−2. JWT for admin/operator/viewer (see central auth env).
58−3. `openfdd-mcp` stdio outside the web UI, or REST `/api/agent/tools`.
59−4. Read-first; writes need `OPENFDD_MCP_ALLOW_WRITES=1` and `confirm:true`.
60−5. Never print secrets. BACnet writes need explicit human approval.
86+1. Root [`AGENTS.md`](../AGENTS.md)
87+2. Machine manifests: [`ownership.yaml`](ownership.yaml), [`capabilities.yaml`](../docs/migration/react-rust/capabilities.yaml)
88+3. Current phase docs under [`tools/open-fdd-vibe21-production/`](../tools/open-fdd-vibe21-production/)
89+4. Generated OpenAPI / MCP / rule catalogs
90+5. Workflow guides and examples
6191
62−## Product UI / FDD agent notes
92+Nested instructions may specialize but never contradict a higher authority.
6393
64−- **Overview plots:** plot Expanders default **open** so charts are not hidden behind carets (`OverviewPopulated`).
65−- **Lab → FDD Plots:** `session_config` `confirm_min` (and rule params) apply to the series overlay (`sql_detail_session`). After **Update this rule**, Reports/FDD Plots must refetch on `RULES_UPDATED`.
66−- **SCHED-1 occupancy:** treat numeric `0` / `0.0` / `false` **and** string `unoccupied` (and related tokens) as unoccupied — SQL + pandas cookbook stay aligned.
67−- **Synthetic-59:** soak via `scripts/synthetic_59_*.py` under `reports/wattlab-parity/fixtures/synthetic_59/`. Do not greenwash `expected_faults.csv`. B100 dump-parity remains **paused**.
68−- **Units:** FDD SQL is °F canonical. Metric CSVs convert at query (`unit_system=metric|si`). Lab sliders show °C when metric is selected; Run all rules after switching.
69−- **Hourly append:** seed with `POST /api/csv/import/package`, then `POST /api/csv/import/package/append` (JWT, `confirm:true`). Custom appenders stay outside the repo.
70−- **Overview layout:** full width beside sidebar (Streamlit-like); named Plotly PNG stems via `downloadFilename`; Lab rule menu A–Z; FDD series = required∪optional roles.
71−- **Mech OAT bins:** status/cmd before amps; prefer web/weather OAT. Analytics envelopes: `scripts/synthetic_59_overview_analytics_soak.py`.
94+## Bootstrap reading order
7295
73−## Low-RAM hosts (bensbench)
96+1. [`../AGENTS.md`](../AGENTS.md)
97+2. This file
98+3. [`ARCHITECTURE.md`](ARCHITECTURE.md) + [`ownership.yaml`](ownership.yaml)
99+4. [`BUILD_CHECKPOINTS.md`](BUILD_CHECKPOINTS.md)
100+5. [`tools/open-fdd-vibe21-production/prompts/MASTER_PRODUCTION_LOOP.md`](../tools/open-fdd-vibe21-production/prompts/MASTER_PRODUCTION_LOOP.md)
101+6. [`MILESTONE_A.md`](MILESTONE_A.md) if executing Milestone A
102+7. [`PR_PROTOCOL.md`](PR_PROTOCOL.md) before opening a PR
103+8. Matching skill
104+9. Cookbooks under `docs/rules/cookbook/`
74105
75−- **Never** local `docker build` / heavy Rust compile for stack images. Ship via PR → GH Actions → GHCR `nightly` / `sha-*`.
76−- Before pulling new images: prune unused/old digests first, then `./scripts/openfdd_stack_pull.sh …` and `./scripts/openfdd_stack_up.sh … --no-pull`.
77−- Details: [`openfdd_agent_spec/CONTAINER_AGENT.md`](openfdd_agent_spec/CONTAINER_AGENT.md).
106+---
78107
79−## Never
108+## Skills
80109
81−- delete `workspace/`
82−- run `docker compose down -v`
83−- run `docker volume prune`
84−- print secrets or tokens
85−- expose API on public internet
86−- write BACnet without explicit human approval
87−- embed vendor chat relays or model API keys in the stack
88−- add Python to the product central/web request path
89−- local stack image builds on low-RAM hosts (use GHCR)
90−
91−See [docs/agent/index.md](docs/agent/index.md) for external-agent architecture.
92−
93−For library/migration/PR missions (Milestone A), start at [openfdd_agent_spec/AGENTS.md](openfdd_agent_spec/AGENTS.md).
110+| Skill | Use when |
111+| --- | --- |
112+| [`openfdd-architecture`](skills/openfdd-architecture/SKILL.md) | Ownership / engine boundaries |
113+| [`openfdd-react-spa`](skills/openfdd-react-spa/SKILL.md) | Product SPA (`frontend/web`) |
114+| [`openfdd-sql-fdd`](skills/openfdd-sql-fdd/SKILL.md) | DataFusion SQL rules |
115+| [`openfdd-pypi-oracle`](skills/openfdd-pypi-oracle/SKILL.md) | PyPI pandas oracle packaging |
116+| [`openfdd-cookbook-parity`](skills/openfdd-cookbook-parity/SKILL.md) | Dual cookbook honesty |
117+| [`openfdd-stack-ghcr`](skills/openfdd-stack-ghcr/SKILL.md) | GHCR pull / recreate |
118+| [`openfdd-ecm-engineering`](skills/openfdd-ecm-engineering/SKILL.md) | ECM math library |
119+| [`openfdd-milestone-a-pr`](skills/openfdd-milestone-a-pr/SKILL.md) | Milestone A PR loop |
94120
