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 <noreply@anthropic.com>
This commit is contained in:
parent
f3c8020ac1
commit
73e90cf710
5 changed files with 64 additions and 22 deletions
36
CLAUDE.md
36
CLAUDE.md
|
|
@ -42,8 +42,11 @@ Advisory beats Historical. Partly-advisory is advisory.
|
||||||
|
|
||||||
## Verification
|
## Verification
|
||||||
|
|
||||||
`docker ps` showing "Up" is **not** proof. `curl -sI` the public URL, expect `302` to the auth
|
`docker ps` showing "Up" is **not** proof. `curl -sI` the public URL and read the container
|
||||||
portal, and read the container logs. Prove each layer without the LLM first: prove Cube returns
|
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.
|
the right number by hand, prove retrieval finds the right procedure by hand, *then* wire the agent.
|
||||||
|
|
||||||
## Working conventions
|
## 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.**
|
- 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** —
|
- **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
|
`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);
|
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.
|
**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.
|
- 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.
|
- Small commits, one concern each. If a change alters an accepted phase, re-run that phase's gate.
|
||||||
|
|
||||||
## The plant
|
## The plant
|
||||||
|
|
@ -76,9 +88,23 @@ historises the result.
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Instrument tag | `LIT-101` | `WRPS/01-design-doc` |
|
| Instrument tag | `LIT-101` | `WRPS/01-design-doc` |
|
||||||
| PLC symbol + address | `%QW0` | `WRPS/04-plc/register-map.csv` |
|
| 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` |
|
| **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,
|
**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
|
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
|
history on a point name is what produced all three Phase 5 findings. `db/seed/tags.csv` owns
|
||||||
|
|
|
||||||
|
|
@ -191,7 +191,7 @@ deletes them.
|
||||||
## 7. History, so this is not relearned
|
## 7. History, so this is not relearned
|
||||||
|
|
||||||
The stand-in was rebuilt on 2026-08-31. The version before it was keyed on CI
|
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
|
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
|
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,
|
independent bug: a tag that would not join, alarm times in the wrong timezone,
|
||||||
|
|
|
||||||
|
|
@ -507,8 +507,15 @@ and only the last is what CI Server historises:
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Instrument tag | `LIT-101` | `WRPS/01-design-doc` |
|
| Instrument tag | `LIT-101` | `WRPS/01-design-doc` |
|
||||||
| PLC symbol + address | `%QW0` | `WRPS/04-plc/register-map.csv` |
|
| 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** | `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` | `WRPS/05-scada/modbus/wrps_item_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
|
`db/002_fixtures.sql` was keyed on the third. The historian is keyed on the
|
||||||
fourth. Everything else followed from that.
|
fourth. Everything else followed from that.
|
||||||
|
|
@ -522,7 +529,7 @@ switchboard.
|
||||||
|
|
||||||
**(a) The wet well level tag does not join — FIXED.**
|
**(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
|
`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
|
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
|
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
|
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
|
server was taken as authoritative here. Someone should decide which is
|
||||||
intended and re-run the WRPS generators — right now the repository does not
|
intended and re-run the WRPS generators — right now the repository does not
|
||||||
describe the running system.
|
describe the running system.
|
||||||
- **A name collision in `scada-points.csv`:** `PS_STN_HIGH_LEVEL_ALARM` names
|
- **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 point
|
both the coil 10 status bit and the holding register 1032 setpoint. The item
|
||||||
name is not unique; the item layer is the first place the two are
|
layer is the first place the two are distinguishable (`STN.HIGH_LEVEL` versus
|
||||||
distinguishable (`STN.HIGH_LEVEL` versus `SP.HIGH_ALARM`). Harmless now that
|
`SP.HIGH_ALARM`). **Closed 2026-09-01** — the corrected delivery is keyed on
|
||||||
the item is the key, but it will confuse anyone reading the point list.
|
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**
|
**Gate**
|
||||||
- [ ] A `SELECT` from a container on `lin001` returns rows
|
- [ ] A `SELECT` from a container on `lin001` returns rows
|
||||||
|
|
|
||||||
|
|
@ -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
|
**The three Phase 5 findings are fixed** (2026-08-31), against the SCADA
|
||||||
configuration rather than against the fixtures. All three came from one
|
configuration rather than against the fixtures. All three came from one
|
||||||
substitution: the stand-in historian was keyed on CI Server **point** names
|
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**
|
`PS_STN_WET_WELL_LEVEL` when the historian is keyed on CI Server **item** names
|
||||||
names (`AID.WRPS.STN.LEVEL`) — two layers apart, not one. Nothing is aliased
|
(`AID.WRPS.STN.LEVEL`). Those `PS_*` names turned out not to be CI Server names
|
||||||
across that gap now: `public.historian_items` holds the mapping, generated from
|
at all — a corrected delivery on 2026-09-01 replaced them, and the seed is now
|
||||||
`WRPS/05-scada/modbus` by [`scripts/gen_historian_items.py`](../scripts/gen_historian_items.py),
|
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
|
and an item that resolves to neither a tag nor a written reason is a build
|
||||||
error rather than a silent "no records found".
|
error rather than a silent "no records found".
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -718,9 +718,12 @@ footer { margin-top: 72px; padding-top: 22px; border-top: 1px solid var(--hair);
|
||||||
<p><b>Four different names describe the wet well level, and only the fourth is what the
|
<p><b>Four different names describe the wet well level, and only the fourth is what the
|
||||||
historian stores.</b> The instrument on the drawing is <span class="mono">LIT-101</span>. The
|
historian stores.</b> The instrument on the drawing is <span class="mono">LIT-101</span>. The
|
||||||
PLC publishes it at <span class="mono">%QW0</span>. SCADA polls that register and calls the
|
PLC publishes it at <span class="mono">%QW0</span>. SCADA polls that register and calls the
|
||||||
point <span class="mono">PS_STN_WET_WELL_LEVEL</span>. And then CI Server files the history
|
point <span class="mono">WRPS_PLC:STN_LEVEL</span> — the Modbus station name, then the
|
||||||
under an <em>item</em> name, <span class="mono">AID.WRPS.STN.LEVEL</span> — which appears
|
point name. And then CI Server files the history under an <em>item</em> name,
|
||||||
nowhere in this project’s repository, because nothing had ever needed it before.</p>
|
<span class="mono">AID.WRPS.STN.LEVEL</span> — 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.</p>
|
||||||
<p><b>Modbus carries register numbers, not names.</b> That is why the point layer and the item
|
<p><b>Modbus carries register numbers, not names.</b> 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
|
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
|
only that the first three agreed with each other. The stand-in was built on the third name. The
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue