289 lines
17 KiB
Markdown
289 lines
17 KiB
Markdown
# 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 `determineActiveReasoningPattern` inheritance; 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
|
||
|
||
```javascript
|
||
// 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:**
|
||
1. Candidate A alone is too narrow (misses edge-connected nodes).
|
||
2. 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.
|
||
3. Candidate C is too permissive for any production use.
|
||
4. Candidate D requires inventing semantic family rules not present in the current schema, increasing implementation complexity.
|
||
5. **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:
|
||
> 1. Any ancestor in X's parentId chain has reasoning pattern Y, OR
|
||
> 2. 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)
|