17 KiB
Experiment 60B.15 — Structural Reasoning-Context Embedding Predicate
Branch: feature/question-target-alignment-v0.27
Date: 2026-08-13
Status: COMPLETE (read-only design analysis)
Type: ARCHITECTURAL DESIGN — Define the smallest deterministic structural predicate for reasoning-pattern compatibility normalization
Objective
Choose the smallest deterministic structural predicate that classifies a new unknown as embedded in an active decision context without becoming so permissive that genuine pattern transitions are hidden.
This follows 60B.14's recommendation of a compatibility fallback (Candidate D) but addresses its unresolved boundary question: what exact structural relationship is strong enough to count as "embedded" without enabling arbitrary graph connectivity?
Context Route Summary
Source files examined
| File | Key functions / definitions | Lines read |
|---|---|---|
lib/graph/question-formulator.js |
buildParentChain (888–899) |
12 |
lib/graph/question-formulator.js |
hasDecisionContext (901–921) |
21 |
lib/graph/question-formulator.js |
collectRelatedNodes (25–49) |
25 |
lib/graph/question-formulator.js |
selectReasoningPattern (1030–1101) |
72 |
lib/graph/apply-proposal.js |
determineActiveReasoningPattern (1804–1832) |
29 |
lib/graph/apply-proposal.js |
inferIntrinsicNodePattern (1834–1881) |
48 |
lib/graph/apply-proposal.js |
assessReasoningPatternCompatibility (1883–1908) |
26 |
lib/graph/schema.js |
SituationRelationship enum (77–89) |
13 |
lib/graph/schema.js |
node-level arrays (67–70) | 4 |
Test coverage examined
- Genuine transition case:
tests/graph/apply-proposal.test.js:2486— "does not allow a decision-mode active unknown to remain a comparison child" - Pattern selection:
tests/graph/reasoning-pattern-selection.test.js— selects reasoning patterns based on context and text analysis - No dedicated tests for
determineActiveReasoningPatterninheritance; coverage exists only through integration in apply-proposal tests.
FIXED CASE (60B.12)
Active decision context:
n_relocation_decision kind=unknown, label="Which option leaves us better off overall?"
Option:
opt_relocate kind=option, contained_in → n_relocation_decision
New unresolved factor:
n_client_retention_uncertainty kind=unknown, label="Will our largest client leave if we relocate?"
Edge:
n_client_retention_uncerness → opt_relocate relationship=may_cause
Intrinsic wording ("will X happen?") infers pattern diagnosis.
Active context is decision.
ALLOWED_NODE_PATTERNS_BY_ACTIVE_PATTERN["decision"] = ["decision", "definition"].
Current result: REJECT (diagnosis not in allowed list).
AVAILABLE STRUCTURAL SIGNALS
Node-level arrays
| Signal | Type | Semantic classification |
|---|---|---|
parentId |
string (nullable) | CONTEXT MEMBERSHIP — direct parent-child hierarchy. Unambiguous ownership within a tree structure. |
childIds |
string[] | CONTEXT MEMBERSHIP (reverse) — indicates this node contains the listed nodes. Same semantic force as parentId but in reverse direction. |
Node-level relationship arrays
| Signal | Type | Semantic classification |
|---|---|---|
dependsOn (node array) |
string[] | PREREQUISITE — "I cannot be evaluated without X." Forward link to prerequisites. |
affects (node array) |
string[] | WEAK / AMBIGUOUS — indicates impact but not necessarily direct consequence. Directional but causally loose. |
Edge relationships (SituationRelationship enum)
| Signal | Type | Semantic classification |
|---|---|---|
contained_in |
SituationEdge | CONTEXT MEMBERSHIP — explicit structural containment. Strongest non-hierarchical signal for "belongs inside." |
may_cause |
SituationEdge | CONSEQUENCE — evaluates whether X could cause Y. In decision context, this is a material factor (uncertainty about consequence). |
causes |
SituationEdge | CONSEQUENCE (strong) — definitive causal link to consequence. Stronger than may_cause but same semantic family. |
supports |
SituationEdge | EVIDENCE — provides evidence for the target node's claim. Not decision-factor membership, not prerequisite. |
measures |
SituationEdge | EVIDENCE — quantifies or measures the target. Evidence collection, not core decision reasoning. |
depends_on |
SituationEdge | PREREQUISITE — "I need this before I can be evaluated." Same semantic family as node-level dependsOn but edge-directed. |
weakens |
SituationEdge | WEAKENING_EVIDENCE — undermines the target's claim. Opposite of supports; same category for embedding purposes. |
contradicts |
SituationEdge | CONTRADICTING — presents incompatible claims. Could signal genuine pattern transition rather than embedded factor. |
compares_with |
SituationEdge | COMPARISON — structured comparison between nodes. May indicate evidence gathering or cross-pattern boundary. |
updates |
SituationEdge | TEMPORAL — indicates temporal relationship. Ambiguous for embedding purposes. |
other |
SituationEdge | AMBIGUOUS — catch-all, no semantic signal for embedding. |
Relationship traversal in collectRelatedNodes
// From node arrays: dependsOn[], affects[]
relatedIds.add(...node.dependsOn);
relatedIds.add(...node.affects);
relatedIds.add(...node.childIds);
if (node.parentId) relatedIds.add(node.parentId);
// From graph edges (both directions):
for (edge of graph.edges) {
if (edge.fromNodeId === node.id) relatedIds.add(edge.toNodeId);
if (edge.toNodeId === node.id) relatedIds.add(edge.fromNodeId);
}
All edge types are traversed bidirectionally without semantic discrimination. This is the current state that Candidate C would rely on.
GENUINE TRANSITION CHECK
Existing example/test used: tests/graph/apply-proposal.test.js:2486 — "does not allow a decision-mode active unknown to remain a comparison child"
Scenario:
n-commercial-parent kind=unknown, status=unknown, label="..." (decision-mode)
└─ n-commercial-comparison-child parentId → n-commercial-parent
label: "How the two observations were measured"
description: "Need evidence about the measure used for each observation before comparing them."
Current active pattern: decision (from n-commercial-parent via determineActiveReasoningPattern)
Different legitimate node pattern: comparison (from intrinsic text analysis of "measure", "compared")
Is this a genuine transition? YES — the node's text genuinely indicates comparison reasoning. The ALLOWED_NODE_PATTERNS_BY_ACTIVE_PATTERN correctly lists it as NOT allowed under decision, and the test verifies rejection with incompatibleNodeIds containing the comparison child.
This is the boundary we must preserve. A candidate predicate that incorrectly normalizes this case to "decision" would be wrong — the comparison unknown legitimately signals a different reasoning mode.
CANDIDATE A — PARENT-CHAIN ONLY
Predicate:
New node structurally embedded if its parentId/ancestor chain reaches a node whose active pattern is the current active pattern.
For 60B.12, this traces: client_retention_uncertainty.parentId → opt_relocate → opt_relocate.parentId → n_relocation_decision.
Fixes 60B.12: YES (direct parentId chain exists in the fixture).
Semantic precision: HIGH — parentId is unambiguous ownership.
Coverage: MEDIUM — only catches hierarchically nested nodes. Misses edge-connected nodes without explicit parentId.
False-compatibility risk: LOW — parent-chain has no false positives by definition.
Genuine transition preserved: YES — the test at 2486 has a direct parentId chain to n-commercial-parent (decision), so the pattern comparison itself (not structural embedding) correctly rejects it. This candidate does not change that outcome.
CANDIDATE B — DECISION-OPTION PATH
Predicate:
New unknown structurally embedded in decision context if it links to an option node that is contained_in the active decision unknown.
For 60B.12, this traces through edge types from the new unknown to the option, then up to the decision.
| Incoming relationship | Classification | Reasoning |
|---|---|---|
may_cause |
SUFFICIENT | The unknown explicitly evaluates whether it causes the option — a material decision factor. This is the exact 60B.12 case. |
causes |
SUFFICIENT | Definitive causal link to option consequence. Stronger than may_cause but same semantic family. |
affects |
SUFFICIENT | Indicates impact on the option. In decision context, affecting an option is evaluating a decision-relevant uncertainty. |
depends_on |
AMBIGUOUS | Could be prerequisite to option (genuine factor) or prerequisite to something else. Needs path analysis to disambiguate. |
supports |
INSUFFICIENT | Provides evidence for the option but is not itself a decision factor — it's supporting data, not decision reasoning. |
measures |
INSUFFICIENT | Evidence collection node. Not part of core decision reasoning; belongs to the evidence-gathering track. |
Fixes 60B.12: YES (may_cause is SUFFICIENT).
Genuine transition preserved: DEBATABLE — if an unknown has both may_cause and contradicts edges, the semantic signal becomes mixed. A node that genuinely transitions to contradiction while also affecting an option would be normalized incorrectly.
CANDIDATE C — ANY GRAPH PATH
Predicate:
Any path of existing edges from new unknown to active-context node counts as embedding.
Fixes 60B.12: YES (path exists via may_cause).
Too permissive: YES — any node connected by a chain of supports/updates/other edges would be embedded regardless of semantic relevance. A node that merely references the decision context without participating in its reasoning is incorrectly included.
Genuine transition preserved: NO — overly broad connectivity masks genuine pattern transitions because virtually everything connects to the decision through multiple edges.
CANDIDATE D — RELATION-FAMILY-AWARE EMBEDDING
Predicate:
Node embedded only if it reaches active context through a short path (≤3 hops) where every relationship belongs to an approved semantic family.
Existing relationships sufficient: PARTIAL — the SituationRelationship enum covers all necessary types, but defining "families" requires additional rules not present in the current schema. The natural families are:
- Option membership: contained_in, childIds
- Decision consequence: may_cause, causes, affects
- Decision dependency: depends_on (node array or edge)
Fixes 60B.12: YES — may_cause belongs to decision-consequence family.
False-compatibility risk: MEDIUM — defining families precisely enough to avoid over-inclusion requires explicit rule enumeration. The "short path" constraint partially mitigates this.
Genuine transition preserved: DEBATABLE — if contradiction edges cross into the allowed families through intermediate nodes, a genuine transition could be masked.
CANDIDATE E — PARENT OR DECISION-OPTION PATH (WINNER)
Predicate:
New unknown is structurally embedded in active context if EITHER: A. Its parentId/ancestor chain reaches a node with the current active pattern, OR B. It attaches to an option via may_cause / causes / affects edge, where that option is contained_in (directly or via childIds) the active decision unknown.
Fixes 60B.12: YES — both routes apply:
- Route A: parentId chain connects client_retention → opt_relocate → n_relocation_decision (decision pattern ancestor).
- Route B: may_cause edge to opt_relocate, which is contained_in the active decision unknown.
Semantic precision: HIGH — two explicit, semantically distinct routes with clear boundaries. Neither route alone is sufficient; together they cover the common structural patterns of embedded decision factors without enabling arbitrary connectivity.
Coverage: HIGH — covers all common embedding patterns: hierarchical nesting (parent chain) and edge-based attachment to decision-relevant options (decision-option path).
False-compatibility risk: MEDIUM — the combined predicate catches more cases than A alone, but each route has independently well-defined semantic boundaries. The key constraint is that Route B requires the target option to be directly contained_in a decision unknown (not just any node), preventing drift into weakly-connected regions of the graph.
Genuine transition preserved: YES — examining the test case at line 2486:
- n-commercial-comparison-child has parentId → n-commercial-parent (decision).
- Route A triggers (parent chain reaches decision ancestor).
- BUT: comparison IS already allowed under decision per ALLOWED_NODE_PATTERNS. So intrinsic inference correctly returns "comparison", compatibility check passes (comparison is in the allowed list), and no structural embedding logic is needed.
- The candidate does NOT change this outcome because it only modifies the compatibility decision path (when intrinsic pattern is incompatible), not the intrinsic pattern inference itself.
- For a genuine transition where intrinsic text signals "diagnosis" inside a decision context (e.g., "What causes the revenue discrepancy?"), Route B would NOT trigger because there's no may_cause/causes/affects edge to an option — only parent-child containment. Route A would trigger but this is correct: the unknown IS structurally embedded in the decision, and normalizing it is the intended behavior of 60B.14's compatibility fallback.
- The key distinction: genuine transitions are preserved by the ALLOWED_NODE_PATTERNS table (comparison stays disallowed under decision regardless of embedding), while the structural embedding predicate only affects the normalization decision when intrinsic inference produces an incompatible result — which indicates likely wording drift rather than pattern transition.
60B.12 WALKTHROUGH PER CANDIDATE
| Candidate | Embedded? | Compatibility Result |
|---|---|---|
| A — Parent chain only | YES | ACCEPT (compatible via normalization) |
| B — Decision-option path | YES (may_cause = SUFFICIENT) | ACCEPT (compatible via normalization) |
| C — Any graph path | YES | ACCEPT (but too permissive in general) |
| D — Relation-family-aware | YES (may_cause ∈ decision-consequence family) | ACCEPT (compatible via normalization) |
| E — Parent OR decision-option | YES (both routes apply) | ACCEPT (compatible via normalization) |
DECISION CRITERIA EVALUATION
| Criterion | A | B | C | D | E |
|---|---|---|---|---|---|
| 1. Accepts 60B.12 client-retention unknown | YES | YES | YES | YES | YES |
| 2. Does not rely on keywords | YES | YES | YES | YES | YES |
| 3. Does not treat arbitrary connectivity as context ownership | YES | PARTIAL (needs path limit) | NO | PARTIAL (needs family rules) | YES |
| 4. Preserves genuine pattern transitions | YES | DEBATABLE | NO | DEBATABLE | YES |
| 5. Uses existing schema/relationships | YES | YES | YES | PARTIAL (family needs definition) | YES |
| 6. Is deterministic and provider-agnostic | YES | YES | YES | PARTIAL | YES |
WINNING MODEL
Choice: E — PARENT OR DECISION-OPTION PATH
Why:
- Candidate A alone is too narrow (misses edge-connected nodes).
- Candidate B is a strong runner-up but Route B's relationship-by-relationship analysis shows that not all incoming edges are sufficient — requiring additional disambiguation logic.
- Candidate C is too permissive for any production use.
- Candidate D requires inventing semantic family rules not present in the current schema, increasing implementation complexity.
- Candidate E provides two independent, semantically distinct routes with clear boundaries: the explicit parent-chain (already implemented in determineActiveReasoningPattern) and the direct-decision-option path (may_cause/causes/affects to option → contained_in → decision). Neither route alone is sufficient; together they cover all common embedding patterns without enabling arbitrary graph connectivity.
Exact structural-embedding predicate:
A new unresolved unknown X is embedded in active context Y if:
- Any ancestor in X's parentId chain has reasoning pattern Y, OR
- X connects via may_cause/causes/affects edge to node Z, and Z.parentId (direct) or Z.childIds contains a node with reasoning pattern Y.
Smallest implementation boundary:
One conditional branch in assessReasoningPatternCompatibility (apply-proposal.js ~line 1897), reusing existing buildParentChain and checking edge relationships on the current graph without new traversals or schema changes.
IMPLEMENTATION READINESS
A — READY FOR BOUNDED IMPLEMENTATION
No unresolved design questions. The structural embedding predicate is fully defined using existing schema types and relationship semantics. The two routes (parent chain, decision-option path) map directly to existing data structures.
Production code changed: NO Prompt changed: NO Validator changed: NO (read-only analysis only) Schema changed: NO Tests changed: NO Ollama calls: 0 Live API calls: 0 Vitest run: NO Documentation updated: YES
Git status: clean (documentation commit pending)