Skip to content

S6 — Detect coordinate-registry drift against the Appwrite Seam Contract (C-57) #187

Description

@Polichinel

Part of epic #181. Closes register C-57 (Tier 3). Depends on S3.

Background

views_postprocessing/unfao/appwrite_env.py declares 15 environment-variable names. Their authority is the Appwrite Seam Contract's coordinate registry, which lives in another repository:

../views-appwrite/docs/ADRs/platform/coordinate_registry.toml

The registry is deliberately referenced, never copied — its own header forbids copying, and this repo's CLAUDE.md states the same rule platform-wide. That is the right call: one owner, no duplicated values, no drift-by-fork.

But it leaves a gap that C-57 names precisely: nothing mechanical will tell you when the two diverge. The registry is versioned ([meta] version = "1.3.0", ratified by þing-02 on 2026-07-31) and it changes — it has already retired one contract name (see S3) and it carries five planned secret slots that will land:

[secret.APPWRITE_READ_API_KEY]     status = "planned — operator issues (D4)"
[secret.APPWRITE_WRITE_API_KEY]    status = "planned — operator issues (D4). Blocked on the create_* gating (§5.5)."
[secret.APPWRITE_PROVISION_API_KEY] status = "planned — operator issues (D4). NEVER given to a long-running process."

Checked 2026-08-01: there is no live drift today. All 15 names this repo declares are present in the registry with matching class, and the secret slot it uses (APPWRITE_DATASTORE_API_KEY) is still the current one. So this story builds a detector, it does not fix a break. Doing it now, while the answer is known-good, is the cheap moment — the alternative is discovering the drift during a failed delivery.

Work

Declare, in appwrite_env.py, the registry version this repo's declaration was verified against:

#: The Appwrite Seam Contract version these names were verified against.
SEAM_CONTRACT_VERSION = "1.3.0"

That is a version string, not a coordinate value — it does not copy the registry, it records which edition of it was read. Cite the pinned URL alongside it.

Then add a gated drift test that, when a views-appwrite checkout is resolvable, parses the registry and asserts:

  1. every name in CONNECTION_ENV / PROD_FORECASTS_ENV / UNFAO_ENV exists in the registry;
  2. each is declared with the class this repo treats it as (connection / target / secret) — the registry declares class explicitly and forbids inferring it from the prefix, so read it, do not derive it;
  3. the registry's [meta] version equals SEAM_CONTRACT_VERSION, with a failure message that says "the registry moved to X; re-verify this repo's declaration and bump the constant".

Check 3 is the one that earns the story. Checks 1 and 2 catch a rename; check 3 catches anything else, including additions this repo ought to adopt.

Never assert on a value. The registry contains non-secret coordinate values; this test must compare names, classes and the version only. Copying a value into a test is the same violation as copying it into code — tests/test_redaction_guard.py governs the surrounding discipline.

Gating. Use the same declared resolution S7 establishes for views-datafactory. Do not hardcode a path; do not silently pass when the sibling is absent. Skip with a reason that names the env var to set.

Acceptance criteria

  • SEAM_CONTRACT_VERSION declared, with a pinned-URL citation.
  • The drift test passes today against registry 1.3.0.
  • It fails, with an actionable message, if a declared name is missing, misclassified, or the registry version moves.
  • No coordinate value appears anywhere in the test or the module.
  • The test skips cleanly (never errors) when views-appwrite is not resolvable.

Testing

Suggested home: tests/test_env_declaration.py (owns this module) or a new tests/test_seam_contract_drift.py if the gating machinery makes the former unwieldy. Prefer extending the existing file — one concept, one home.

Prove the detector bites, per this repo's standing practice:

def test_the_drift_check_would_catch_a_rename(tmp_path):
    """A gated test that cannot fail is decoration —
    cf. test_clone_readiness.py::test_the_guard_would_actually_catch_a_violation."""

Feed it a synthetic registry with one name renamed and assert the check fails. That part needs no views-appwrite checkout, so it runs in CI.

Validation:

ruff check .
pytest -q tests/test_env_declaration.py
pytest -q                                   # with ../views-appwrite present: drift test runs
grep -rn "691b14fc\|fra.cloud.appwrite" tests/ views_postprocessing/   # must be empty — no values copied

Dependencies

Depends on S3 — both edit appwrite_env.py's docstring and citations, and S3 establishes the current contract name this story's constant refers to. Sequence them to avoid a conflict.

Uses the resolution helper from S7 if S7 lands first; otherwise S7 adopts whatever this story establishes. Whichever is second must not leave two ways to find a sibling repo.

Files

  • views_postprocessing/unfao/appwrite_env.py — the declaration
  • ../views-appwrite/docs/ADRs/platform/coordinate_registry.toml — the authority (read only)
  • tests/test_env_declaration.py — the governing test file
  • tests/test_redaction_guard.py — the no-values discipline
  • Register C-57

Activity

  1. added
    storyA single reviewable unit of an epic
    testingTest/parity/validation work
    on Aug 1, 2026
  2. Polichinel commented on Aug 2, 2026

    @Polichinel
    CollaboratorAuthor

    Two corrections before this is started, from #184/#195:

    1. The registry is v1.4.0, not 1.3.0. It was amended upstream on 2026-08-02 (amended = "2026-08-02", contract §5.9 "one writer per value"). This issue's body specifies SEAM_CONTRACT_VERSION = "1.3.0", which went stale before the story was written — a fair illustration of why the drift test is worth having.

    2. The pinned commit b54928f now appears in two places that must move together: views_postprocessing/unfao/appwrite_env.py (module docstring) and docs/ADRs/013_…md §7d. Making that pair checkable is squarely this story's job — consider whether the declared constant should carry the commit as well as the version.

    Also: read appwrite_env.py against the tree, not against this issue. S1 (#182) and S3 (#184) have both rewritten parts of it since this was filed.

  3. Polichinel commented on Aug 2, 2026

    @Polichinel
    CollaboratorAuthor

    Third correction for this story, from #196/#198 — and it changes the test's shape, not just a constant.

    S3's pin (b54928f, labelled v1.4.0) was an unmerged branch commit and has been withdrawn. It was resolved with git -C ../views-appwrite rev-parse HEAD on a checkout that happened to be sitting on feat/s1-single-writer-rule.

    Superseding my earlier comment: the registry is v1.3.0, pinned at 47172af (the tip of main, ratified þing-02) — not 1.4.0, and not 1.3.0-for-the-reason-I-first-gave.

    Add to this story's scope: the drift test must assert the pinned commit is reachable from views-appwrite's main, not merely that the file exists at it. Existence was true of the withdrawn commit; reachability was not, and reachability is the property that actually means "this is what the contract says".

    git -C <views-appwrite> merge-base --is-ancestor <pinned> origin/main
    

    That single check would have caught this within the hour instead of requiring another seat to notice.

  4. added a commit that references this issue on Aug 2, 2026
  5. Polichinel commented on Aug 2, 2026

    @Polichinel
    CollaboratorAuthor

    Landed in #202. C-57 closed.

    Four checks, each mutation-proven with bytecode disabled (stale .pyc was lying mid-campaign — both shas are 7 chars, so size+mtime validation passed and Python served a cached module contradicting its own source; everything re-verified from clean).

    The reachability check is the one that matters. #196 happened three hours before this story: a pin whose commit existed, whose files existed at it, and which passed every check anyone had written — but had never reached main. Existence is not reachability, and now something asserts it.

    The value-copy check took two wrong narrowings, both recorded in C-57's resolution. A substring scan cried wolf on a function name; narrowing to assignments and defaults then caught zero of {"bucket": "unfao_bucket"} and AppwriteConfig(bucket_id="unfao_bucket") — the second being the shape this repo would actually produce. Right axis: exact equality on string constants, not statement shape.

    Review also caught _EXPECTED_CLASS deriving class from the _API_KEY suffix — the exact inference the registry forbids, inside the test enforcing the registry.

    Register: 74 concerns, 19 open, 55 resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    storyA single reviewable unit of an epictestingTest/parity/validation work

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions