yau-plant-assistant/CLAUDE.md
Claude b08fe60772 Rewrite CLAUDE.md as an index, and fix what the review found
CLAUDE.md was 120 lines and had become a detailed document. Its job is to
be the first thing another team's Claude Code reads: an index that says
where everything is, plus the few rules that must be known before acting.
Detail is better maintained next to the thing it describes, so it moved
there - the four namespaces and the PS_ ban to db/seed/scada-source, the
ai-docs-worker divergence to api/documents.py's docstring, the plant
description to README.md. The file now says so, and tells the next editor
to keep it that way.

Then a sentence-by-sentence review, which did not go the way the premise
suggested. Of seventeen sentences flagged as possibly unnecessary, one was
padding - and it had been introduced ten minutes earlier. The rest were
carrying real information in the wrong shape, or were wrong, or were
hiding a gap:

  WRONG. "Details: api/contracts.py" followed the classification
  precedence rule, which is implemented in classifier.py. "Prove each
  layer before wiring the agent" instructed a build phase that finished
  weeks ago.

  MISSING. Neither table pointed at retrieval, the schema and migrations,
  compose, or the operator's web app - about a third of the repository's
  changeable surface, absent from an index whose only job is to point at
  it. The retrieval gap surfaced because "the document library" was
  ambiguous: it names the SCREENS in documents.py, not the corpus, and the
  corpus had no row at all.

  CUT BY MISTAKE, in the rewrite this commit also contains. The trigger
  for writing to OPEN-ISSUES.md, and the .bak-<purpose>-<date> convention
  which is the only concrete form of "snapshot before editing" - the rule
  that made running migration 008 on a live database safe.

Two rules are new because the audit kept finding the same failures.
"Never quote a count in prose" generalises three separate staleness
defects - container counts across four documents, the eval-set size across
five, the env-file count. "Pin it before you fix it" now names both
destinations, eval/testset.jsonl and api/tests/, and admits that
deployment and migration defects have neither.

The host's size is recorded for the first time: 2 vCPU and 3 GiB, verified
live. Build elsewhere and ship the image where possible; where not, check
the host is quiet and announce it. deploy.sh builds on the host today,
which is why the rule is a judgement rather than an absolute.

86 lines became 91 during a review meant to shorten it. That is the
finding: the file was not too long, it was pointing at the wrong things.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 21:37:41 +10:00

90 lines
5.6 KiB
Markdown

# WRPS Plant Assistant — an index to this repository, and rules that are not advisory
A proof-of-concept assistant that answers an operator's plain-English questions from plant
data and controlled documents. It runs on `lin001`, an existing **live, shared** Docker host
that is not ours alone.
This file holds only what you must know *before* you act; everything else is a pointer.
Keep it that way: put detail next to the thing it describes, not here.
## Where everything is
| To understand | Read |
|---|---|
| What this is, and how to get oriented | `README.md` |
| Why it is built this way, and the accepted shortcuts (§14) | `spec/BUILD-AI-CONTAINERS.md` |
| The host, its history and its rules — **wins on any conflict** | `spec/YAU_Linux_Host_Onboarding.md` |
| How to deploy or rebuild it | `spec/REBUILD.md` |
| What is running right now | `status/current-state.html` |
| What is blocked, and on whom | `status/REQUESTS.md` |
| What is broken or unfinished, and ours to fix | `status/OPEN-ISSUES.md` |
| To change | Go to |
|---|---|
| The safety contracts, one per answer class | `api/contracts.py` |
| Which class a question gets | `api/classifier.py` |
| The SQL allow-list and query caps | `api/guardrails.py` |
| The document library screens — its docstring records how the build diverges from the design | `api/documents.py` |
| How a document becomes searchable, and how it is found and cited | `ingest/ingest.py`, `api/tools/retrieval.py` |
| How a question becomes a number | `cube/model/` |
| Retention, windows and history measures | `api/tools/metrics.py` |
| Plant facts, tags and equipment | `db/seed/tags.csv` |
| The schema, roles and migrations | `db/*.sql` |
| How a container is defined — networks, volumes, env files | `compose/ai-compose.yml` |
| The operator's screen | `web/` |
| SCADA namespaces and the delivered CI Server config | `db/seed/scada-source/README.md` |
| The stand-in historian — some assumptions fail silently, not loudly | `db/README-standin-historian.md` |
| Who can reach what | `caddy/ai-routes.caddy`, `authelia/access-rules.md` |
| The eval set and its scorecard | `eval/` |
## The three lines this system does not cross
1. **No instructions for safety-critical actions.** Procedural answers give document identity,
revision, effective date and verbatim prerequisites — never synthesised steps.
2. **No recommended setpoints or operating parameters.** Advisory answers give evidence and
documented limits, then defer. Never a number presented as the answer.
3. **No answers outside the evidence.** Zero rows is "no records found", never a plausible figure.
**These are code paths, not prompt instructions.** A safety rule living only in a prompt is not
implemented. Fix failures in the classifier, Cube or ingestion — never by adding to the prompt.
When a class is uncertain, choose the **more restrictive** one.
## Do not break these — a live host, and a live PLC
- **`openplc-runtime` is live control.** Never restart, update or reconfigure it as a side effect
of AI work, and never move its `10.0.0.17` binding to `0.0.0.0` — that binding is the only thing
keeping unauthenticated Modbus off the internet.
- **Never install on, write to or restart `cicore1`.** Never exceed read-only on `imh`.
- **Never omit `import authelia`** from a Caddyfile block — it silently makes a service public.
One deliberate exception exists for `ai.yokogawa.tech`; do not copy or widen it.
- **Restarting Authelia or Caddy logs out every active user.** Announce it first.
- **Growing data goes on `/datadisk`, never `/`.** Root is 62 GB and has hit 100% before.
- **Publish no host ports.** Join the `proxy` network and go through Caddy.
- **No secrets in Git, compose files, logs or error messages.** `0600` env files under `~/ai/`.
- **`lin001` has 2 vCPU and 3 GiB. Do not build images on it if you can build elsewhere**
and ship the image. When you cannot, check the host is quiet first — `uptime`, and who
else is mid-demo — and say so before you start. `deploy.sh` builds on the host today,
which is why this is a judgement and not an absolute.
## How to work here
- **`docker ps` showing "Up" is not proof.** Run `scripts/verify.sh` and read the container logs.
- **When an answer is wrong, prove Cube and retrieval by hand before touching the agent** —
otherwise a wrong answer has four possible causes.
- **Do not invent schema.** `imh` is still pending — inspect it, or ask. Fixtures are marked.
- **Do not hand-edit generated files.** `db/seed/historian_items.csv` comes from
`scripts/gen_historian_items.py`.
- **Store UTC. Convert to local exactly once, in Cube.** Never do timezone maths in a prompt.
- **A document is citable only after a human confirms its number, revision and effective date.**
Never add a path that ingests an unconfirmed header.
- **When something fails, pin it before you fix it.** A question answered wrongly goes in
`eval/testset.jsonl`; anything provable without a model goes in `api/tests/`. Deployment and
migration defects have no test home yet, so they go in `status/OPEN-ISSUES.md`.
- **A defect you are not fixing now goes in `status/OPEN-ISSUES.md`** — read its header first;
it says what belongs there and what belongs elsewhere.
- **Never quote a count in prose.** Point at the file. Container counts, eval-set sizes and
assumption counts have all gone stale in this repo; the file never does.
- **Small commits, one concern each.**
- **If a change alters an accepted phase, re-run that phase's gate.**
- **Snapshot before you edit anything on the host** — config, database or seed.
Name back-ups `.bak-<purpose>-<date>`.