Skip to content

Commit 93b6902

Browse files
ashuaibi7claude
andcommitted
Add rebuttal analysis scripts (network extraction, over-correction, figures)
New analysis tooling from the BMR / burden-aware + literature-validation work: - extract_consensus_networks.py: top-10 ME/CO pairs per cohort x BMR -> consensus JSON - group_networks_by_cancer_type.py: group 69 cohorts into cancer-type families, tag OncoKB drivers, classify pairs (dd/dp/pp) - bmr_overcorrection_check.py: LAML lambda obs/exp, low-TMB CO scan, TMB-vs-CO table - plot_burden_aware.py, plot_co_clusters.py: rebuttal figures - _lit_workflow_template.js: literature-validation workflow template Adds S108 to the analysis/* ruff per-file-ignores (intentional /tmp scratch paths). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 4c90672 commit 93b6902

7 files changed

Lines changed: 926 additions & 1 deletion

analysis/_lit_workflow_template.js

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
export const meta = {
2+
name: 'dialect-lit-validation',
3+
description: 'Validate DIALECT ME/CO driver-interaction networks against the biological literature across 34 cancer-type groups (find -> adversarially verify -> synthesize)',
4+
phases: [
5+
{ title: 'Search', detail: 'one literature-search agent per cancer-type group' },
6+
{ title: 'Verify', detail: 'adversarial re-check of established/emerging pairs' },
7+
{ title: 'Synthesize', detail: 'method-recovery + novel-discovery + concordance report' },
8+
],
9+
}
10+
11+
// ---- payload injected at generation time (per cancer-type group: cohorts + ME/CO pairs) ----
12+
const GROUPS = /*__PAYLOAD__*/;
13+
14+
// Background DIALECT framing handed to every agent so it judges direction correctly.
15+
const FRAMING = `DIALECT is an EM model that, after subtracting a per-gene/per-sample passenger
16+
background mutation rate (BMR), estimates latent DRIVER mutation status for each gene and then fits a
17+
bivariate-Bernoulli interaction (tau) between gene pairs. It reports:
18+
- ME (mutually exclusive): the two genes' DRIVER mutations co-occur in the same tumor far LESS than
19+
chance (negative correlation rho<0). Biologically this usually means functional redundancy /
20+
same-pathway epistasis (one hit suffices) or different molecular subtypes.
21+
- CO (co-occurring): the two genes' driver mutations co-occur MORE than chance (rho>0). Biologically
22+
this usually means cooperation/synergy, a shared subtype, or a defined genomic context.
23+
Gene symbols may carry _M (missense/in-frame) or _N (truncating/nonsense) effect suffixes; here they
24+
are collapsed to the base gene symbol. BMR support codes: c=CBaSE, d=DIG, m=per-sample MutSig2CV.
25+
A pair supported by 'm' is robust to per-sample tumor-burden confounding (the strongest evidence it is
26+
not a hypermutation artifact); a CO pair seen ONLY under 'c' in a high-burden cohort is the most
27+
suspect.`;
28+
29+
const FINDER_SCHEMA = {
30+
type: 'object',
31+
additionalProperties: false,
32+
properties: {
33+
group: { type: 'string' },
34+
findings: {
35+
type: 'array',
36+
items: {
37+
type: 'object',
38+
additionalProperties: false,
39+
properties: {
40+
pair: { type: 'string', description: 'GENE_A:GENE_B exactly as given' },
41+
observed: { type: 'string', enum: ['ME', 'CO'], description: 'direction DIALECT called' },
42+
status: {
43+
type: 'string',
44+
enum: ['established', 'emerging', 'novel', 'contradicted', 'artifact'],
45+
description: 'established=textbook/multiple studies; emerging=some evidence; novel=biologically plausible but little/no prior report; contradicted=literature shows the OPPOSITE direction; artifact=one or both genes are not credible drivers in this tumor (likely passenger/FLAGS gene)',
46+
},
47+
lit_direction: { type: 'string', enum: ['ME', 'CO', 'both', 'none'] },
48+
concordant: { type: 'boolean', description: 'does DIALECT observed direction match the literature direction?' },
49+
mechanism: { type: 'string', description: 'one sentence (<=200 chars) on the biology' },
50+
citations: {
51+
type: 'array',
52+
description: 'up to 3 real peer-reviewed sources; empty if novel/artifact',
53+
items: {
54+
type: 'object',
55+
additionalProperties: false,
56+
properties: {
57+
ref: { type: 'string', description: 'first-author + journal + year, <=120 chars' },
58+
identifier: { type: 'string', description: 'PMID, DOI, or URL' },
59+
year: { type: 'integer' },
60+
},
61+
required: ['ref'],
62+
},
63+
},
64+
confidence: { type: 'string', enum: ['high', 'medium', 'low'] },
65+
},
66+
required: ['pair', 'observed', 'status', 'lit_direction', 'concordant', 'mechanism', 'confidence'],
67+
},
68+
},
69+
},
70+
required: ['group', 'findings'],
71+
}
72+
73+
const VERIFIER_SCHEMA = {
74+
type: 'object',
75+
additionalProperties: false,
76+
properties: {
77+
group: { type: 'string' },
78+
verdicts: {
79+
type: 'array',
80+
items: {
81+
type: 'object',
82+
additionalProperties: false,
83+
properties: {
84+
pair: { type: 'string' },
85+
claim_holds: { type: 'boolean', description: 'does the cited evidence really support the claimed status+direction?' },
86+
final_status: { type: 'string', enum: ['established', 'emerging', 'novel', 'contradicted', 'artifact', 'unverifiable'] },
87+
final_direction: { type: 'string', enum: ['ME', 'CO', 'both', 'none'] },
88+
best_citation: { type: 'string', description: 'the single strongest verified citation w/ identifier, or "" if none verifiable' },
89+
reason: { type: 'string', description: 'why the claim held or failed (<=240 chars)' },
90+
},
91+
required: ['pair', 'claim_holds', 'final_status', 'final_direction', 'reason'],
92+
},
93+
},
94+
},
95+
required: ['group', 'verdicts'],
96+
}
97+
98+
function finderPrompt(g) {
99+
const cohorts = g.cohorts.map(c => `${c.c} (N=${c.n}, medianTMB=${c.tmb})`).join('; ');
100+
const fmt = arr => arr.map(p => `${p[0]} [${p[1]},bmr=${p[2]},nCohorts=${p[3]}]`).join('\n ');
101+
return `You are a cancer-genomics literature analyst. Validate DIALECT's predicted driver-gene
102+
interaction network for the cancer-type group "${g.group}" against the PEER-REVIEWED biological literature.
103+
104+
${FRAMING}
105+
106+
CONSTITUENT COHORTS: ${cohorts}
107+
108+
DIALECT predicted MUTUALLY-EXCLUSIVE (ME) pairs:
109+
${fmt(g.ME) || '(none)'}
110+
111+
DIALECT predicted CO-OCCURRING (CO) pairs:
112+
${fmt(g.CO) || '(none)'}
113+
114+
(Tag legend: class dd=both OncoKB cancer genes, dp=one non-OncoKB partner. bmr letters c/d/m as defined above. nCohorts = how many cohorts in this group recovered the pair.)
115+
116+
TASK: For EVERY pair listed, determine what the literature says about that gene pair IN THIS CANCER TYPE
117+
(or closely related context). Use web search to find real evidence — load it first if needed via
118+
ToolSearch query "select:WebSearch,WebFetch", then run multiple targeted WebSearch queries
119+
(e.g. "GENE_A GENE_B mutual exclusivity <cancer>", "GENE_A GENE_B co-occurrence co-mutation <cancer>",
120+
"GENE_A GENE_B <cancer> pathway"). Prefer TCGA marker papers, cBioPortal/MSK studies, COSMIC,
121+
pathway/epistasis papers, and reviews. Fetch a source to confirm when a snippet is ambiguous.
122+
123+
For each pair decide:
124+
- status (established / emerging / novel / contradicted / artifact),
125+
- lit_direction (what direction the literature supports: ME, CO, both, or none),
126+
- concordant (does DIALECT's observed direction match the literature?),
127+
- a one-sentence mechanism, and up to 3 REAL citations with PMIDs/DOIs (NEVER fabricate an identifier;
128+
if you cannot find a real source, leave citations empty and lower the status to novel or artifact).
129+
130+
Be skeptical and calibrated: only "established" if you can name concrete supporting literature.
131+
If a pair's biology is well known to go the OTHER way than DIALECT called it, mark contradicted
132+
(this is a valuable finding). Return ALL pairs. Your structured output IS the result.`;
133+
}
134+
135+
function verifierPrompt(g, toCheck) {
136+
const lines = toCheck.map(f =>
137+
`- ${f.pair} | DIALECT=${f.observed} | claimed status=${f.status}, lit_dir=${f.lit_direction}, concordant=${f.concordant} | mechanism="${f.mechanism}" | cites=${JSON.stringify(f.citations || [])}`
138+
).join('\n');
139+
return `You are an ADVERSARIAL fact-checker for a cancer-genomics rebuttal. Another analyst claimed the
140+
following gene-pair interactions in "${g.group}" are supported by the literature. Independently verify
141+
each one. Default to skepticism: if you cannot confirm a real source supports the claimed STATUS and
142+
DIRECTION, mark claim_holds=false and set final_status to "unverifiable" (or "artifact"/"novel"/"contradicted"
143+
as appropriate).
144+
145+
${FRAMING}
146+
147+
CLAIMS TO CHECK:
148+
${lines}
149+
150+
For EACH claim: load web search if needed (ToolSearch "select:WebSearch,WebFetch"), run your OWN searches,
151+
and (a) confirm the cited identifier (PMID/DOI) actually exists and is about this gene pair in this/related
152+
cancer, and (b) confirm the literature direction (ME vs CO) matches what was claimed. Watch specifically for:
153+
fabricated PMIDs/DOIs, citations that are about a different cancer, and direction errors (claimed CO but the
154+
genes are actually mutually exclusive, or vice versa). Output a verdict per pair with the single strongest
155+
VERIFIED citation (or "" if none). Your structured output IS the result.`;
156+
}
157+
158+
// ---------- Phase 1+2: per-group find -> adversarially verify (pipeline, no barrier) ----------
159+
const perGroup = await pipeline(
160+
GROUPS,
161+
g => agent(finderPrompt(g), { label: `find:${g.group}`, phase: 'Search', schema: FINDER_SCHEMA }),
162+
(found, g) => {
163+
if (!found) return { group: g.group, found: null, verdicts: [] };
164+
const toCheck = found.findings.filter(f => f.status === 'established' || f.status === 'emerging' || f.status === 'contradicted');
165+
if (toCheck.length === 0) return { group: g.group, found, verdicts: [] };
166+
return agent(verifierPrompt(g, toCheck), { label: `verify:${g.group}`, phase: 'Verify', schema: VERIFIER_SCHEMA })
167+
.then(v => ({ group: g.group, found, verdicts: (v && v.verdicts) || [] }));
168+
},
169+
);
170+
171+
// ---------- merge finder + verifier into one master table ----------
172+
const master = [];
173+
for (const r of perGroup.filter(Boolean)) {
174+
if (!r.found) continue;
175+
const vmap = {};
176+
for (const v of r.verdicts) vmap[v.pair] = v;
177+
for (const f of r.found.findings) {
178+
const v = vmap[f.pair];
179+
master.push({
180+
group: r.group,
181+
pair: f.pair,
182+
observed: f.observed,
183+
status: v ? v.final_status : f.status,
184+
lit_direction: v ? v.final_direction : f.lit_direction,
185+
concordant: f.concordant,
186+
verified: v ? v.claim_holds : (f.status === 'novel' || f.status === 'artifact'),
187+
confidence: f.confidence,
188+
mechanism: f.mechanism,
189+
citation: v && v.best_citation ? v.best_citation
190+
: (f.citations && f.citations[0] ? `${f.citations[0].ref} ${f.citations[0].identifier || ''}`.trim() : ''),
191+
});
192+
}
193+
}
194+
log(`master table: ${master.length} validated pair-findings across ${perGroup.filter(Boolean).length} groups`);
195+
196+
// compact views for the synthesis agents
197+
const established = master.filter(m => (m.status === 'established' || m.status === 'emerging') && m.verified);
198+
const novel = master.filter(m => m.status === 'novel' && (m.confidence === 'high' || m.confidence === 'medium'));
199+
const discordant = master.filter(m => m.status === 'contradicted' || m.concordant === false);
200+
const compact = arr => arr.map(m => `${m.group} | ${m.pair} | DIALECT=${m.observed} | status=${m.status} | litdir=${m.lit_direction} | conf=${m.confidence} | ${m.mechanism} | ${m.citation}`).join('\n');
201+
202+
// ---------- Phase 3: synthesis (3 independent sections, in parallel) ----------
203+
phase('Synthesize');
204+
const STORY_CONTEXT = `This is for a PLOS Comp Biol major-revision rebuttal. The central reviewer critique was that
205+
DIALECT's co-occurrence (CO) calls were inflated by background-mutation-rate (BMR) / hypermutator confounding.
206+
Key findings already established by the authors: (1) a proper per-(gene,sample,context) BMR extracted from a
207+
patched MutSig2CV collapses spurious CO in high-tumor-burden cohorts (e.g. UCEC CO 4850->~300); (2) BUT in
208+
LOW-burden cohorts that same per-sample BMR OVER-corrects and erases REAL biology — e.g. in AML it inflates the
209+
DNMT3A background so much (observed/expected ~1.35) that it deletes the canonical DNMT3A:FLT3 / DNMT3A:IDH1
210+
co-occurrences that CBaSE and DIG both recover; (3) this motivates a burden-aware BMR choice: per-sample MutSig
211+
for high-TMB cohorts, per-gene CBaSE/DIG for low-TMB. The literature validation below tests whether DIALECT's
212+
ME/CO networks recover known cancer biology (method validation) and surface credible novel interactions.`;
213+
214+
const synth = await parallel([
215+
() => agent(`${STORY_CONTEXT}
216+
217+
You are writing the METHOD-RECOVERY section of the rebuttal's literature-validation appendix. Below are the
218+
gene-pair interactions DIALECT predicted that are SUPPORTED by the literature and survived adversarial
219+
verification. Write a tight, well-organized markdown section that demonstrates DIALECT recovers established
220+
cancer biology. Organize by cancer-type group; for each, give a one-line summary then a bullet list of the
221+
strongest recovered ME and CO pairs with their mechanism and a citation. Lead with the most famous textbook
222+
recoveries (e.g. lung KRAS/EGFR mutual exclusivity, PDAC KRAS:TP53:SMAD4:CDKN2A co-occurrence, glioma
223+
IDH1:TP53:ATRX, AML DNMT3A:FLT3:NPM1, colorectal APC:KRAS:TP53). Note where a CO pair is robust to per-sample
224+
MutSig (bmr code includes m) as the strongest anti-confounding evidence. Be precise and do not invent pairs
225+
not in the list.
226+
227+
VERIFIED ESTABLISHED/EMERGING PAIRS:
228+
${compact(established)}`, { label: 'synth:recovery', phase: 'Synthesize' }),
229+
230+
() => agent(`${STORY_CONTEXT}
231+
232+
You are writing the NOVEL-CANDIDATES section. Below are biologically plausible gene-pair interactions DIALECT
233+
predicted for which there is little/no prior published report (status=novel) at medium/high confidence. Write a
234+
markdown section highlighting the most interesting candidates as discovery opportunities. Group by cancer type,
235+
prioritize driver-driver pairs and pairs recovered in multiple cohorts or robust to per-sample MutSig. For each,
236+
give the observed direction (ME/CO), a plausible mechanistic hypothesis, and why it is worth experimental or
237+
cohort follow-up. Be honest that these are hypotheses. Do not invent pairs not in the list.
238+
239+
NOVEL CANDIDATE PAIRS:
240+
${compact(novel)}`, { label: 'synth:novel', phase: 'Synthesize' }),
241+
242+
() => agent(`${STORY_CONTEXT}
243+
244+
You are writing the DIRECTION-CONCORDANCE & CAVEATS section. Below are pairs where DIALECT's ME/CO call is
245+
DISCORDANT with the literature, or was flagged contradicted. Write a markdown section that (1) honestly catalogs
246+
the discordances by cancer type with the likely cause (residual BMR confounding, subtype mixing, or a genuine
247+
novel direction), (2) connects them to the burden-aware BMR story (which discordant CO calls appear only under
248+
CBaSE in high-TMB cohorts vs survive per-sample MutSig), and (3) gives a short, fair assessment of the overall
249+
concordance rate as evidence the method is well-calibrated when the right BMR is used. Do not invent pairs.
250+
251+
DISCORDANT / CONTRADICTED PAIRS:
252+
${compact(discordant)}`, { label: 'synth:concordance', phase: 'Synthesize' }),
253+
]);
254+
255+
return {
256+
counts: {
257+
total: master.length,
258+
established_verified: established.length,
259+
novel: novel.length,
260+
discordant: discordant.length,
261+
groups: perGroup.filter(Boolean).length,
262+
},
263+
master,
264+
sections: {
265+
recovery: synth[0],
266+
novel: synth[1],
267+
concordance: synth[2],
268+
},
269+
};

0 commit comments

Comments
 (0)