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

5.6 KiB

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>.