---
title: Clear the three failing check:cluster gates on league master
type: task
status: completed
description: >-
  yarn check:cluster fails on master across three unowned gates -- 8 call sites passing keys their
  callee ignores, 2 unadjudicated stale SPA reads, and league-schema-consumers reporting
  non-executing SQL plus a stale adjudication.
base_uri: user:task/league/clear-red-check-cluster-gates.md
created_at: '2026-09-06T06:07:20.460Z'
entity_id: ed3b8b96-2550-4550-b212-92d986e12eb6
finished_at: '2026-09-06T16:07:06.666Z'
owner_identity_uri: █████████████████████████
priority: Medium
public_read: true
started_at: '2026-09-06T14:38:21.035Z'
tags:
  - user:tag/league-xo-football.md
  - user:tag/software-task.md
updated_at: '2026-09-06T16:07:06.666Z'
visibility_analyzed_at: '2026-11-05T23:48:31.147Z'
---

# Overview

`yarn check:cluster` fails on league master. Three gates report findings, and none of the three is owned by any live session.

The gate runner exits 1 as soon as any gate reports, so all three have to clear before `check:cluster` is green again. It is not on the push path today — the red-main push guard reads CI, not this — so this is standing debt rather than a block.

Outcome: `check:cluster` exits 0, with every finding either repaired or carrying an adjudication entry that states why the code is correct as written.

# Background

Two of the three gates are now green. `league-schema-consumers` remains, and its scope was materially understated in the original measurement.

| Gate | State | Detail |
| --- | --- | --- |
| `call-site-param-contracts` | GREEN | 8 findings repaired or marked intentional |
| `██████████████████████` | GREEN | 2 reads adjudicated as not-a-row-read |
| `league-schema-consumers` | FINDINGS | 7 remaining, down from 13 |

Every one of the three declares a negative control and every control fires `RED as expected`, so all three verdicts were real findings rather than a blind gate. This corrects a prevailing description worth naming, because it was handed on in a dispatch brief and would have sent the next session looking for a detector defect: `league-schema-consumers` was previously reported as BLIND. It is not. Its control block prints twenty-one `RED as expected` lines covering all four sub-gates.

**The count was wrong twice, in the same direction.** The gate was first described as failing on two things and actually failed on three, with `call-site-param-contracts` missing from that description. Then `league-schema-consumers` was described here as two findings — a non-executing statement plus a stale adjudication — and a full run reported thirteen. Read this gate's own FINDINGS block rather than any prose summary of it; two of the thirteen were stale adjudications each masking further findings behind them.

## What was done

`call-site-param-contracts`, green at `738923b06` and `87daf86c8`:

- `classify_check_rows` pair — `parked_by_key` and `parked_for_check` are LOCALS the function derives from `parked`, not parameters. Dropping the dead keys alone would have left the case unable to see a broken `parked`, so the spec gained the suppression case it never had. Falsified as a pair: neutering `parked_for_check` turns the new case red while the pre-existing one stays green.
- `get_data_view_results_query({ splits })` — real rename to `row_axes` in `6610e26fb`. `splits` survives as a live legacy URL alias, so the parse and migration specs keep it correctly.
- `withdraw_auction_election({ user_id })` — callee takes none and the production route passes none.
- The three Caesars `get_market_type({ market_category })` sites are correct as written: the category switch was deleted in `d994c6852` and passing the key is how the cases pin that it stays deleted. They carry a new inline marker rather than an adjudication entry, since the adjudications file is a debt ledger and none of its verdicts fit a site that is fine. See `docs/guides/gates.md`.

`██████████████████████`, green at `a0a69c519`: neither site reads a row. `browser-storage.mjs:358` reads a `timestamp` off the localStorage record the same module writes; `view-generation-control.js:118` reads `element.value` off a DOM input ref. Both are the established `not-a-row-read` class.

The 20 orphaned adjudication entries found behind this work moved to [[██████████████████████-████-████-█████-████████-███████]], and the close-time guard that would have caught their citation going stale is in `cli/check-wrap-hygiene.mjs`.

## The 7 remaining league-schema-consumers findings

Four groups. Each site has now been read in its source and, where execution could test it, executed.

**Vendor API keys read as columns (1 finding) — CONFIRMED `not_a_column`.** `docs/guides/api.md:108` documents `config.content_feed_api`, a config key read off the runtime config object (`libs-server/content-feed-client.mjs` reads `config.content_feed_api?.base_url`), not a `config` table column. `config` is a real league table, so the pair binds at all; same established collision as the `config.*` adjudications already on file. Adjudicate per site, `kind = not_a_column`.

**In-memory JS objects read as columns (2 findings) — CONFIRMED `not_a_column`.** `nfl/league/projection-period-encoding.md:46` is `player.pts_added` as the in-memory player object shape ("The same rule holds in memory. `player.pts_added` is keyed by numeric week AND by the four named aggregate keys"); `:148` is `player.projection` inside a grep-control-file example (`player.projection.ros`). Both plain JS object properties, not columns. Adjudicate each site, `kind = not_a_column`.

**SQL against another database (2 findings) — both adjudicatable, one with a real defect beneath it.**
`nfl/betting/analyze-game-for-selections.md:99` is a content-feed statement — Step 5's heading says "Query `feed_items` in the content-feed database", the doc's tool list includes `mcp__db__query_content-feed`, and `feed_items` + its columns are verified live in content-feed. Cross-database, `kind = not_a_league_statement`. BUT it also selects `summary`, which exists in NO content-feed table or view (checked the live schema and the content-feed repo history) — the workflow's own SQL would `42P01` against its target database too, so the doc should be REPAIRED (`summary` -> `body`, the raw body column) in addition to adjudicating the cross-database binding. Note the first `feed_items` block (line 87) is not a finding — it sits in the gate-2 syntax-after-substitution uncovered bucket. The precedent `price-check-a-player.md` `FROM feed_items` adjudication claims "all seven columns this statement selects exist there" but its own statement selects `summary` too, which does not exist — that claim predates this measurement and is wrong; it is one adjudication on file, out of this task's lane.
`libs-server/population-levels.mjs:1031` is a transaction-scoped TEMP TABLE control: the enclosing function (`sql_controls`) creates `control_history` in the same `db.transaction(trx => ...)` (`CREATE TEMP TABLE control_history ... ON COMMIT DROP` at ~line 1022) and the standalone `INSERT INTO control_history VALUES (2025,'UNBACKED','B',now())` runs inside it. Correct as written — a transaction-local table the gate's per-statement EXPLAIN cannot see. Not a league defect; adjudicate per site (there is no standing kind that names this; the closest existing shape is `fragment` — record which statement defines the relation).

**Knex bindings the gate cannot plan (2 findings) — CONFIRMED gate-model limitation, not a code defect.** Both `libs-server/propagate-prop-market-identity.mjs` statements (lines 76 and 122) are `db.raw(sql, [JSON.stringify(candidates)])` with `?::jsonb` inside `jsonb_to_recordset(?::jsonb)`. Verified on a scratch DB loaded from `db/schema.postgres.sql`:
- The EXECUTED form — `?` replaced by a real bound jsonb array, as knex would send it — EXPLAINs cleanly for both statements.
- The gate's extracted form keeps the `?` because `substitute_placeholders` rewrites a bare `?` only when followed by whitespace/`)`/`,`/end, and here it is followed by `::jsonb`. Knex turns the surviving `?` into `$1`, and EXPLAIN rejects an unbound `$1` (`there is no parameter $1`).
So the extracted form is not the executed form: the statement plans in production; the gate's placeholder substitution just does not model the `?::<type>` cast shape. What the other ~53 clean statements do differently is not use a `?` binding at all. This is a gate substitution gap to RECORD (adjudicate per site), not a league code defect — do not fix the `.mjs`. The deeper, optional gate fix (teach `substitute_placeholders` the `?::<type>` shape) is a gate change, out of this task's scope; if a future knex site uses the same idiom the finding recurs.

# Plan

- [x] Clear `call-site-param-contracts` (`87daf86c8`, `738923b06`).
- [x] Clear `██████████████████████` (`a0a69c519`).
- [x] Remove the resolved `prop-edge-finder.sql` ruling and repoint the moved `player.position` adjudication (`da5bec4c7`) — 13 findings to 7.
- [x] Diagnose all 7 remaining findings (see sections above — each established per site, three against a live scratch DB).
- [x] Apply adjudications: 3x `not_a_column` (api.md, projection-period-encoding x2), 1x `not_a_league_statement` + doc repair for `analyze-game-for-selections.md` (`summary` -> `body`), 1x temp-table ruling for `population-levels.mjs`, 2x gate-model ruling for `propagate-prop-market-identity.mjs`.
- [x] Repair the `feed_items` workflow SQL in `workflow/nfl/betting/analyze-game-for-selections.md` — `summary` is a dead column even against content-feed; `body` is the real raw-body column.
- [x] Re-run `yarn check:cluster` and require exit 0.

# Notes

- Run the gate as `yarn check:cluster > /tmp/cluster.txt 2>&1` and read the verdict table near the top. The tail of the output is dominated by already-adjudicated entries printing their full rationale, and reading the tail alone gives a badly wrong impression of what is failing.
- This shell exports `DEBUG=1`, which `enable_debug_namespaces` treats as an authoritative override. Since `1` matches no namespace every log line goes dark and a long run leaves an empty log with exit 0. Set `DEBUG=<namespace>` explicitly on any script run.
- The gate was confirmed red before the season-grain advanced stat table work and is unrelated to it; that session ran the control.
