⚠ This issue was rewritten 2026-08-02. The original body contained two false claims, both produced by regex searches whose null results were reported as findings. They are corrected below and the retraction is kept at the bottom rather than deleted.
The problem, verified
The pieces for a clean environment setup all exist and work. What does not exist is a path through them — and the one document that should provide that path contains a dead link exactly where the path belongs.
What is actually true — each item read, not grepped
.env files on disk — 6, in 3 repos. All three containing real secrets are gitignored (verified per-file with git check-ignore):
| File |
Real secrets |
Gitignored |
views-models/.env |
yes — 16 declared vars |
✓ |
views-faoapi/.env |
yes |
✓ |
views-faoapi/.env.bak-20260720 |
yes — a backup with no stated purpose |
✓ |
three .env.example files |
no |
tracked |
Who reads what at runtime:
| Repo |
Reads |
Resolves to |
| views-pipeline-core |
model_path.py:371 → model_path.root / ".env" |
views-models/.env |
| views-faoapi |
managers/api.py:139 → APIPathManager.root / ".env" (its own root, managers/model.py:254) |
its own file |
| views-crafdapi |
the same line, inherited by copy |
would need its own |
| views-postprocessing |
nothing — asserts the launcher assembled the env (þing-02 P1) |
— |
| views-models |
no Python loads dotenv. postprocessors/un_fao/run.sh:23-27 sources .env and exports ONE variable by name |
is the source |
| views-appwrite |
nothing, no runtime |
— |
The good pattern already exists and already runs. views-models/tools/registry_to_env.py reads coordinate_registry.toml and emits the non-secret coordinates; it cannot emit a secret, because the registry holds secrets as slots with no value. views-faoapi/deployment/registry_to_env.py is its twin. un_fao/run.sh:88 already calls it. On that path, coordinates already come from git and only the key comes from .env.
The actual gap
docs/Onboarding Resources/views_platform_onboarding_guide.md is the platform's entry point, and its technical-setup pointer is empty.
- Line 31:
[Internal Tech Guide]() — literally empty parentheses
- Line 71:
[Internal Tech Guide] — no link at all
The guide mentions no .env, no credentials, no environment variables, and lists 6 repos out of 20+. It is a navigation guide; the place where it would hand off to technical setup is a dead link.
So a newcomer — or the maintainer on a new machine — follows the one documented entry point and arrives at nothing.
Secondary finding
views-faoapi requires 8 variables (_REQUIRED_APPWRITE_ENV_VARS, api.py:41-50). Seven are coordinates the registry already owns, and api.py already contains _registry_coordinates() and _validate_env_against_registry() — it reads the registry and treats it as canonical. Only APPWRITE_DATASTORE_API_KEY is a genuine secret.
So faoapi requires from a hand-written file seven values it already knows how to read from git. This is not a defect in any single file; it is the pattern from un_fao/run.sh not yet applied here.
What this issue does NOT propose
Not "one .env for the whole platform." That is impossible: views-faoapi deploys standalone to Hetzner where views-models does not exist, and deployment/bootstrap.sh exists to bridge exactly that.
The end state is not one file. It is one secret and zero coordinates per machine, which makes the file count stop mattering.
Work
Acceptance criteria
Why here
The registry is homed in this repo and this is seam contract §4. The work lands mostly in views-faoapi and the platform docs repo; this issue holds the shape and the acceptance criteria.
Retraction — what the original body got wrong
Kept rather than deleted, because the failure mode matters more than the two facts.
-
"views-faoapi/.env.example is lying — it declares 1 variable and the code requires 8." False. That file deliberately serves two audiences — a caller needs one key; a runner needs the server set — and it points at docs/ADRs/active/013_environment_variable_validation.md, which lists all eight in a table. It also deliberately refuses to put infrastructure values in a template, which is what seam contract §4 requires. The chain works. I produced the claim with a regex matching only ^[A-Z_]+= assignment lines, which cannot see prose comments, then reported it as "the file only tells you about one variable."
-
"No new-machine setup doc exists anywhere." False. docs/Onboarding Resources/views_platform_onboarding_guide.md exists. My find used -maxdepth 3 and never searched for "onboard". Proven broken by pointing it at views-datafactory/docs/guides/credential_setup.md, a file cited earlier in the same session: old query 0 hits, correct query 1.
Both errors are one fault: a null result from a hand-written pattern reported as a fact about the world rather than a fact about the pattern. That is Cluster J — "cannot distinguish 'no' from 'I could not tell', and answers anyway" — the defect class views-pipeline-core#339 was opened to kill, reproduced while writing an issue about tidiness.
The rule adopted as a result: a negative result is not a finding until the query has been shown to find a known positive. Every claim in the corrected body above was re-verified that way, or by reading the file in full.
The problem, verified
The pieces for a clean environment setup all exist and work. What does not exist is a path through them — and the one document that should provide that path contains a dead link exactly where the path belongs.
What is actually true — each item read, not grepped
.envfiles on disk — 6, in 3 repos. All three containing real secrets are gitignored (verified per-file withgit check-ignore):views-models/.envviews-faoapi/.envviews-faoapi/.env.bak-20260720.env.examplefilesWho reads what at runtime:
model_path.py:371→model_path.root / ".env"views-models/.envmanagers/api.py:139→APIPathManager.root / ".env"(its own root,managers/model.py:254)postprocessors/un_fao/run.sh:23-27sources.envand exports ONE variable by nameThe good pattern already exists and already runs.
views-models/tools/registry_to_env.pyreadscoordinate_registry.tomland emits the non-secret coordinates; it cannot emit a secret, because the registry holds secrets as slots with no value.views-faoapi/deployment/registry_to_env.pyis its twin.un_fao/run.sh:88already calls it. On that path, coordinates already come from git and only the key comes from.env.The actual gap
docs/Onboarding Resources/views_platform_onboarding_guide.mdis the platform's entry point, and its technical-setup pointer is empty.[Internal Tech Guide]()— literally empty parentheses[Internal Tech Guide]— no link at allThe guide mentions no
.env, no credentials, no environment variables, and lists 6 repos out of 20+. It is a navigation guide; the place where it would hand off to technical setup is a dead link.So a newcomer — or the maintainer on a new machine — follows the one documented entry point and arrives at nothing.
Secondary finding
views-faoapirequires 8 variables (_REQUIRED_APPWRITE_ENV_VARS,api.py:41-50). Seven are coordinates the registry already owns, andapi.pyalready contains_registry_coordinates()and_validate_env_against_registry()— it reads the registry and treats it as canonical. OnlyAPPWRITE_DATASTORE_API_KEYis a genuine secret.So faoapi requires from a hand-written file seven values it already knows how to read from git. This is not a defect in any single file; it is the pattern from
un_fao/run.shnot yet applied here.What this issue does NOT propose
Not "one
.envfor the whole platform." That is impossible:views-faoapideploys standalone to Hetzner whereviews-modelsdoes not exist, anddeployment/bootstrap.shexists to bridge exactly that.The end state is not one file. It is one secret and zero coordinates per machine, which makes the file count stop mattering.
Work
[Internal Tech Guide]()dead link — or delete the reference. A pointer to nothing is worse than no pointer: it tells a reader the answer exists and they failed to find it.un_fao/run.shpattern to faoapi — resolve its seven coordinates from the registry instead of.env, using the reader already in that repo. Reduces its.envto one line.views-crafdapiat birth. Free now, a migration later.views-faoapi/.env.bak-20260720— a third file of real secrets with no stated purpose.Acceptance criteria
views-crafdapinever acquires a second.envhabit.Why here
The registry is homed in this repo and this is seam contract §4. The work lands mostly in
views-faoapiand the platformdocsrepo; this issue holds the shape and the acceptance criteria.Retraction — what the original body got wrong
Kept rather than deleted, because the failure mode matters more than the two facts.
"
views-faoapi/.env.exampleis lying — it declares 1 variable and the code requires 8." False. That file deliberately serves two audiences — a caller needs one key; a runner needs the server set — and it points atdocs/ADRs/active/013_environment_variable_validation.md, which lists all eight in a table. It also deliberately refuses to put infrastructure values in a template, which is what seam contract §4 requires. The chain works. I produced the claim with a regex matching only^[A-Z_]+=assignment lines, which cannot see prose comments, then reported it as "the file only tells you about one variable.""No new-machine setup doc exists anywhere." False.
docs/Onboarding Resources/views_platform_onboarding_guide.mdexists. Myfindused-maxdepth 3and never searched for "onboard". Proven broken by pointing it atviews-datafactory/docs/guides/credential_setup.md, a file cited earlier in the same session: old query 0 hits, correct query 1.Both errors are one fault: a null result from a hand-written pattern reported as a fact about the world rather than a fact about the pattern. That is Cluster J — "cannot distinguish 'no' from 'I could not tell', and answers anyway" — the defect class
views-pipeline-core#339was opened to kill, reproduced while writing an issue about tidiness.The rule adopted as a result: a negative result is not a finding until the query has been shown to find a known positive. Every claim in the corrected body above was re-verified that way, or by reading the file in full.