JuliusBrussee/caveman · error · Error
${where} diagnosis upstream node ${nodeID} is not an upstrea
Error message
${where} diagnosis upstream node ${nodeID} is not an upstream node of its causal slice What it means
Thrown when a node in diagnosis.root_cause_hypothesis.upstream_node_ids is either absent from causal_slice.node_chain or equals the slice's manifestation_node_id. Upstream nodes are the hypothesized causes; by definition they must sit earlier in the chain than the manifestation and must not be the manifestation itself.
Source
Thrown at packages/shared/contracts/scripts/validate-continuous-improvement.mjs:209
if (!provenDependency(edge)) throw new Error(`${where} hard chain carries an unproven edge ${edge.from}->${edge.to}`);
if (!sliceNodes.has(edge.from) || !sliceNodes.has(edge.to)) throw new Error(`${where} hard chain edge ${edge.from}->${edge.to} names a node outside variant ${sliceVariant.id}`);
if (!slice.node_chain.includes(edge.from) || !slice.node_chain.includes(edge.to)) {
throw new Error(`${where} hard chain edge ${edge.from}->${edge.to} is outside the slice's node chain`);
}
const source = variantEdges.get(`${edge.from} ${edge.to} ${edge.type}`);
if (!source || source.strength !== "hard") throw new Error(`${where} hard chain edge ${edge.from}->${edge.to} is not a hard edge of variant ${sliceVariant.id}`);
}
for (const edge of slice.unproven_adjacencies) {
// The whole point of the separate list: nothing in it may read as proven.
if (provenDependency(edge)) throw new Error(`${where} unproven adjacency ${edge.from}->${edge.to} is in fact a proven hard dependency`);
if (slice.hard_edges.some((hard) => hard.from === edge.from && hard.to === edge.to && hard.type === edge.type)) {
throw new Error(`${where} adjacency ${edge.from}->${edge.to} appears in both the hard chain and the unproven list`);
}
}
const upstream = new Set(item.diagnosis.root_cause_hypothesis.upstream_node_ids);
for (const nodeID of upstream) {
if (!slice.node_chain.includes(nodeID) || nodeID === slice.manifestation_node_id) {
throw new Error(`${where} diagnosis upstream node ${nodeID} is not an upstream node of its causal slice`);
}
}
// A case that proposes nothing must carry nothing: no operator, no eval
// pack, no replay. Anything else would present an abstention as a proposal.
if (item.status === "diagnosis_only") {
if (item.change_set.operator_id !== "" || item.eval_pack.id !== "" || item.replay.status !== "not_run") {
throw new Error(`${where} is diagnosis-only but carries a change set, eval pack or replay`);
}
continue;
}
if (item.change_set.case_id !== item.id || item.eval_pack.case_id !== item.id) {
throw new Error(`${where} change set or eval pack is bound to another case`);
}
// The case must investigate the pair its own opportunity named. A cohort
// built from a differently-derived pair would describe one comparison and
// measure another.View on GitHub (pinned to 27d5a3981a)
Solutions
- Remove or fix upstream ids that are not in slice.node_chain — every entry must be a chain member other than manifestation_node_id.
- If a genuinely causal node was dropped from the chain, add it back to node_chain (it must be a node of the slice's variant).
- Delete manifestation_node_id from upstream_node_ids if it was listed by mistake.
Example fix
// before node_chain: ["fetch","parse","emit"] manifestation_node_id: "emit" upstream_node_ids: ["parse","emit"] // after upstream_node_ids: ["parse"]
Defensive patterns
Strategy: validation
Validate before calling
const ok = item.diagnosis.root_cause_hypothesis.upstream_node_ids.every( (id) => slice.node_chain.includes(id) && id !== slice.manifestation_node_id );
Type guard
function isUpstreamChainNode(id, slice) {
return slice.node_chain.includes(id) && id !== slice.manifestation_node_id;
} Prevention
- Derive upstream_node_ids by filtering node_chain instead of authoring them independently.
- When trimming a node_chain, re-derive the diagnosis hypothesis in the same edit.
When it happens
Trigger: Listing a manifestation node as its own upstream cause; naming a node from another variant or a node dropped from the slice's chain; stale upstream ids after the node_chain was revised.
Common situations: Updating node_chain (e.g., trimming it to the baseline variant) without updating the diagnosis hypothesis; reusing node ids from a different workflow revision.
Related errors
- ${where} hard chain edge ${edge.from}->${edge.to} is not a h
- ${where} unproven adjacency ${edge.from}->${edge.to} is in f
- ${where} adjacency ${edge.from}->${edge.to} appears in both
- option not found
- cave_harness_adapter_version_invalid
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/3e6c0ac4fb9a736f.
Report an issue: GitHub.