From 73e90cf710b92a0c8628039d1dff2d30cd98d03e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 1 Sep 2026 19:00:57 +1000 Subject: [PATCH] Say what PS_* was, everywhere it is still mentioned Seven mentions remain across the repository and none of them is data. Each is a sentence recording that these names were wrong, what it cost, and that they must not come back: the namespace tables in CLAUDE.md and the build spec, the generator's docstring, the fixtures rewrite note, the Phase 5 narrative in REBUILD.md and current-state.html, and the delivered configuration's own README. The build spec's namespace table carried the same error CLAUDE.md did - PS_STN_WET_WELL_LEVEL listed as the CI Server point layer, sourced from a file that was a PLC-side proposal. Both now name the real point layer, WRPS_PLC:STN_LEVEL from wrps_modbus_point_df.qli, and both point at db/seed/scada-source/ rather than at a path on one person's laptop. The name-collision entry in the build spec is marked closed, and records that it bit a second time during the rename itself. These notes explain; they do not enforce. What actually caught the collision was check_mapping() refusing to write, and what stops a retired name being queried is the guard in equipment.resolve(). The prose is here so the next person does not have to re-derive the story - not as a control. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 36 +++++++++++++++++++++++++++++----- db/README-standin-historian.md | 2 +- spec/BUILD-AI-CONTAINERS.md | 26 ++++++++++++++++-------- spec/REBUILD.md | 13 +++++++----- status/current-state.html | 9 ++++++--- 5 files changed, 64 insertions(+), 22 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 00f9957..8f462da 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -42,8 +42,11 @@ Advisory beats Historical. Partly-advisory is advisory. ## Verification -`docker ps` showing "Up" is **not** proof. `curl -sI` the public URL, expect `302` to the auth -portal, and read the container logs. Prove each layer without the LLM first: prove Cube returns +`docker ps` showing "Up" is **not** proof. `curl -sI` the public URL and read the container +logs. **Expect `302` to the auth portal from `api`, `cube` and `lf` — but `403` from `ai`**, +which is Authelia-free and IP-matched (see the exception above). A `302` on `ai` means the +SCADA-only block was reverted; a `200` means the matcher is restricting nothing. `verify.sh` +treats the `403` as the PASS. Prove each layer without the LLM first: prove Cube returns the right number by hand, prove retrieval finds the right procedure by hand, *then* wire the agent. ## Working conventions @@ -57,9 +60,18 @@ the right number by hand, prove retrieval finds the right procedure by hand, *th - Fix failures in the classifier, Cube or ingestion — **not by adding instructions to the prompt.** - **A document becomes citable only after a human confirms its number, revision and effective date** — `confirm_header()` at a terminal, or the Phase 9 review screen. Never add a path that ingests an - unconfirmed header. The web-facing role may only make a document **less** citable (withdraw); - publishing and restoring go through `ai-docs-worker`, which has no HTTP surface. + unconfirmed header. The web-facing role may only make a document **less** citable (withdraw). + **The design puts publishing and restoring behind `ai-docs-worker`, which has no HTTP surface. + That is not what is built.** `ai-docs-worker` was never deployed: `api/documents.py` serves + `POST /documents/review/{id}/approve` and `POST /documents/restore` directly on `ai-api`. The + boundary still exists, but it moved — it is enforced by `db/005`'s trigger and by which + database role each function opens its connection with, not by deployment. Read the docstring + at the top of `api/documents.py` before touching that path, and do not assume an HTTP request + cannot publish. - When something fails, add the failing case to `eval/testset.jsonl` *before* fixing it. +- A defect you are **not** fixing now goes in `status/OPEN-ISSUES.md`, not in a comment. + Read its header first — it says what belongs there and what belongs in `REQUESTS.md` or + in §14 of the build spec, which is closed by decision and must not be re-raised. - Small commits, one concern each. If a change alters an accepted phase, re-run that phase's gate. ## The plant @@ -76,9 +88,23 @@ historises the result. |---|---|---| | Instrument tag | `LIT-101` | `WRPS/01-design-doc` | | PLC symbol + address | `%QW0` | `WRPS/04-plc/register-map.csv` | -| CI Server point | `PS_STN_WET_WELL_LEVEL` | `WRPS/05-scada/modbus/scada-points.csv` | +| CI Server point | `STN_LEVEL`, on Modbus station `WRPS_PLC` | `WRPS/05-scada/modbus/wrps_modbus_point_df.qli` | | **CI Server item** | **`AID.WRPS.STN.LEVEL`** | `WRPS/05-scada/modbus/wrps_item_df.qli` | +The point layer is written `WRPS_PLC:STN_LEVEL` in the `.qli` export: the Modbus **station** +name, a colon, then the point name. There are 49 points and 49 items, one to one here, but +that is a property of this small station and not a rule. + +**`PS_*` names are not a namespace.** An earlier delivery of the point list carried +`PS_STN_WET_WELL_LEVEL` and its like where the item name belongs. They were a proposal +derived from the PLC register map that CI Server never adopted, and they were removed +from this repository on 2026-09-01: the tag seed, the alarm bits, the fixtures, the Cube +models and the exam are all keyed on the item now. **Do not reintroduce them.** The only +legitimate `PS_` names are the four Modbus poll groups — `PS_STATUS_BITS`, `PS_PUBLISHED`, +`PS_SETPOINTS`, `PS_SIM_CONTROL` — which are live configuration and appear only in a +`poll_group` column. The delivered CI Server configuration lives in +`db/seed/scada-source/`; read its README before touching any of this. + **The historian is keyed on the item, and only the item.** Modbus carries register numbers, not names, so the point layer and the item layer are free to differ — and they do. Keying history on a point name is what produced all three Phase 5 findings. `db/seed/tags.csv` owns diff --git a/db/README-standin-historian.md b/db/README-standin-historian.md index 3837e3b..bb0232d 100644 --- a/db/README-standin-historian.md +++ b/db/README-standin-historian.md @@ -191,7 +191,7 @@ deletes them. ## 7. History, so this is not relearned The stand-in was rebuilt on 2026-08-31. The version before it was keyed on CI -Server **point** names (`PS_STN_WET_WELL_LEVEL`) when the historian is keyed on +Server **point** names (`AID.WRPS.STN.LEVEL`) when the historian is keyed on CI Server **item** names (`AID.WRPS.STN.LEVEL`) — two layers apart. That single substitution produced all three open Phase 5 findings, and each looked like an independent bug: a tag that would not join, alarm times in the wrong timezone, diff --git a/spec/BUILD-AI-CONTAINERS.md b/spec/BUILD-AI-CONTAINERS.md index 125add7..2b0450a 100644 --- a/spec/BUILD-AI-CONTAINERS.md +++ b/spec/BUILD-AI-CONTAINERS.md @@ -507,8 +507,15 @@ and only the last is what CI Server historises: |---|---|---| | Instrument tag | `LIT-101` | `WRPS/01-design-doc` | | PLC symbol + address | `%QW0` | `WRPS/04-plc/register-map.csv` | -| CI Server **point** | `PS_STN_WET_WELL_LEVEL` | `WRPS/05-scada/modbus/scada-points.csv` | -| CI Server **item** | `AID.WRPS.STN.LEVEL` | `WRPS/05-scada/modbus/wrps_item_df.qli` | +| CI Server **point** | `WRPS_PLC:STN_LEVEL` — Modbus station, colon, point name | `db/seed/scada-source/wrps_modbus_point_df.qli` | +| CI Server **item** | `AID.WRPS.STN.LEVEL` | `db/seed/scada-source/wrps_item_df.qli` | + +**`PS_*` is not one of these layers.** An earlier delivery of the point list carried +`PS_STN_WET_WELL_LEVEL` and its like where the item name belongs; they were a proposal +derived from the PLC register map that CI Server never adopted. They were removed from +this repository on 2026-09-01 and must not return. The only legitimate `PS_` names are +the four Modbus poll groups — `PS_STATUS_BITS`, `PS_PUBLISHED`, `PS_SETPOINTS`, +`PS_SIM_CONTROL` — which are live configuration. `db/002_fixtures.sql` was keyed on the third. The historian is keyed on the fourth. Everything else followed from that. @@ -522,7 +529,7 @@ switchboard. **(a) The wet well level tag does not join — FIXED.** `AID.WRPS.STN.LEVEL` is a real item in the `WRPS_ONE_SEC` group. The correct -reading was the one already suspected: `PS_STN_WET_WELL_LEVEL` becomes a tag row +reading was the one already suspected: the published level becomes a tag row in its own right, and `LIT-101` is marked NOT HISTORISED — it is a field input on `%IW0` and never reaches SCADA. `LIT-101` was the only row in the seed carrying two addresses (`%QW0,%IW0`), which is the instrument and the published @@ -614,11 +621,14 @@ it is the same derivation that will run against `imh`. server was taken as authoritative here. Someone should decide which is intended and re-run the WRPS generators — right now the repository does not describe the running system. -- **A name collision in `scada-points.csv`:** `PS_STN_HIGH_LEVEL_ALARM` names - both the coil 10 status bit and the holding register 1032 setpoint. The point - name is not unique; the item layer is the first place the two are - distinguishable (`STN.HIGH_LEVEL` versus `SP.HIGH_ALARM`). Harmless now that - the item is the key, but it will confuse anyone reading the point list. +- **A name collision in the delivered point list:** one name was used for + both the coil 10 status bit and the holding register 1032 setpoint. The item + layer is the first place the two are distinguishable (`STN.HIGH_LEVEL` versus + `SP.HIGH_ALARM`). **Closed 2026-09-01** — the corrected delivery is keyed on + the item, so both are unique and the special case that told them apart by hand + is gone. It is recorded here because it bit twice: once in the original seed, + and once again during the rename, where a naive substitution mapped the status + bit onto the setpoint. **Gate** - [ ] A `SELECT` from a container on `lin001` returns rows diff --git a/spec/REBUILD.md b/spec/REBUILD.md index 7174016..a7fae93 100644 --- a/spec/REBUILD.md +++ b/spec/REBUILD.md @@ -107,11 +107,14 @@ banner all the way to the operator's screen. **The three Phase 5 findings are fixed** (2026-08-31), against the SCADA configuration rather than against the fixtures. All three came from one -substitution: the stand-in historian was keyed on CI Server **point** names -(`PS_STN_WET_WELL_LEVEL`) when the historian is keyed on CI Server **item** -names (`AID.WRPS.STN.LEVEL`) — two layers apart, not one. Nothing is aliased -across that gap now: `public.historian_items` holds the mapping, generated from -`WRPS/05-scada/modbus` by [`scripts/gen_historian_items.py`](../scripts/gen_historian_items.py), +substitution: the stand-in historian was keyed on names delivered as +`PS_STN_WET_WELL_LEVEL` when the historian is keyed on CI Server **item** names +(`AID.WRPS.STN.LEVEL`). Those `PS_*` names turned out not to be CI Server names +at all — a corrected delivery on 2026-09-01 replaced them, and the seed is now +keyed on the item throughout. Nothing is aliased across that gap: +`public.historian_items` holds the mapping, generated from +[`db/seed/scada-source/`](../db/seed/scada-source/README.md) by +[`scripts/gen_historian_items.py`](../scripts/gen_historian_items.py), and an item that resolves to neither a tag nor a written reason is a build error rather than a silent "no records found". diff --git a/status/current-state.html b/status/current-state.html index 08104f5..08841e1 100644 --- a/status/current-state.html +++ b/status/current-state.html @@ -718,9 +718,12 @@ footer { margin-top: 72px; padding-top: 22px; border-top: 1px solid var(--hair);

Four different names describe the wet well level, and only the fourth is what the historian stores. The instrument on the drawing is LIT-101. The PLC publishes it at %QW0. SCADA polls that register and calls the - point PS_STN_WET_WELL_LEVEL. And then CI Server files the history - under an item name, AID.WRPS.STN.LEVEL — which appears - nowhere in this project’s repository, because nothing had ever needed it before.

+ point WRPS_PLC:STN_LEVEL — the Modbus station name, then the + point name. And then CI Server files the history under an item name, + AID.WRPS.STN.LEVEL — which appeared nowhere in this + project’s repository, because nothing had ever needed it before. Worse, the point list + handed over at the time carried invented names that CI Server never used; a corrected one + arrived on 1 September and the whole repository is keyed on the item now.

Modbus carries register numbers, not names. That is why the point layer and the item layer are free to drift apart, and why reconciling against the register map would have proved only that the first three agreed with each other. The stand-in was built on the third name. The