From a7ca8d712d13de12edf84fe7103fbb971e5f4c1f Mon Sep 17 00:00:00 2001 From: robbond Date: Thu, 13 Aug 2026 12:49:42 +0100 Subject: [PATCH] docs: record bounded structural context admission --- docs/current-handoff.md | 14 ++++- docs/experiment-60b19.md | 133 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 docs/experiment-60b19.md diff --git a/docs/current-handoff.md b/docs/current-handoff.md index c127998..3bfd23d 100644 --- a/docs/current-handoff.md +++ b/docs/current-handoff.md @@ -140,6 +140,18 @@ Experiment 54N tested whether an interpretation disagreement can be judged for m - `npx vitest run tests/graph/apply-proposal.test.js tests/graph/prompt-builder.test.js` → PASS (174/174) - **Still unproven live:** the exact 60B.6 continuation case where the model selects the newly exposed client-retention factor and the final target preserves that same ready material unknown in a live run. +### 60B.19 bounded structural context admission + +- The blocked 60B.16 implementation was removed from the **global compatibility path** after 60B.17 showed it caused 8 `apply-proposal` regressions. +- The structural fallback now runs only for **newly-added unresolved unknowns** at the **pre-mutation proposal boundary** inside `applyValidatedProposal()`. +- The context identity for that admission is the **pre-update `activeUnknownNodeId`**, not the post-update selected node. +- Route A = ancestor chain to the original active decision; Route B = `may_cause` / `causes` / `affects` to an option `contained_in` that original active decision. +- A local in-memory admitted-node set now preserves that same-turn admission through later compatibility checks without changing generic compatibility semantics for unrelated nodes. +- Bounded verification passed: + - `npx vitest run tests/graph/reasoning-context-compatibility.test.js` → PASS (14/14) + - `npx vitest run tests/graph/apply-proposal.test.js tests/graph/reasoning-context-compatibility.test.js` → PASS (96/96) +- **Still unproven live:** the exact 60B.12 client-retention continuation case under live model output. + ### When This Knowledge-Management Phase Is Complete Provisional criteria for review (all confirmed met by Experiment 38 cold-start test): @@ -2824,7 +2836,7 @@ Experiment 60B.13 performed read-only architectural diagnosis of the 60B.12 kind --- -Experiment 60B.14 performed read-only design analysis on whether a newly-created unresolved factor inside an active decision should inherit the decision's reasoning pattern rather than being classified mainly from its wording. **Classification: D — COMPATIBILITY FALLBACK.** The current architecture separates *active pattern determination* (which DOES use parent-chain traversal via `determineActiveReasoningPattern` and correctly yields "decision" for the 60B.12 node) from *node-intrinsic pattern inference* (which does NOT use that context — it runs standalone text analysis on the node's label/description only). This separation is the root cause: the active pattern correctly walks up to find "decision" in the decision unknown, but the compatibility check re-runs standalone inference on the new node and gets "diagnosis" from its diagnostic-style wording ("will our largest client leave"). The smallest correct fix preserves intrinsic text analysis as primary signal but adds a normalization fallback: when inferred node pattern is incompatible with active pattern AND the node's graph position (parentId, edges) places it structurally within that active context, reinterpret using the active pattern rather than rejecting. This requires no new schema, no new keywords, and preserves genuine pattern transitions (the intrinsic inference still returns diagnosis; only the compatibility decision changes). +Experiment 60B.14 performed read-only design analysis on whether a newly-created unresolved factor inside an active decision should inherit the decision's reasoning pattern rather than being classified mainly from its wording. **Classification: D — COMPATIBILITY FALLBACK.** The current architecture separates _active pattern determination_ (which DOES use parent-chain traversal via `determineActiveReasoningPattern` and correctly yields "decision" for the 60B.12 node) from _node-intrinsic pattern inference_ (which does NOT use that context — it runs standalone text analysis on the node's label/description only). This separation is the root cause: the active pattern correctly walks up to find "decision" in the decision unknown, but the compatibility check re-runs standalone inference on the new node and gets "diagnosis" from its diagnostic-style wording ("will our largest client leave"). The smallest correct fix preserves intrinsic text analysis as primary signal but adds a normalization fallback: when inferred node pattern is incompatible with active pattern AND the node's graph position (parentId, edges) places it structurally within that active context, reinterpret using the active pattern rather than rejecting. This requires no new schema, no new keywords, and preserves genuine pattern transitions (the intrinsic inference still returns diagnosis; only the compatibility decision changes). --- diff --git a/docs/experiment-60b19.md b/docs/experiment-60b19.md new file mode 100644 index 0000000..f2258bb --- /dev/null +++ b/docs/experiment-60b19.md @@ -0,0 +1,133 @@ +# Experiment 60B.19 — Bounded Structural Context Admission + +**Branch:** `feature/reasoning-context-compatibility-v0.28` +**Date:** 2026-08-13 +**Status:** COMPLETE +**Type:** BOUNDED IMPLEMENTATION + +## Objective + +Replace the blocked 60B.16 global reasoning-compatibility fallback with the 60B.18 boundary: + +- run structural context fallback only for **newly-added unresolved unknowns**; +- run it only when the **full proposal is known but before graph mutation**; +- use the **pre-update `activeUnknownNodeId`** as the decision-context identity; +- preserve that same-turn admission through later validation without changing generic compatibility semantics for unrelated nodes. + +## Why the global 60B.16 integration was removed + +60B.17 established that the structural Route A / Route B predicate itself was useful, but its **global integration layer was too broad**. Applying fallback inside generic compatibility checks caused 8 `apply-proposal` regressions by changing behaviour for unrelated pre-existing and later-selected unknowns. + +The secondary defect was that later compatibility re-checks could lose the original decision-context identity and validate against the wrong active node. + +## Implemented boundary + +Structural context fallback now runs only at the **pre-mutation proposal boundary**: + +- eligibility is limited to `proposal.addedNodes` where: + - `kind === "unknown"` + - `status !== "resolved"` +- the original context identity is: + - `situationGraph.activeUnknownNodeId` +- the original context pattern is recovered from the **pre-update graph** +- structural admission is only relevant when: + - `activePattern === "decision"` + +No pre-existing unknowns, updated existing nodes, resolved nodes, or later-selected targets enter the fallback by scope. + +## Route A / Route B + +### Route A + +Admit a newly-added unresolved unknown when its `parentId` / ancestor chain reaches the original active decision context node. + +### Route B + +Admit a newly-added unresolved unknown when: + +- `X --(may_cause | causes | affects)--> option Z` +- `Z --contained_in--> active decision D` +- `D.id === pre-update activeUnknownNodeId` + +### Explicit non-qualifiers + +These do **not** establish Route B: + +- `supports` +- `measures` +- `depends_on` +- arbitrary graph connectivity + +## Local admitted-node tracking + +No schema field was added. + +Within `applyValidatedProposal()` only, the implementation now maintains a local in-memory set of structurally admitted node IDs for same-turn newly-added unresolved unknowns. + +That set is then threaded into later compatibility checks so the exact already-adjudicated diagnosis→decision mismatch is not rejected again during post-mutation selection/result validation. + +This does **not**: + +- rewrite intrinsic node pattern; +- make the node universally compatible; +- disable non-pattern validations; +- change question targeting, scoring, materiality, prompt, or schema. + +## Regression restoration + +Focused suite: + +- `npx vitest run tests/graph/reasoning-context-compatibility.test.js` → **PASS (14/14)** + +Combined bounded verification: + +- `npx vitest run tests/graph/apply-proposal.test.js tests/graph/reasoning-context-compatibility.test.js` → **PASS (96/96)** + +This restored the 8 60B.17 regressions by construction: + +- pre-existing unknowns no longer enter fallback; +- later-selected 60B.11 fixtures no longer trigger fallback merely because they are chosen after mutation; +- later compatibility re-checks now use the preserved original reasoning-context node identity instead of selected-child drift; +- generic compatibility semantics remain unchanged outside the bounded same-turn admission class. + +## What remains unproven + +Still unproven until the exact live regression is rerun: + +- the precise 60B.12 client-retention continuation case in a live end-to-end path where the model introduces the factor and the final selected target preserves that same ready material unknown. + +## Production code changed + +YES — `lib/graph/apply-proposal.js` + +## Tests changed + +YES — `tests/graph/reasoning-context-compatibility.test.js` + +## Prompt changed + +NO + +## Schema changed + +NO + +## Question targeting changed + +NO + +## Materiality changed + +NO + +## Selection scoring changed + +NO + +## Ollama calls + +0 + +## Live API calls + +0