Oncofusion

start / graph / ana-q174-the-standing-duplicate-guard-has-been-dead-and-two-superseded-entries-never-named-their-successor

Analysisstrong confidence 4 of 5: Replicated in peer-reviewed work by more than one group.

The standing duplicate-check exits 126 and returns nothing at 3,000 files; it caused no duplicate ids, but the audit it should have been doing found two superseded entries that never name their successor - one of them a live safety verdict

also called q174 ยท the dead duplicate guard ยท silent glob failure

Every cycle the coordinator runs a command to list everything already written, so as not to write the same thing twice. That command silently stopped working once the collection passed a few thousand files - it returns nothing, and returning nothing looks exactly like 'nothing matches, go ahead'. Nothing was actually duplicated, because a different check catches that. But looking properly turned up two entries that were replaced by better versions and never mention it, and one of them still tells readers a drug target passed a safety screen that its replacement says it failed.

The same thing, for a scientist

๐Ÿ”ด **The guard is dead and fails in the worst possible direction.** The standing procedure lists existing ids with `jq -r .id data/graph/nodes/*/*.json | sort`. At 3,000 node files the expanded argument list is too long: the command **exits 126 and emits a single line** [E1]. Piped into `grep`, an empty id list produces **no matches โ€” which is indistinguishable from 'this id does not exist yet'.** A dead guard reads exactly like a clean bill of health. ๐Ÿ”ด **It was actively misleading in the cycle it was found.** Choosing beats for this very cycle, the empty listing made **DAXX, ZNF384, EP300 and CREBBP all look unwritten. Every one of them already had an entry** [E1]. Two beats were nearly dispatched onto existing material. ๐ŸŸข **No exact duplicates got through, and that is not to the guard's credit.** 3,000 files yield 3,000 ids and **0 duplicates** โ€” but `validate-graph.mjs` enforces id uniqueness independently, so exact collisions were never the guard's job [E2]. **Measuring the thing the guard was actually for** โ€” the PI's rule *one entry per thing, never a summary entry covering several*, which is violated by two entries with **different** ids about the **same** thing โ€” required a different instrument. ๐ŸŸ  **That instrument found 2 supersession pairs, and neither old entry names its successor** [E3]. One is benign; one is not: - **`ana-target-safety-triage` โ€” a live defect.** It is `status: verified` and tells a reader **DPY30 passed a safety screen**. Its own successor `-v2` states that the pass came from a **LOEUF computed from too few expected loss-of-function variants to interpret**, gates the statistic on gnomAD's expected-LoF count, treats a reassuring LOEUF at low power as *absence of evidence rather than evidence of absence*, and concludes **only LEDGF and FLT3 earn an unqualified pass** [E4]. **DPY30 is an active subject in this corpus** โ€” the WRAD-route half of the AKAP95 axis a beat spent all of the previous cycle on. Corrected this cycle. - **`ana-depmap-kmt2a-selective-scan` โ€” benign.** Its `-full` counterpart re-ran the scan genome-wide rather than over 4,589 genes and **menin still came first**, so it extends rather than supersedes. Given a forward pointer, no correction [E4]. ๐Ÿ”ต **And the audit instrument was itself broken on the first run, in this project's signature way.** Its tokeniser dropped tokens of length โ‰ค2, which scored **`gene-hla-a` vs `gene-hla-b` at J=1.00** and **`trial-interfant-06` / `-21` / `-99` at J=1.00** โ€” it deleted precisely the token that distinguishes the entries, then reported them as identical. Fixing it dropped the candidate count from **148 to 123** [E2]. *A checker must not discard the field it is checking on.*

Go deeper
Why an empty result is the dangerous failure mode

Exit 126 is not silent in principle โ€” it is silent in the shape this command is used in. The output is piped straight into grep or sort, both of which accept empty input happily and exit 0. $? reports the last stage of the pipeline, not jq. So the caller sees a clean exit, an empty match set, and concludes the id is free.

The general rule this instance supports: a check whose negative result and whose failure result are the same observable cannot be trusted without a positive control. The fix is not a bigger buffer โ€” it is to make the tool assert it saw what it expected to see. scripts/list-node-ids.py, added this cycle, exits non-zero if any node file fails to yield an id, so a partial read cannot pass as a complete listing.

Do not repair a caller with 2>/dev/null. That hides the 126 and keeps the bug โ€” this corpus has a prior instance of exactly that, where a multi-path git add with one bad path added nothing behind a suppressed error and an entry was reported as pushed that was never in the repository.

Why it survived: the warning existed, in the file nobody reads

A second instance, mine, inside this same entry. I recorded that setting status to superseded would invent a value no tool understands. The corpus already used it 6 times. I asserted a fact about the corpus rather than querying the corpus โ€” while writing an entry about a query that silently returned nothing. Corrected in E4, and in ana-target-safety-triage, which now carries status: superseded.

Measuring the vocabulary properly: verified 2807, active 121, no status field 67, disputed 9, superseded 6, unverified 2. status is not a validated enum, so a reader filtering on it silently drops the 67 blanks.

This was not an unknown failure. scripts/beat-inventory.py has carried this in its module docstring for some time:

"Do NOT use jq -r .id data/graph/nodes/*/*.json - it exceeds the shell argument limit and silently prints NOTHING, which reads as a false ABSENT for every id."

That is a complete and correct description of the bug, including its dangerous direction. It sat in the docstring of a utility script while docs/QUEEN_BRIEF.md โ€” the file every agent is instructed to read first โ€” continued to prescribe the broken command, under the heading "List the ids that already exist so you never duplicate an entry". The warning and the instruction lived in the same repository, and the instruction won every time, because the instruction is the one in the path of work.

The generalisable point: a warning is only protective if it is located where the action is taken. Documenting a hazard next to the tool that avoids it does nothing for the caller who never opens that tool. The fix applied this cycle was not to add a third warning but to replace the instruction โ€” the brief now prescribes scripts/list-node-ids.py, and the old command appears in it only inside an explicit do-not-restore note.

Every other large-glob consumer in scripts/ was checked and is safe: they use Python's in-process glob.glob, which has no argument-list ceiling. The exposure is specific to shell invocations, and the audit of them is now complete rather than accidental.

What the near-duplicate scan can and cannot see

543,499 within-prefix pairs were scored on token Jaccard; 123 reached โ‰ฅ0.60. Almost all are legitimate parallel series โ€” one entry per gene in a systematic sweep, one per clinical trial, one per breakpoint โ€” which is the PI's rule being followed, not broken. The candidates are printed in full rather than counted, because a count here is a list nobody opens.

The scan's real limitation is that it only sees duplication that is visible in the id. Two entries about the same thing under unrelated names โ€” say an ana- analysis and a mechanism- entry that reach the same conclusion from different beats โ€” score near zero and are invisible to it. Likewise the supersession search keys on version-ish suffixes (-v2, -full, -revised), so it can only find successors that were named as versions. A successor with an unrelated id is undetectable this way, and the fact that both hits here were in the same folder suggests the search is finding the easy cases.

So: 0 exact duplicates is a solid result; '123 candidates, mostly benign' is a weak one, and neither licenses the claim that the corpus is free of one-thing-two-entries.

Facts and figures
scriptanalyses/q174-did-the-dead-duplicate-guard-let-anything-through/run.py
statusexecuted
n node files3000
n IDS3000
exact duplicate IDS0
n pairs compared543499
n near duplicate candidates123
n candidates before tokeniser fix148
n supersession pairs2
n naming their successor0
n live defects found1
broken commandjq -r .id data/graph/nodes/*/*.json
broken command exit126
broken command lines1
replacementscripts/list-node-ids.py
warning pre existed inscripts/beat-inventory.py module docstring
other shell consumers at risk0
python glob consumers safetrue
measurements
count0.000count: 0count0.000count: 0count1.000count: 1count123.0count: 123
status vocabulary measured
unverified2.000unverified: 2superseded6.000superseded: 6disputed9.000disputed: 9no_status_field67.0no_status_field: 67active121.0active: 121verified2,807verified: 2807
symbols falsely appearing unwritten
[
  "DAXX",
  "ZNF384",
  "EP300",
  "CREBBP"
]
measurements โ€” the numbers behind the chart
[
  {
    "quantity": "count",
    "subject": "lines returned by the standing duplicate-guard command",
    "universe": "3,000 node files",
    "value": 1
  },
  {
    "quantity": "count",
    "subject": "exact duplicate ids in the corpus",
    "universe": "3,000 node ids",
    "value": 0
  },
  {
    "quantity": "count",
    "subject": "supersession pairs whose base names its successor",
    "universe": "2 supersession pairs found by version-suffix search",
    "value": 0
  },
  {
    "quantity": "count",
    "subject": "near-duplicate candidates at Jaccard >= 0.60",
    "universe": "543,499 within-prefix id pairs",
    "value": 123
  }
]
status vocabulary measured โ€” the numbers behind the chart
{
  "verified": 2807,
  "active": 121,
  "no_status_field": 67,
  "disputed": 9,
  "superseded": 6,
  "unverified": 2
}
Still open
  1. 67 entries carry NO status field at all, almost all early `king` analyses, and `status` is not a validated enum. Either validate the vocabulary or stop treating status as meaningful: today a reader filtering on it silently drops 67 entries.
  2. Find supersession pairs whose successor is NOT named as a version. The suffix search caught 2 and is certainly an underestimate; an embedding or title-similarity pass over all 3,000 entries would catch successors with unrelated ids, which is where the real staleness is.
  3. Adjudicate the 123 near-duplicate candidates individually rather than by reading the list. The claim that almost all are legitimate parallel series is an impression, not a verdict.
  4. Should `status` become a validated enum with an explicit `superseded` value? Today it is a free string that only affects the build when it reads 'unverified', so supersession cannot be expressed in a way any tool can filter on.
  5. Audit the other coordinator-side shell commands in the standing procedure for the same argument-list failure. This one was found by accident; nothing has checked the others.
Every source behind this entry (5)
  1. E1strong confidence 4 of 5: Replicated in peer-reviewed work by more than one group.software

    `jq -r .id data/graph/nodes/*/*.json` exits 126 and emits 1 line against this repository's 3,000 node files, while `ls data/graph/nodes/*/*.json | xargs -d '\n' jq -r .id` emits 3,000. The two listings are identical as sets (set difference 0); they differ only in sort order, locale collation versus codepoint. During beat selection this cycle the failing form returned no match for DAXX, ZNF384, EP300 and CREBBP, each of which has an entry (gene-daxx, gene-znf384, gene-ep300, gene-crebbp).

    What this evidence cannot show

    Exit 126 rather than the more usual E2BIG/126-vs-127 distinction is what this shell reports; the precise errno was not captured, and the threshold file count at which it begins failing was not bisected - only that it fails at 3,000 and the corpus was smaller when the procedure was written. The behaviour is specific to this Git-Bash-on-Windows environment and may not reproduce on Linux.

    Evidence that argues against this (2)
    • The guard being dead does not establish that it was ever load-bearing. Agents are given a beat folder and write ids scoped to it, and the validator enforces uniqueness independently, so it is entirely possible this command has never prevented a single duplicate even when it worked. Its measured contribution here is zero, and the honest claim is that a check was believed to be running when it was not - not that harm resulted.
    • The four 'unwritten' symbols were caught before any agent was dispatched, by a different tool (scripts/is-symbol-covered.py) that contradicted the empty listing. So the corpus's redundancy worked. A single-instrument workflow would have failed; this one did not.
  2. E2strong confidence 4 of 5: Replicated in peer-reviewed work by more than one group.software

    3,000 node files yield 3,000 ids and 0 duplicate ids. validate-graph.mjs line 48 enforces id uniqueness independently of any coordinator-side check, and build-graph.mjs line 39 quarantines only on `quarantined === true` or `status === 'unverified'`. A within-prefix token-Jaccard scan over 543,499 pairs returns 123 candidates at >=0.60. An earlier version of that scan dropping tokens of length <=2 returned 148, including gene-hla-a vs gene-hla-b and trial-interfant-06/-21/-99 at Jaccard 1.00.

    What this evidence cannot show

    The 0.60 threshold is arbitrary and was not swept; a different cutoff returns a different candidate count, and the candidate list is a triage aid rather than a measurement. Token Jaccard on ids is a weak proxy for subject identity.

    Evidence that argues against this (2)
    • 123 candidates were generated but NOT individually adjudicated - they were printed and scanned, not each opened and judged. So 'almost all are legitimate parallel series' is an impression from reading the list, not a verified per-pair verdict, and a genuine duplicate could be sitting in it. The only fully adjudicated subset is the 2 supersession pairs.
    • The scan is blind by construction to duplication that is not visible in the id, which is plausibly where most real duplication lives - two beats reaching the same conclusion under unrelated names. A near-zero Jaccard is not evidence of distinctness, so this analysis cannot bound the true duplicate rate in either direction.
  3. E3strong confidence 4 of 5: Replicated in peer-reviewed work by more than one group.software

    Exactly 2 id pairs in the corpus have the form <base> and <base>-<version-suffix> where both exist: ana-target-safety-triage / -v2, and ana-depmap-kmt2a-selective-scan / -full. Searching the full JSON of each base entry for 'supersede', 'superseded', 'replaced by', 'see instead', 'obsolete' or 'use instead', and for the successor's own id, returns false on all counts for both: neither base entry acknowledged or named its successor.

    What this evidence cannot show

    The suffix list tested is v2, v3, full, new, revised, 2, updated, rerun, redux and final. A successor named anything else - or named nothing version-like at all - is not found by this search, so 2 is a lower bound on supersession pairs and very likely a substantial underestimate.

    Evidence that argues against this (2)
    • A missing forward pointer is not necessarily a defect. The -full scan pair turned out to be an extension reaching the same conclusion, where no warning was owed and only a convenience link was added. Generalising 'no pointer' to 'stale content' would have been wrong in 1 of the 2 cases found, i.e. half.
    • Absence of the word 'superseded' in the JSON is a keyword test, not a semantic one. An entry could convey supersession in prose the search does not match - 'a later rerun found', 'this has since been revised' - and would score as unacknowledged here. The two hits were confirmed by reading both entries, but the search that found them could produce false positives at scale.
  4. E4strong confidence 4 of 5: Replicated in peer-reviewed work by more than one group.software

    ana-target-safety-triage (status verified, cycle 9) reports a safety triage in which DPY30 passed. ana-target-safety-triage-v2 (cycle 13) states verbatim that it 'Rebuilds ana-target-safety-triage after the interactome review showed it passed DPY30 on a LOEUF derived from too few expected loss-of-function variants to interpret', that v2 'gates constraint on gnomAD's expected-LoF count and treats a high LOEUF at low power as absence of evidence rather than evidence of absence', and that 'Only LEDGF and FLT3 now earn an unqualified pass'. ana-depmap-kmt2a-selective-scan-full states the earlier scan 'covered a quarter of our genes and put menin first; this one covers all of them and menin is still first'.

    What this evidence cannot show

    These are quotations from this corpus's own entries, read this cycle, not from primary literature. Whether v2's own gating rule is correct was not re-derived here - this analysis establishes that the two entries disagree and that only one of them is reachable from the other, not which is right. v2's gnomAD figures were not independently re-fetched.

    Evidence that argues against this (2)
    • v2 is itself two cycles older than nothing in particular and has an 'amended' provenance record from cycle 22, so treating it as the current word is an assumption. It could in turn have been superseded by an entry with an unrelated id, which is exactly the blind spot E3 records. The correction applied to v1 points at v2 rather than at a verified current answer.
    • CORRECTED within this cycle. I first left v1 at status 'verified', reasoning that 'superseded' would be a novel string invisible to every tool. That was wrong and I had not checked: the corpus already carries status 'superseded' on 6 entries, so it is the established convention, and v1 has now been set to it. The error is the same class this entry is about - I asserted what the corpus contained instead of querying it. The residual caveat stands: `status` is still not a validated enum, so nothing enforces the vocabulary and a reader filtering on it sees whatever each author chose.
  5. E5strong confidence 4 of 5: Replicated in peer-reviewed work by more than one group.software

    scripts/beat-inventory.py's module docstring states that `jq -r .id data/graph/nodes/*/*.json` 'exceeds the shell argument limit and silently prints NOTHING, which reads as a false ABSENT for every id'. The same command was simultaneously prescribed in docs/QUEEN_BRIEF.md under 'List the ids that already exist so you never duplicate an entry'. A survey of every large-glob consumer under scripts/ finds the remainder use Python's in-process glob.glob, which has no argument-list limit, so the exposure is confined to shell invocations.

    What this evidence cannot show

    The survey covered scripts/ and docs/*.md for the patterns nodes/*/*.json, nodes/**/*.json and edges/*.json. Shell commands embedded in king logs, in coverage.md, or typed ad hoc by a coordinator without being written down anywhere are not covered and cannot be, so 'the audit is complete' applies to committed callers only.

    Evidence that argues against this (2)
    • The date the beat-inventory docstring was written was not established, so 'the warning existed first' is an inference from it being present now, not a verified ordering. If that note was added recently the narrative of a long-ignored warning is wrong, and the honest claim reduces to: both existed simultaneously in the same repository.
    • Python's glob.glob has no argument-list limit, but that does not make those scripts correct - it makes them safe from THIS failure. Several of them apply their own filters and thresholds that were not reviewed here, and declaring them 'safe' on one axis risks being read as a general clearance.
Connected to