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
|
||||
|
||||
`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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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".
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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
|
||||
point <span class="mono">PS_STN_WET_WELL_LEVEL</span>. And then CI Server files the history
|
||||
under an <em>item</em> name, <span class="mono">AID.WRPS.STN.LEVEL</span> — which appears
|
||||
nowhere in this project’s repository, because nothing had ever needed it before.</p>
|
||||
point <span class="mono">WRPS_PLC:STN_LEVEL</span> — the Modbus station name, then the
|
||||
point name. And then CI Server files the history under an <em>item</em> name,
|
||||
<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
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue