The problem
The platform resolves Appwrite configuration through two mechanisms that both write to the same shared mutable state — the process environment — with no rule about which wins.
views-models/postprocessors/un_fao/run.sh sources .env (15 coordinates + 1 secret at lines 23-27), then exports registry values over the top (line 88). The registry wins because it runs second, not because anything says it should. Reorder those two blocks and the semantics invert silently.
Why this is correctness, not tidiness
Two prior reviews framed this as duplication and discoverability. A third, reading it through Rust's ownership model, found it meets a formal definition. Rust states three conditions for a data race, all of which must hold simultaneously:
| Condition |
Here |
| Two or more pointers access the same data |
.env and the registry both name APPWRITE_ENDPOINT, *_BUCKET_ID, all 15 |
| At least one is used to write |
both write |
| No mechanism synchronising access |
none. Statement order is the only discipline, and it is implicit |
All three hold. This is not an analogy — it satisfies the definition.
Two related findings from the same lens:
- A
/blob/main/ reference is a dangling pointer. Rust: "a reference to a location that may have been given to someone else." views-datafactory's guides linked to main; §5.7 was struck; the reference silently began resolving to different rules. A pinned tag is a lifetime annotation; main is a raw pointer into mutable storage.
- Clone must be visible. Rust does not forbid duplication — it forbids implicit duplication, because
.clone() is "a visual indicator that something different is going on." The secret exists in four places and nothing marks any of them as a copy.
The contract already mandates the fix; the shell layer cannot express it
Seam contract §2:
Every library on this seam receives credentials as constructor parameters from the launching process and must never source, persist, default, or log them.
That is ownership and borrowing stated exactly: the launcher owns, libraries borrow, libraries never persist. views-postprocessing implements it correctly (it asserts rather than loads). Shell cannot comply — it has no parameter passing to child processes, only the global environment. That is why this defect class keeps recurring, and it is not a bug in any one file.
Desired end state
- Exactly one writer per class of value. Registry owns coordinates. Operator owns the secret. A second writer errors rather than losing quietly.
- No importable library loads a
.env anywhere on the platform.
- Registry resolution failure exits non-zero. No warn-and-continue.
- A new machine is set up by running one script, which is tested in CI without real credentials.
Already shipped — verified against current HEADs, not assumed
| Work |
Where |
State |
find_dotenv() deleted; preflight on every Appwrite path; AppwriteConfig frozen |
views-pipeline-core#346 |
closed |
| C-231 metadata-delete fix |
views-faoapi#322 |
shipped in v1.4.0 |
| Cache-partition salt |
views-faoapi#323 |
shipped in v1.4.0 |
| Conformance vector |
views-appwrite#13 |
closed |
Explicitly NOT this epic
- Extracting shared client code into views-appwrite. That is views-faoapi#337, gated on þing-01 D8, which is views-appwrite#23. Those two are the same decision seen from opposite sides. Cross-linked, not re-scoped here.
- The 131
run.sh files. They do not touch Appwrite — grep -ci appwrite returns 0. CRP: things not reused together should not be forced together. They get one marking line (S7) and nothing else.
- The
views-models rename; any secret-store design; views-pipeline-core#339's eviction phases.
Stories
| # |
Story |
Repo |
Depends on |
| S1 |
Contract states the single-writer rule |
views-appwrite |
— |
| S2 |
Registry resolution fails loud (C-47) |
views-models |
— |
| S3 |
tools/platform_env.sh — one writer (C-48) |
views-models |
S1, S2 |
| S4 |
The surviving library dotenv load at model_path.py:371 |
views-pipeline-core |
— |
| S5 |
faoapi resolves coordinates from the registry |
views-faoapi |
S1 |
| S6 |
Agreement check between the two registry readers (C-50) |
views-appwrite |
— |
| S7 |
Mark the 131 generated run.sh as clones |
views-models |
— |
| S8 |
bootstrap.sh, CI-testable without credentials |
views-models |
S2, S3 |
| S9 |
Fill the dead [Internal Tech Guide]() link |
views-platform/docs |
S8 |
| S10 |
views-crafdapi adopts the pattern at birth |
views-faoapi |
S5 |
Order: S1 · S2 · S4 · S6 · S7 (independent) → S3 → S5 → S8 → S9 · S10
S2 first among implementation items — it is the one most likely to break the machine being set up next.
Epic acceptance criteria
Why this epic is homed here
The registry is homed in this repo and this is seam contract §4 territory. Most of the work lands in views-models and views-faoapi; this epic holds the shape, the ownership rule, and the acceptance criteria.
The problem
The platform resolves Appwrite configuration through two mechanisms that both write to the same shared mutable state — the process environment — with no rule about which wins.
views-models/postprocessors/un_fao/run.shsources.env(15 coordinates + 1 secret at lines 23-27), then exports registry values over the top (line 88). The registry wins because it runs second, not because anything says it should. Reorder those two blocks and the semantics invert silently.Why this is correctness, not tidiness
Two prior reviews framed this as duplication and discoverability. A third, reading it through Rust's ownership model, found it meets a formal definition. Rust states three conditions for a data race, all of which must hold simultaneously:
.envand the registry both nameAPPWRITE_ENDPOINT,*_BUCKET_ID, all 15All three hold. This is not an analogy — it satisfies the definition.
Two related findings from the same lens:
/blob/main/reference is a dangling pointer. Rust: "a reference to a location that may have been given to someone else."views-datafactory's guides linked tomain; §5.7 was struck; the reference silently began resolving to different rules. A pinned tag is a lifetime annotation;mainis a raw pointer into mutable storage..clone()is "a visual indicator that something different is going on." The secret exists in four places and nothing marks any of them as a copy.The contract already mandates the fix; the shell layer cannot express it
Seam contract §2:
That is ownership and borrowing stated exactly: the launcher owns, libraries borrow, libraries never persist.
views-postprocessingimplements it correctly (it asserts rather than loads). Shell cannot comply — it has no parameter passing to child processes, only the global environment. That is why this defect class keeps recurring, and it is not a bug in any one file.Desired end state
.envanywhere on the platform.Already shipped — verified against current HEADs, not assumed
find_dotenv()deleted; preflight on every Appwrite path;AppwriteConfigfrozenExplicitly NOT this epic
run.shfiles. They do not touch Appwrite —grep -ci appwritereturns 0. CRP: things not reused together should not be forced together. They get one marking line (S7) and nothing else.views-modelsrename; any secret-store design;views-pipeline-core#339's eviction phases.Stories
tools/platform_env.sh— one writer (C-48)model_path.py:371run.shas clonesbootstrap.sh, CI-testable without credentials[Internal Tech Guide]()linkviews-crafdapiadopts the pattern at birthOrder: S1 · S2 · S4 · S6 · S7 (independent) → S3 → S5 → S8 → S9 · S10
S2 first among implementation items — it is the one most likely to break the machine being set up next.
Epic acceptance criteria
.envanywhere on the platformviews-faoapi's.envrequires one variablebootstrap.shgreen in CI with no real credentials, and followed end-to-end on a machine that had never run the platformviews-crafdapinever acquires a second.envhabitregistry_to_env.pycopies proven to emit identical outputWhy this epic is homed here
The registry is homed in this repo and this is seam contract §4 territory. Most of the work lands in
views-modelsandviews-faoapi; this epic holds the shape, the ownership rule, and the acceptance criteria.