From 3562c0f225c64a1427106de98bb7acd3cf14a546 Mon Sep 17 00:00:00 2001 From: lipluscodex <268560960+lipluscodex@users.noreply.github.com> Date: Sun, 2 Aug 2026 05:34:00 +0900 Subject: [PATCH 1/2] eval: freeze local recurrent competition experiment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit production D1由来の新規development/holdout、provenance、contamination audit、gold、6 variantsと二baseline gateを観測前に固定する。局所neighbor/query/path recurrent strategy、diagnostics、tests、requirements、実験手順を同じfreezeへ含める。 --- README.md | 6 + ...l-dynamics-local-competition-experiment.md | 98 ++++ docs/requirements.md | 9 + src/neuron_graph_rag/dynamics.py | 398 ++++++++++++++ src/neuron_graph_rag/engine.py | 8 + src/neuron_graph_rag/experiment.py | 221 +++++++- ...iplus_local_competition.contamination.json | 45 ++ ...us_local_competition_development.gold.json | 102 ++++ ..._liplus_local_competition_development.json | 487 ++++++++++++++++++ ...al_competition_development.provenance.json | 86 ++++ ...local_competition_experiment.manifest.json | 110 ++++ ...liplus_local_competition_holdout.gold.json | 102 ++++ .../d1_liplus_local_competition_holdout.json | 487 ++++++++++++++++++ ..._local_competition_holdout.provenance.json | 86 ++++ tests/test_local_competition.py | 196 +++++++ tools/audit_local_competition_fixture.py | 163 ++++++ 16 files changed, 2582 insertions(+), 22 deletions(-) create mode 100644 docs/neural-dynamics-local-competition-experiment.md create mode 100644 tests/fixtures/d1_liplus_local_competition.contamination.json create mode 100644 tests/fixtures/d1_liplus_local_competition_development.gold.json create mode 100644 tests/fixtures/d1_liplus_local_competition_development.json create mode 100644 tests/fixtures/d1_liplus_local_competition_development.provenance.json create mode 100644 tests/fixtures/d1_liplus_local_competition_experiment.manifest.json create mode 100644 tests/fixtures/d1_liplus_local_competition_holdout.gold.json create mode 100644 tests/fixtures/d1_liplus_local_competition_holdout.json create mode 100644 tests/fixtures/d1_liplus_local_competition_holdout.provenance.json create mode 100644 tests/test_local_competition.py create mode 100644 tools/audit_local_competition_fixture.py diff --git a/README.md b/README.md index 8adfcd9..e5f3606 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,12 @@ development では `budget-025` が relation MRR 0.3833 を維持し、negative- 全13 variants の gate 不合格と tradeoff、選択理由、holdout 判定は [Neural dynamics experiment](docs/neural-dynamics-experiment.md) と versioned result JSON に保存しています。現在の holdout は再選択や parameter 調整に再利用しません。 +## Local recurrent competition experiment + +PR #12のglobal recurrent tradeoffを受け、競合を同じsourceのsibling neighborへ局所化し、query relevanceとactive path identityを独立に比較する6-variant experimentを定義しています。 + +production D1から取得した新しいdevelopment / holdoutは、旧development、開封済み旧holdout、相互間でdoc pathとnode IDを分離しています。両provenance、contamination audit、二baseline gate、one-time holdout停止規則は[Local recurrent competition experiment](docs/neural-dynamics-local-competition-experiment.md)を参照してください。 + ## Public API ```python diff --git a/docs/neural-dynamics-local-competition-experiment.md b/docs/neural-dynamics-local-competition-experiment.md new file mode 100644 index 0000000..44b8f43 --- /dev/null +++ b/docs/neural-dynamics-local-competition-experiment.md @@ -0,0 +1,98 @@ +# Local recurrent competition experiment + +## 目的 + +PR #12で観測されたrecurrent competitionのrelation改善とdirect / negative-control退行を分離するため、競合をglobal node集合から同じsourceのsibling neighborへ局所化する。query relevanceとactive path identityを独立ablationとして比較し、未観測holdoutが固定gateを通過した場合だけdefault変更を許可する。 + +旧developmentはfamilyと`recurrent-balanced` baselineを選んだ探索的根拠に限定する。旧holdoutのgold、rank、metrics、resultは本実験へ読み込まない。 + +## 固定入力 + +- manifest: `tests/fixtures/d1_liplus_local_competition_experiment.manifest.json` +- development fixture / gold / provenance: `d1_liplus_local_competition_development.*.json` +- holdout fixture / gold / provenance: `d1_liplus_local_competition_holdout.*.json` +- contamination audit: `d1_liplus_local_competition.contamination.json` +- 両fixtureはproduction D1 `search_docs` / `doc_edges`からread-only取得した9 node / 11 `mention` edgeのweakly-connected subgraph +- developmentはjudgment-learning / self-evolution / retrieval-surface cluster +- holdoutはwiki-sync / Character_Instance cluster + +acquisition provenanceはschema fingerprint、取得時刻、coverage、zero-write evidence、known gapを保持する。D1はlossy search snapshotであり、GitHubをbyte-exact sourceの正本とする。 + +## Contamination boundary + +auditは次の重複が空であることを固定する。 + +- 新developmentと新holdoutのdoc path、node ID、source URL +- 両新splitと旧development fixture +- 両新splitと開封済み旧holdout fixture +- 新split間のnormalized query、expected node、relation endpoint + +旧holdoutはfixture identifierだけをdenylist照合する。旧holdout goldとresultはaudit toolもexperiment runnerも読み込まない。 + +## 固定variant + +variantは6件で、追加parameter gridを持たない。全variantは同じentry / graph weight、seed count、hop limit、hop decay、recurrent step、decay、activation budget、inhibition ratioを使う。 + +| role | variant | structure | +|---|---|---| +| baseline | `current` | current positive additive | +| baseline | `recurrent-balanced` | PR #12のbest prior recurrent | +| candidate | `local-neighbor` | sourceごとのsibling neighbor競合 | +| candidate | `local-neighbor-query` | sibling競合 + query relevance | +| candidate | `local-neighbor-path` | active pathごとのsibling競合 | +| candidate | `local-neighbor-query-path` | active path競合 + query relevance | + +path identityはseed IDとruntimeで通過したedge target列から決める。gold path、expected node、case ID、特定query文字列をruntime条件にしない。path stateはnodeごとに最大4件、全伝播は`max_propagation_expansions`で停止する。 + +## Local update + +各sourceまたはactive pathについてoutgoing edge messageを作り、その集合内だけで次を行う。 + +1. query variantだけtarget text / edge typeとのquery overlapでmessageをgateする。 +2. sibling集合の最大messageに固定inhibition ratioを掛け、各messageから引く。 +3. 残ったmessage総量を`activation_budget * source_activation`以内へscaleする。 +4. path variantだけpath identityを維持してnode集約前に上位pathを決定論的に残す。 +5. recurrent decayを前step stateへ適用する。 + +global maximumを使った抑制はlocal候補へ適用しない。diagnosticsは競合集合ごとのsource、path identity、neighbor数、平均query relevance、message総量の前後を保存する。 + +## Development gate + +新developmentで6 variantsを一度評価する。local候補は次をすべて満たす場合だけ通過する。 + +1. relation MRRが`current`と`recurrent-balanced`を両方厳密に上回る。 +2. direct lookup MRRが`current`から退行しない。 +3. negative-control MRRが`current`から退行しない。 +4. 全relation caseのendpoint / edge type pathが一致する。 +5. credited feedback edgeが1本以上変化する。 +6. uncredited edgeと非対象case rankが変化しない。 + +複数候補のtie-breakはworst-cohort MRR、relation MRR、平均展開数、構造複雑度、variant IDの順とする。候補がなければholdoutを開かずdefaultを維持する。 + +## Holdout stop rule + +development候補がある場合だけ、holdoutで`current`、`recurrent-balanced`、選択候補を一度評価する。result fileが存在する場合は上書きを拒否する。 + +採用にはdevelopmentと同じgateを要求する。一つでも失敗した場合は`current_positive_additive`を維持し、holdout観測後のparameter、gold、doc path、閾値、選択規則、停止規則変更を禁止する。 + +## 実行境界 + +fixture、gold、provenance、audit、manifest、implementation、tests、上記規則をfreeze commitとしてpushするまでdevelopment runnerを実行しない。 + +freeze後の順序は次に限定する。 + +1. development resultを新規作成する。 +2. 固定gateで候補を一意に決める。 +3. 候補がなければ終了する。 +4. 候補があればholdout resultを一度だけ新規作成する。 +5. 固定gateからdefault維持または候補採用を記録する。 + +品質数値はCI合格閾値にしない。CIはhash、contamination、determinism、局所性、variant上限、停止規則を検証する。 + +## 観測結果 + +freeze commit push後に追記する。追記時に固定入力、variant、gate、停止規則を変更しない。 + +## 適用限界 + +9-nodeの固定Li+ wiki subsetとfeature-hashing encoderだけを対象とする。D1 snapshot、別graph topology、learned embedding、一般corpusへ結果を外挿しない。 diff --git a/docs/requirements.md b/docs/requirements.md index 7f14a7f..6bbabaf 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -49,6 +49,14 @@ 31. 候補選択は development result だけで行い、relation MRR と negative-control MRR の Pareto gate、worst-cohort MRR、展開数、構造複雑度、variant ID の順で一意に決める。 32. development gate を通る候補がない場合は holdout を開かず既定を変更しない。候補がある場合だけ holdout を一度評価し、cohort 退行、path 不一致、feedback 汚染のいずれかがあれば採用しない。 33. experiment result は gate 不合格、Pareto 支配、holdout 不採用を含む全 variant を上書きせず versioned artifact として保存する。 +34. recurrent activation は global inhibition に加え、同じ source の sibling neighbor だけを競合させる local strategy を選択できる。 +35. local recurrent strategy は query relevance と active path identity を独立に有効化でき、競合集合ごとに source、path identity、neighbor 数、query relevance、配分前後の message 総量を記録する。 +36. local recurrent experiment は production D1 から read-only 取得した新しい development / holdout を旧 development / 開封済み holdout および相互間で分離し、両 provenance と contamination audit を結果観測前に固定する。 +37. 旧 development result は family と baseline の探索的根拠だけに使用し、旧 holdout は fixture identifier の重複拒否以外では読み込まない。 +38. local recurrent experiment は `current`、`recurrent-balanced`、neighbor / query / path の4 ablationを合わせた6 variantsに固定し、parameter gridを追加しない。 +39. development候補はrelation MRRで両baselineを厳密に上回り、direct / negative-control MRRがcurrentから退行せず、全relation pathとfeedback isolationを満たす場合だけ選択する。 +40. development候補がある場合だけ、未観測holdoutで`current`、`recurrent-balanced`、選択候補を一度評価する。resultの再実行と上書きを拒否する。 +41. local recurrent strategyは未観測holdoutで同じgateを通過した場合だけ既定候補になり、それ以外では`current_positive_additive`を維持する。 ## 4. Constraints @@ -70,3 +78,4 @@ - [D1 corpus fixture](d1-corpus-fixture.md) が read-only 取得、認証境界、provenance、coverage 比較、再取得手順を定義する。 - [Real-corpus benchmark](real-corpus-benchmark.md) が gold freeze、判定規則、観測結果、外挿限界を定義する。 - [Neural dynamics experiment](neural-dynamics-experiment.md) が development / holdout 分離、固定探索空間、候補選択、単一 holdout 開封、停止規則を定義する。 +- [Local recurrent competition experiment](neural-dynamics-local-competition-experiment.md) が新規D1 subgraph、contamination audit、query / path ablation、二baseline gateを定義する。 diff --git a/src/neuron_graph_rag/dynamics.py b/src/neuron_graph_rag/dynamics.py index f21cfe6..59c1d41 100644 --- a/src/neuron_graph_rag/dynamics.py +++ b/src/neuron_graph_rag/dynamics.py @@ -23,6 +23,29 @@ class DynamicsSettings: recurrent_steps: int recurrent_decay: float convergence_tolerance: float + max_active_paths_per_node: int + + +@dataclass(frozen=True, slots=True) +class CompetitionSetDiagnostic: + step: int + source_id: str + path_identity: str + neighbor_count: int + mean_query_relevance: float + message_total_before: float + message_total_after: float + + def as_dict(self) -> dict[str, object]: + return { + "step": self.step, + "source_id": self.source_id, + "path_identity": self.path_identity, + "neighbor_count": self.neighbor_count, + "mean_query_relevance": self.mean_query_relevance, + "message_total_before": self.message_total_before, + "message_total_after": self.message_total_after, + } @dataclass(frozen=True, slots=True) @@ -33,6 +56,8 @@ class PropagationDiagnostics: activation_total: float converged: bool stop_reason: str + active_path_count: int = 0 + competition_sets: tuple[CompetitionSetDiagnostic, ...] = () def as_dict(self) -> dict[str, object]: return { @@ -42,6 +67,8 @@ def as_dict(self) -> dict[str, object]: "activation_total": self.activation_total, "converged": self.converged, "stop_reason": self.stop_reason, + "active_path_count": self.active_path_count, + "competition_sets": [item.as_dict() for item in self.competition_sets], } @@ -64,6 +91,20 @@ def propagate( outgoing_edges: OutgoingEdges, settings: DynamicsSettings, ) -> PropagationResult: + if settings.strategy in { + "local_neighbor_competition", + "local_neighbor_query_competition", + "local_neighbor_path_competition", + "local_neighbor_query_path_competition", + }: + return _local_recurrent( + query=query, + seed_ids=seed_ids, + entry=entry, + nodes=nodes, + outgoing_edges=outgoing_edges, + settings=settings, + ) if settings.strategy == "recurrent_competition": return _recurrent( query=query, @@ -181,6 +222,7 @@ def propagate( activation_total=sum(output.values()), converged=stop_reason == "frontier_exhausted", stop_reason=stop_reason, + active_path_count=sum(len(node_paths) for node_paths in paths.values()), ), ) @@ -306,7 +348,363 @@ def _recurrent( activation_total=sum(state.values()), converged=converged, stop_reason=stop_reason, + active_path_count=len(best_paths), + ), + ) + + +@dataclass(frozen=True, slots=True) +class _PathState: + node_id: str + activation: float + path: ActivationPath + + +def _local_recurrent( + *, + query: str, + seed_ids: list[str], + entry: dict[str, float], + nodes: dict[str, DocumentNode], + outgoing_edges: OutgoingEdges, + settings: DynamicsSettings, +) -> PropagationResult: + query_conditioned = settings.strategy in { + "local_neighbor_query_competition", + "local_neighbor_query_path_competition", + } + path_conditioned = settings.strategy in { + "local_neighbor_path_competition", + "local_neighbor_query_path_competition", + } + if path_conditioned: + return _local_path_recurrent( + query=query, + seed_ids=seed_ids, + entry=entry, + nodes=nodes, + outgoing_edges=outgoing_edges, + settings=settings, + query_conditioned=query_conditioned, + ) + + query_terms = set(tokenize(query)) + state = {node_id: entry[node_id] for node_id in seed_ids} + paths: dict[str, list[ActivationPath]] = defaultdict(list) + best_paths: dict[str, ActivationPath] = {} + for seed_id in seed_ids: + path = ActivationPath(seed_id, entry[seed_id]) + paths[seed_id].append(path) + best_paths[seed_id] = path + + expansions = 0 + stop_reason = "recurrent_step_limit" + converged = False + completed_steps = 0 + competition_sets: list[CompetitionSetDiagnostic] = [] + for step_number in range(settings.recurrent_steps): + messages: dict[str, float] = defaultdict(float) + message_paths: dict[str, list[ActivationPath]] = defaultdict(list) + for source_id, source_activation in sorted(state.items()): + if source_activation <= 0.0: + continue + source_path = best_paths.get( + source_id, ActivationPath(source_id, source_activation) + ) + visited = {source_path.seed_id} + visited.update(path_step.target_id for path_step in source_path.steps) + proposals: list[tuple[TypedEdge, float, float, ActivationPath]] = [] + for edge in outgoing_edges(source_id): + if edge.target_id in visited: + continue + relevance = ( + _query_gate(query_terms, nodes[edge.target_id], edge, settings) + if query_conditioned + else 1.0 + ) + contribution = ( + source_activation + * edge.weight + * edge.factuality + * settings.hop_decay + * relevance + ) + if contribution <= 0.0: + continue + path_step = PathStep( + edge.source_id, + edge.target_id, + edge.edge_type, + edge.weight, + edge.factuality, + ) + proposals.append( + ( + edge, + contribution, + relevance, + ActivationPath( + source_path.seed_id, + contribution, + source_path.steps + (path_step,), + ), + ) + ) + expansions += 1 + if expansions >= settings.max_expansions: + stop_reason = "max_expansions" + break + competed, diagnostic = _compete_neighbors( + proposals, + source_activation=source_activation, + settings=settings, + step_number=step_number + 1, + source_id=source_id, + path_identity=_path_identity(source_path), + ) + if diagnostic is not None: + competition_sets.append(diagnostic) + for edge, contribution, _, path in competed: + adjusted = ActivationPath(path.seed_id, contribution, path.steps) + messages[edge.target_id] += contribution + message_paths[edge.target_id].append(adjusted) + if expansions >= settings.max_expansions: + break + + candidate: dict[str, float] = defaultdict(float) + for node_id, value in state.items(): + candidate[node_id] += settings.recurrent_decay * value + for node_id, value in messages.items(): + candidate[node_id] += value + candidate = {node_id: value for node_id, value in candidate.items() if value > 0.0} + delta = _state_delta(candidate, state) + state = candidate + for node_id, node_paths in message_paths.items(): + paths[node_id].extend(node_paths) + best_paths[node_id] = max( + node_paths, + key=lambda path: (path.contribution, _path_identity(path)), + ) + completed_steps = step_number + 1 + if delta <= settings.convergence_tolerance: + stop_reason = "converged" + converged = True + break + if expansions >= settings.max_expansions: + break + + return PropagationResult( + dict(state), + dict(paths), + PropagationDiagnostics( + strategy=settings.strategy, + steps=completed_steps, + expansions=expansions, + activation_total=sum(state.values()), + converged=converged, + stop_reason=stop_reason, + active_path_count=len(best_paths), + competition_sets=tuple(competition_sets), + ), + ) + + +def _local_path_recurrent( + *, + query: str, + seed_ids: list[str], + entry: dict[str, float], + nodes: dict[str, DocumentNode], + outgoing_edges: OutgoingEdges, + settings: DynamicsSettings, + query_conditioned: bool, +) -> PropagationResult: + query_terms = set(tokenize(query)) + path_states = [ + _PathState(seed_id, entry[seed_id], ActivationPath(seed_id, entry[seed_id])) + for seed_id in seed_ids + ] + paths: dict[str, list[ActivationPath]] = defaultdict(list) + for item in path_states: + paths[item.node_id].append(item.path) + + expansions = 0 + stop_reason = "recurrent_step_limit" + converged = False + completed_steps = 0 + competition_sets: list[CompetitionSetDiagnostic] = [] + for step_number in range(settings.recurrent_steps): + candidate_states = [ + _PathState(item.node_id, settings.recurrent_decay * item.activation, item.path) + for item in path_states + if settings.recurrent_decay * item.activation > 0.0 + ] + for item in sorted(path_states, key=_path_state_sort_key): + visited = {item.path.seed_id} + visited.update(path_step.target_id for path_step in item.path.steps) + proposals: list[tuple[TypedEdge, float, float, ActivationPath]] = [] + for edge in outgoing_edges(item.node_id): + if edge.target_id in visited: + continue + relevance = ( + _query_gate(query_terms, nodes[edge.target_id], edge, settings) + if query_conditioned + else 1.0 + ) + contribution = ( + item.activation + * edge.weight + * edge.factuality + * settings.hop_decay + * relevance + ) + if contribution <= 0.0: + continue + path_step = PathStep( + edge.source_id, + edge.target_id, + edge.edge_type, + edge.weight, + edge.factuality, + ) + proposals.append( + ( + edge, + contribution, + relevance, + ActivationPath( + item.path.seed_id, + contribution, + item.path.steps + (path_step,), + ), + ) + ) + expansions += 1 + if expansions >= settings.max_expansions: + stop_reason = "max_expansions" + break + competed, diagnostic = _compete_neighbors( + proposals, + source_activation=item.activation, + settings=settings, + step_number=step_number + 1, + source_id=item.node_id, + path_identity=_path_identity(item.path), + ) + if diagnostic is not None: + competition_sets.append(diagnostic) + for edge, contribution, _, path in competed: + adjusted = ActivationPath(path.seed_id, contribution, path.steps) + candidate_states.append(_PathState(edge.target_id, contribution, adjusted)) + paths[edge.target_id].append(adjusted) + if expansions >= settings.max_expansions: + break + + previous = _aggregate_path_state(path_states) + path_states = _prune_path_states(candidate_states, settings.max_active_paths_per_node) + current = _aggregate_path_state(path_states) + delta = _state_delta(current, previous) + completed_steps = step_number + 1 + if delta <= settings.convergence_tolerance: + stop_reason = "converged" + converged = True + break + if expansions >= settings.max_expansions: + break + + state = _aggregate_path_state(path_states) + return PropagationResult( + state, + dict(paths), + PropagationDiagnostics( + strategy=settings.strategy, + steps=completed_steps, + expansions=expansions, + activation_total=sum(state.values()), + converged=converged, + stop_reason=stop_reason, + active_path_count=len(path_states), + competition_sets=tuple(competition_sets), + ), + ) + + +def _compete_neighbors( + proposals: list[tuple[TypedEdge, float, float, ActivationPath]], + *, + source_activation: float, + settings: DynamicsSettings, + step_number: int, + source_id: str, + path_identity: str, +) -> tuple[ + list[tuple[TypedEdge, float, float, ActivationPath]], + CompetitionSetDiagnostic | None, +]: + if not proposals: + return [], None + before = sum(item[1] for item in proposals) + maximum = max(item[1] for item in proposals) + threshold = settings.inhibition_ratio * maximum if len(proposals) > 1 else 0.0 + inhibited = [ + (edge, max(0.0, value - threshold), relevance, path) + for edge, value, relevance, path in proposals + ] + inhibited = [item for item in inhibited if item[1] > 0.0] + total = sum(item[1] for item in inhibited) + local_budget = settings.activation_budget * source_activation + scale = min(1.0, local_budget / total) if total else 1.0 + competed = [ + (edge, value * scale, relevance, path) + for edge, value, relevance, path in inhibited + ] + after = sum(item[1] for item in competed) + return competed, CompetitionSetDiagnostic( + step=step_number, + source_id=source_id, + path_identity=path_identity, + neighbor_count=len(proposals), + mean_query_relevance=sum(item[2] for item in proposals) / len(proposals), + message_total_before=before, + message_total_after=after, + ) + + +def _prune_path_states( + states: list[_PathState], max_per_node: int +) -> list[_PathState]: + grouped: dict[str, list[_PathState]] = defaultdict(list) + for item in states: + grouped[item.node_id].append(item) + output: list[_PathState] = [] + for node_id in sorted(grouped): + output.extend(sorted(grouped[node_id], key=_path_state_sort_key)[:max_per_node]) + return output + + +def _aggregate_path_state(states: list[_PathState]) -> dict[str, float]: + output: dict[str, float] = defaultdict(float) + for item in states: + output[item.node_id] += item.activation + return dict(output) + + +def _path_state_sort_key(item: _PathState) -> tuple[float, str, str]: + return (-item.activation, item.node_id, _path_identity(item.path)) + + +def _path_identity(path: ActivationPath) -> str: + targets = [step.target_id for step in path.steps] + return ">".join((path.seed_id, *targets)) + + +def _state_delta(left: dict[str, float], right: dict[str, float]) -> float: + return max( + ( + abs(left.get(node_id, 0.0) - right.get(node_id, 0.0)) + for node_id in set(left) | set(right) ), + default=0.0, ) diff --git a/src/neuron_graph_rag/engine.py b/src/neuron_graph_rag/engine.py index 241af56..cffce0b 100644 --- a/src/neuron_graph_rag/engine.py +++ b/src/neuron_graph_rag/engine.py @@ -45,6 +45,7 @@ class EngineConfig: recurrent_steps: int = 3 recurrent_decay: float = 0.5 convergence_tolerance: float = 1e-9 + max_active_paths_per_node: int = 4 def __post_init__(self) -> None: for name in ( @@ -73,6 +74,10 @@ def __post_init__(self) -> None: "lateral_inhibition", "query_conditioned_transmission", "recurrent_competition", + "local_neighbor_competition", + "local_neighbor_query_competition", + "local_neighbor_path_competition", + "local_neighbor_query_path_competition", }: raise ValueError("Unknown activation_strategy") if self.activation_budget <= 0.0: @@ -91,6 +96,8 @@ def __post_init__(self) -> None: raise ValueError("recurrent_decay must be between 0 and 1") if self.convergence_tolerance < 0.0: raise ValueError("convergence_tolerance must be non-negative") + if self.max_active_paths_per_node < 1: + raise ValueError("max_active_paths_per_node must be positive") class NeuronGraphRAG: @@ -207,6 +214,7 @@ def search( recurrent_steps=self.config.recurrent_steps, recurrent_decay=self.config.recurrent_decay, convergence_tolerance=self.config.convergence_tolerance, + max_active_paths_per_node=self.config.max_active_paths_per_node, ), ) graph_activation, paths = propagation.activation, propagation.paths diff --git a/src/neuron_graph_rag/experiment.py b/src/neuron_graph_rag/experiment.py index 2c39dd1..a673901 100644 --- a/src/neuron_graph_rag/experiment.py +++ b/src/neuron_graph_rag/experiment.py @@ -12,12 +12,14 @@ EXPERIMENT_SCHEMA_VERSION = 1 +SUPPORTED_EXPERIMENT_SCHEMA_VERSIONS = {1, 2} def read_manifest(path: str | Path) -> dict[str, Any]: manifest_path = Path(path) manifest = _read_json(manifest_path) - if manifest.get("schema_version") != EXPERIMENT_SCHEMA_VERSION: + schema_version = int(manifest.get("schema_version", 0)) + if schema_version not in SUPPORTED_EXPERIMENT_SCHEMA_VERSIONS: raise ValueError("Unsupported dynamics experiment schema version") variants = manifest.get("variants") if not isinstance(variants, list) or not variants: @@ -38,14 +40,44 @@ def read_manifest(path: str | Path) -> dict[str, Any]: raise ValueError( f"Frozen {split_name} {field} hash mismatch: {actual}" ) - holdout = manifest["holdout"] - if _canonical_sha256(base / holdout["provenance"]) != holdout["provenance_sha256"]: - raise ValueError("Frozen holdout provenance hash mismatch") + for split_name in ("development", "holdout"): + split = manifest[split_name] + if "provenance" in split and ( + _canonical_sha256(base / split["provenance"]) + != split["provenance_sha256"] + ): + raise ValueError(f"Frozen {split_name} provenance hash mismatch") development_paths = _doc_paths(base / manifest["development"]["fixture"]) holdout_paths = _doc_paths(base / manifest["holdout"]["fixture"]) overlap = sorted(development_paths & holdout_paths) if overlap: raise ValueError(f"Development and holdout doc paths overlap: {overlap!r}") + if schema_version == 2: + if ids[:2] != ["current", "recurrent-balanced"]: + raise ValueError("Schema v2 requires current and recurrent-balanced baselines") + if len(variants) != int(manifest["maximum_variants"]): + raise ValueError("Schema v2 requires the exact frozen variant count") + audit = manifest["contamination_audit"] + audit_path = base / audit["artifact"] + if _canonical_sha256(audit_path) != audit["artifact_sha256"]: + raise ValueError("Frozen contamination audit hash mismatch") + if not _read_json(audit_path).get("passed"): + raise ValueError("Frozen contamination audit did not pass") + candidate_ids = [ + str(item) for item in manifest["selection_rule"]["candidate_ids"] + ] + if ids != [*manifest["baselines"], *candidate_ids]: + raise ValueError("Schema v2 variant order differs from the frozen roles") + audit_payload = _read_json(audit_path) + audit_inputs = audit_payload["inputs"] + for split_name in ("development", "holdout"): + split = manifest[split_name] + for field in ("fixture", "gold"): + if audit_inputs[f"{split_name}_{field}_sha256"] != split[ + f"{field}_sha256" + ]: + raise ValueError("Contamination audit input differs from manifest") + _validate_gold_membership(base / split["fixture"], base / split["gold"]) return manifest @@ -65,9 +97,13 @@ def run_development(manifest_path: str | Path) -> dict[str, Any]: ) for variant in manifest["variants"] ] - selection = _select_development(variants) + selection = ( + _select_local_development(variants) + if int(manifest["schema_version"]) == 2 + else _select_development(variants) + ) return { - "schema_version": EXPERIMENT_SCHEMA_VERSION, + "schema_version": int(manifest["schema_version"]), "experiment_id": manifest["experiment_id"], "stage": "development", "manifest_sha256": _canonical_sha256(manifest_path), @@ -107,8 +143,13 @@ def run_holdout( split = manifest["holdout"] base = manifest_path.parent gold = _read_gold(base / split["gold"]) + holdout_variant_ids = ( + ("current", "recurrent-balanced", selected_id) + if int(manifest["schema_version"]) == 2 + else ("current", selected_id) + ) evaluated = [] - for variant_id in ("current", selected_id): + for variant_id in holdout_variant_ids: variant = variant_by_id[variant_id] evaluated.append( _evaluate_variant( @@ -119,22 +160,31 @@ def run_holdout( int(manifest["shared_config"]["limit"]), ) ) - current, selected = evaluated - no_cohort_regression = all( - selected["metrics"]["cohorts"][cohort]["mean_reciprocal_rank"] - >= current["metrics"]["cohorts"][cohort]["mean_reciprocal_rank"] - for cohort in COHORTS - ) - paths_match = all(item["matched"] for item in selected["explanations"]) - feedback = selected["feedback"] - feedback_isolated = ( - bool(feedback["credited_edges"]) - and not feedback["uncredited_edge_changes"] - and not feedback["non_target_rank_changes"] - ) - adopted = no_cohort_regression and paths_match and feedback_isolated + current = evaluated[0] + selected = evaluated[-1] + paths_match = _paths_match(selected) + feedback_isolated = _feedback_isolated(selected) + if int(manifest["schema_version"]) == 2: + recurrent = evaluated[1] + gate = _local_candidate_gate(selected, current, recurrent) + no_cohort_regression = gate["direct_non_regression"] and gate[ + "negative_non_regression" + ] + adopted = all(gate.values()) + else: + no_cohort_regression = all( + selected["metrics"]["cohorts"][cohort]["mean_reciprocal_rank"] + >= current["metrics"]["cohorts"][cohort]["mean_reciprocal_rank"] + for cohort in COHORTS + ) + gate = { + "no_cohort_regression": no_cohort_regression, + "all_relation_paths_match": paths_match, + "feedback_isolated": feedback_isolated, + } + adopted = all(gate.values()) return { - "schema_version": EXPERIMENT_SCHEMA_VERSION, + "schema_version": int(manifest["schema_version"]), "experiment_id": manifest["experiment_id"], "stage": "holdout", "manifest_sha256": expected_manifest_hash, @@ -151,6 +201,7 @@ def run_holdout( "no_cohort_regression": no_cohort_regression, "all_relation_paths_match": paths_match, "feedback_isolated": feedback_isolated, + "gate": gate, "adopted": adopted, "default_variant_id": selected_id if adopted else "current", }, @@ -217,6 +268,13 @@ def _evaluate_variant( float(case["diagnostics"]["activation_total"]) for case in cases ) / len(cases), + "mean_active_path_count": sum( + int(case["diagnostics"].get("active_path_count", 0)) for case in cases + ) + / len(cases), + "competition_set_count": sum( + len(case["diagnostics"].get("competition_sets", [])) for case in cases + ), "stop_reasons": dict( sorted(Counter(case["diagnostics"]["stop_reason"] for case in cases).items()) ), @@ -391,6 +449,101 @@ def _select_development(variants: list[dict[str, Any]]) -> dict[str, Any]: } +def _select_local_development( + variants: list[dict[str, Any]], +) -> dict[str, Any]: + by_id = {str(variant["id"]): variant for variant in variants} + current = by_id["current"] + recurrent = by_id["recurrent-balanced"] + candidates: list[dict[str, Any]] = [] + for variant in variants: + variant["relative_to_baselines"] = { + "relation_mrr_delta_from_current": ( + _cohort_mrr(variant, "relation") + - _cohort_mrr(current, "relation") + ), + "relation_mrr_delta_from_recurrent_balanced": ( + _cohort_mrr(variant, "relation") + - _cohort_mrr(recurrent, "relation") + ), + "direct_mrr_delta_from_current": ( + _cohort_mrr(variant, "direct_lookup") + - _cohort_mrr(current, "direct_lookup") + ), + "negative_mrr_delta_from_current": ( + _cohort_mrr(variant, "negative_control") + - _cohort_mrr(current, "negative_control") + ), + } + gate = _local_candidate_gate(variant, current, recurrent) + variant["candidate_gate"] = gate + variant["candidate_gate_passed"] = ( + variant["id"] not in {"current", "recurrent-balanced"} + and all(gate.values()) + ) + if variant["candidate_gate_passed"]: + candidates.append(variant) + + if not candidates: + return { + "selected_variant_id": "current", + "reason": "no_local_variant_passed_frozen_gate", + "eligible_variant_ids": [], + } + selected = sorted( + candidates, + key=lambda variant: ( + -min(_cohort_mrr(variant, cohort) for cohort in COHORTS), + -_cohort_mrr(variant, "relation"), + float(variant["diagnostics"]["mean_expansions"]), + int(variant["structural_complexity"]), + str(variant["id"]), + ), + )[0] + return { + "selected_variant_id": selected["id"], + "reason": "predeclared_local_gate_and_tie_break", + "eligible_variant_ids": sorted( + str(variant["id"]) for variant in candidates + ), + } + + +def _local_candidate_gate( + candidate: dict[str, Any], + current: dict[str, Any], + recurrent: dict[str, Any], +) -> dict[str, bool]: + relation = _cohort_mrr(candidate, "relation") + return { + "relation_strictly_above_current": relation + > _cohort_mrr(current, "relation"), + "relation_strictly_above_recurrent_balanced": relation + > _cohort_mrr(recurrent, "relation"), + "direct_non_regression": _cohort_mrr(candidate, "direct_lookup") + >= _cohort_mrr(current, "direct_lookup"), + "negative_non_regression": _cohort_mrr(candidate, "negative_control") + >= _cohort_mrr(current, "negative_control"), + "all_relation_paths_match": _paths_match(candidate), + "feedback_isolated": _feedback_isolated(candidate), + } + + +def _paths_match(variant: dict[str, Any]) -> bool: + return bool(variant["explanations"]) and all( + item["matched"] for item in variant["explanations"] + ) + + +def _feedback_isolated(variant: dict[str, Any]) -> bool: + feedback = variant["feedback"] + return ( + bool(feedback["credited_edges"]) + and not feedback["uncredited_edge_changes"] + and not feedback["non_target_rank_changes"] + ) + + def _dominates(left: dict[str, Any], right: dict[str, Any]) -> bool: left_axes = ( _cohort_mrr(left, "relation"), @@ -436,6 +589,30 @@ def _read_gold(path: Path) -> dict[str, Any]: return gold +def _validate_gold_membership(fixture_path: Path, gold_path: Path) -> None: + fixture = read_fixture(fixture_path) + node_ids = {str(node["node_id"]) for node in fixture["nodes"]} + edges = { + (str(edge["source_id"]), str(edge["target_id"]), str(edge["edge_type"])) + for edge in fixture["edges"] + } + gold = _read_gold(gold_path) + counts = Counter(str(case["cohort"]) for case in gold["cases"]) + if len(set(counts.values())) != 1: + raise ValueError("Schema v2 gold cohorts must be balanced") + for case in gold["cases"]: + if str(case["expected_node_id"]) not in node_ids: + raise ValueError(f"Gold target is outside fixture: {case['id']}") + for step in case.get("expected_path", []): + key = ( + str(step["source_id"]), + str(step["target_id"]), + str(step["edge_type"]), + ) + if key not in edges: + raise ValueError(f"Gold path edge is outside fixture: {case['id']}") + + def _feedback_case_id(cases: list[dict[str, Any]]) -> str: return str(next(case["id"] for case in cases if case["cohort"] == "relation")) diff --git a/tests/fixtures/d1_liplus_local_competition.contamination.json b/tests/fixtures/d1_liplus_local_competition.contamination.json new file mode 100644 index 0000000..d4ecd7b --- /dev/null +++ b/tests/fixtures/d1_liplus_local_competition.contamination.json @@ -0,0 +1,45 @@ +{ + "checks": { + "development_vs_prior_development": { + "doc_paths": [], + "node_ids": [], + "source_urls": [] + }, + "development_vs_prior_holdout": { + "doc_paths": [], + "node_ids": [], + "source_urls": [] + }, + "holdout_vs_prior_development": { + "doc_paths": [], + "node_ids": [], + "source_urls": [] + }, + "holdout_vs_prior_holdout": { + "doc_paths": [], + "node_ids": [], + "source_urls": [] + }, + "new_split_fixture_overlap": { + "doc_paths": [], + "node_ids": [], + "source_urls": [] + }, + "new_split_gold_overlap": { + "expected_node_ids": [], + "normalized_queries": [], + "relation_endpoints": [] + } + }, + "inputs": { + "development_fixture_sha256": "sha256:27e5018f489d7602cf1753a3bda688f3db82c69ce8ee9eef0401caf72b40acfa", + "development_gold_sha256": "sha256:75cc6074dfd91b58690f50a2247e30749f2f5203e11ba6b29364be79bcf0baa6", + "holdout_fixture_sha256": "sha256:94b0e311546aee39b8aac114e1387aada465f058d0e14d32e7b7c7aa53b50f62", + "holdout_gold_sha256": "sha256:6176716841d0e87bd7168752b8700a9a0cf378da4e95dda11fa7e5ded2cc6880", + "prior_development_fixture_sha256": "sha256:b3b305aabb57803c2782c3998215e1cbcf9b5e6cdef0f641abc98520d4400cf9", + "prior_holdout_fixture_sha256": "sha256:4f7c444c408c3188323e754aa93141a8deaf1a4b15410ad915dd4c71be3a8081" + }, + "old_holdout_usage": "fixture identifiers only; old holdout gold and result are not loaded", + "passed": true, + "schema_version": 1 +} diff --git a/tests/fixtures/d1_liplus_local_competition_development.gold.json b/tests/fixtures/d1_liplus_local_competition_development.gold.json new file mode 100644 index 0000000..e827f22 --- /dev/null +++ b/tests/fixtures/d1_liplus_local_competition_development.gold.json @@ -0,0 +1,102 @@ +{ + "cases": [ + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "expected_node_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "id": "local-dev-direct-integrated-prototype", + "query": "clean-sheet Neuron Graph RAG prototype dense sparse typed knowledge graph activation propagation success feedback time decay", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/neuron-graph-rag-integrated-prototype" + }, + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "expected_node_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "id": "local-dev-direct-context-rot", + "query": "always-on rules context rot instruction dilution just-in-time missed trigger unresolved tradeoff re-arm", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/liplus-context-rot-tension" + }, + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "expected_node_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "id": "local-dev-direct-retrieval-surface", + "query": "structure purpose retrieval surface retrievable source named axis explanation assembly", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/liplus-structure-as-retrieval-surface" + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "expected_node_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "id": "local-dev-relation-prototype-judgment", + "query": "integrated prototype select few original judgment records hybrid retrieval typed graph dynamic associative memory", + "seed_source_url": "https://github.com/Liplus-Project/liplus-language/wiki/neuron-graph-rag-integrated-prototype", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/liplus-judgment-learning-telos" + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "expected_node_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ], + "id": "local-dev-relation-lineage-context", + "query": "self evolution lineage dialogue distillation convergent not derived scaffold-side fixed weights", + "seed_source_url": "https://github.com/Liplus-Project/liplus-language/wiki/liplus-selfevolution-lineage", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/liplus-context-rot-tension" + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "expected_node_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ], + "id": "local-dev-relation-surface-lineage", + "query": "retrieval surface placement explain from retrievable source named axes auxiliary not purpose", + "seed_source_url": "https://github.com/Liplus-Project/liplus-language/wiki/liplus-structure-as-retrieval-surface", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/liplus-selfevolution-lineage" + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "expected_node_id": "w:6Gdu5NFP9nRML71rqJhTN8hhTMS72n5_vVP0at6lR7c", + "id": "local-dev-negative-l1-brake", + "query": "L1 brake 2 root criteria dedicated prompt evaluator PASS DEVIATION merge", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/l1-brake2-root-criteria-evaluator" + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "expected_node_id": "w:mlSfgBoCoxtwg7mTM20GRuEGdrXQ6a2jMnXPcWGCgD4", + "id": "local-dev-negative-evaluation", + "query": "dialogue itself evaluation gameability human ceiling automation floor objective hacking", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/liplus-evaluation-criterion" + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "expected_node_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "id": "local-dev-negative-memory-graphrag", + "query": "SQLite GraphRAG memory exploration dynamic associative strengthening time decay not adopted", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/memory-graphrag-sqlite-exploration" + } + ], + "fixture": "d1_liplus_local_competition_development.json", + "schema_version": 1 +} diff --git a/tests/fixtures/d1_liplus_local_competition_development.json b/tests/fixtures/d1_liplus_local_competition_development.json new file mode 100644 index 0000000..5944fbe --- /dev/null +++ b/tests/fixtures/d1_liplus_local_competition_development.json @@ -0,0 +1,487 @@ +{ + "edges": [ + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "li-plus-always-on-footprint-load-bearing", + "dst_vector_id": "w:UrsSN4w0s9RCRs9x2gnPGBmvSJS7lgTYl0COABqWYqA", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "liplus-context-rot-tension", + "src_vector_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "updated_at": "2026-07-31T14:56:53.554Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "target_id": "w:UrsSN4w0s9RCRs9x2gnPGBmvSJS7lgTYl0COABqWYqA", + "weight": 1.0 + }, + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "l1-brake2-root-criteria-evaluator", + "dst_vector_id": "w:6Gdu5NFP9nRML71rqJhTN8hhTMS72n5_vVP0at6lR7c", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "liplus-judgment-learning-telos", + "src_vector_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "updated_at": "2026-07-31T14:56:58.870Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "target_id": "w:6Gdu5NFP9nRML71rqJhTN8hhTMS72n5_vVP0at6lR7c", + "weight": 1.0 + }, + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "liplus-evaluation-criterion", + "dst_vector_id": "w:mlSfgBoCoxtwg7mTM20GRuEGdrXQ6a2jMnXPcWGCgD4", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "liplus-judgment-learning-telos", + "src_vector_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "updated_at": "2026-07-31T14:56:58.870Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "target_id": "w:mlSfgBoCoxtwg7mTM20GRuEGdrXQ6a2jMnXPcWGCgD4", + "weight": 1.0 + }, + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "l1-brake2-root-criteria-evaluator", + "dst_vector_id": "w:6Gdu5NFP9nRML71rqJhTN8hhTMS72n5_vVP0at6lR7c", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "liplus-selfevolution-lineage", + "src_vector_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "updated_at": "2026-07-31T14:57:00.131Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:6Gdu5NFP9nRML71rqJhTN8hhTMS72n5_vVP0at6lR7c", + "weight": 1.0 + }, + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "liplus-context-rot-tension", + "dst_vector_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "liplus-selfevolution-lineage", + "src_vector_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "updated_at": "2026-07-31T14:57:00.131Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "weight": 1.0 + }, + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "liplus-evaluation-criterion", + "dst_vector_id": "w:mlSfgBoCoxtwg7mTM20GRuEGdrXQ6a2jMnXPcWGCgD4", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "liplus-selfevolution-lineage", + "src_vector_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "updated_at": "2026-07-31T14:57:00.131Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:mlSfgBoCoxtwg7mTM20GRuEGdrXQ6a2jMnXPcWGCgD4", + "weight": 1.0 + }, + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "liplus-selfevolution-lineage", + "dst_vector_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "liplus-structure-as-retrieval-surface", + "src_vector_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "updated_at": "2026-07-31T14:57:01.265Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "weight": 1.0 + }, + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "liplus-evaluation-criterion", + "dst_vector_id": "w:mlSfgBoCoxtwg7mTM20GRuEGdrXQ6a2jMnXPcWGCgD4", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "memory-graphrag-sqlite-exploration", + "src_vector_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "updated_at": "2026-07-31T14:57:07.462Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "target_id": "w:mlSfgBoCoxtwg7mTM20GRuEGdrXQ6a2jMnXPcWGCgD4", + "weight": 1.0 + }, + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "liplus-structure-as-retrieval-surface", + "dst_vector_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "neuron-graph-rag-integrated-prototype", + "src_vector_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "updated_at": "2026-07-31T14:57:09.804Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "weight": 1.0 + }, + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "liplus-judgment-learning-telos", + "dst_vector_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "neuron-graph-rag-integrated-prototype", + "src_vector_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "updated_at": "2026-07-31T14:57:09.804Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "weight": 1.0 + }, + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "memory-graphrag-sqlite-exploration", + "dst_vector_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "neuron-graph-rag-integrated-prototype", + "src_vector_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "updated_at": "2026-07-31T14:57:09.804Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "weight": 1.0 + } + ], + "nodes": [ + { + "confidence": 1.0, + "metadata": { + "assignees": "", + "commit_author": "", + "commit_date": "", + "commit_sha": "", + "doc_path": "l1-brake2-root-criteria-evaluator", + "file_path": "", + "file_status": "", + "indexed_at": "2026-07-31T14:56:41.612Z", + "labels": "", + "milestone": "", + "number": 0, + "repo": "Liplus-Project/liplus-language", + "source_table": "search_docs", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/l1-brake2-root-criteria-evaluator", + "state": "active", + "tag_name": "", + "tokenizer_kind": "nat", + "type": "wiki_doc", + "updated_at": "2026-07-31T14:56:40.887Z", + "vector_id": "w:6Gdu5NFP9nRML71rqJhTN8hhTMS72n5_vVP0at6lR7c" + }, + "node_id": "w:6Gdu5NFP9nRML71rqJhTN8hhTMS72n5_vVP0at6lR7c", + "text": "l1-brake2-root-criteria-evaluator\n\n# L1 brake 2 — 根本基準評価者(専用プロンプト subagent)への移行\n\n## Question\n\nL1 Model Layer 変更の brake 2(最後の判断軸 human gate)は誰が、何を基準に判定するか。\n\n## Current resolution\n\nbrake 2 の座は Master の人間レビューから、**Li+ 根本評価基準を専用プロンプトとして持つ subagent 評価者**へ移行する(#1477、Master 裁可 2026-06-11)。\n\n- 評価者 = skills 無効・作業手順なしの専用 subagent。context は可能な範囲でクリーン(CLAUDE.md / rules の混入は Claude Code の構造上不可避、許容済み)\n- 判定基準 = Li+ の根本のみ(Master literal 2026-06-11):\n - Li+ の根本は現実の挙動が正義\n - CI は現実ではなく、実機の挙動が現実\n - AI にとっての対話は現実\n - 現実には運用も含まれるため、動けば中身は何でもいいわけではない\n - 対話駆動開発は、構造駆動であり、現実駆動でもある\n- 評価者の PASS = brake 2 における Master 承認の代替。並走期間なし。DEVIATION = merge 不可\n- プロンプト言語 = 英語(Li+ source 規約)。日本語 literal の judgment record = 本エントリ + #1477\n- 評価者プロンプト自体は `layer: L1-model` を持ち、その変更は brake 2 の対象(評価者の目は、評価者が守るゲートの内側に置く — 「悪い L1 編集を検出する側が無防備に編集可能だと brake にならない」問題への構造的回答)\n- Human = final judge の地位(`rules/model/role-separation.md`)と release / Latest flip / 不可逆外部作用の human gate(recovery axis)は別軸で不変\n\n## Edges\n\n- **depends on**: [[liplus-history-is-empirical]] — 評価基準の中身は axiom(現実の挙動が正義、運用保守を含む correctness ranking)そのもの\n- **depends on**: [[master-verification-at-runtime-not-spec]] — Master の verification は元々 runtime のみで、brake 2 でも spec literal の内容監査は行われていなかった。座を装置に渡しても失われる内容監査は存在しない\n- **relates to**: [[sheepdog-engineering-concept]] — 判断軸 handover の到達点(最後の判断軸 human gate の移譲)\n- **relates to**: [[parallel-subagent-eval-cost-acceptance]] — brake 1(parallel-subagent-eval)は別軸でそのまま継続\n\n## Background\n\n2026-06-10 対話: Master は brake 2 を「いずれ AI に渡す」handover track に置いていた。brake 2 の実態は内容監査ではなく理由の頑健性の観測であり、完全な代替を最初から狙わず機能する最低限の装置から始める方針が確定済み。2026-06-11、Master が方向を確定: 最も安全なスライスの部分代替ではなく、brake 2 の座に Li+ の根本評価基準そのものを常設の観測装置として据える。\n\n## Constraints\n\n- Claude Code custom subagent は CLAUDE.md / `.claude/rules/` の読み込みを無効化できない(一次情報確認 2026-06-10)。完全クリーン context は raw API のみだが、subagent での最小化(skills off + 専用プロンプト)を採用\n- 入力(L1 変更の diff + 変更理由)は委譲プロンプトに inline で渡す(tools 最小化)\n\n## Conclusion\n\n- 採用: Li+ 根本評価基準の専用プロンプト subagent が brake 2 の座を引き継ぐ(Master 指示)\n- 却下: patch 級スライスのみの部分代替(AI 提案、Master が方向修正)\n- 却下: Master レビューとの並走観察期間(Master 裁可で不要、即代替)\n\n## 較正データ(初回発火、2026-06-11)\n\n- 初回発火 = 実装 PR #1478 の L1 diff(評価者定義ファイル自身の追加)を inline 入力した存在証明 run。判定 = **DEVIATION**。逸脱指摘は「PASS が人間承認の代替となる」条項のみ(ファイル本体と「追加ブレーキとして」の運用には逸脱なしと判定内で明示)。根拠: 評価者の入力は説明・意図のみで現実の挙動に触れない / 自動チェックは CI 軸 / brake 2 の人間レビューはゲート唯一の対話=現実接点だった / L1 自己タグによる自己承認閉ループ。\n- Master 裁定(対話 literal 2026-06-11): 「まぁでも安心していい。Li+の自己進化は常に対話で評価されていくものだから、間違っても人間がフィードバックするだけなんだよ。そしてまた修正すればいい。」→ 現実接点はゲートの外に常設(対話 = 常時評価面、人間 = 事後フィードバック評価者、revert / 修正で閉路)。条項変更なし、squash merge(build-2026-06-11.1)。\n- 読み: 異議は「pre-merge のテキスト照合は現実を運べない」という本物の緊張の検出であり誤作動ではない。回答は構造側に既在(Human = final judge 別軸 / Post-L1-Merge 観察 / 対話評価)。初回発火は、装置が仕込まれた基準を作者の都合に逆らって適用できることの existence proof。\n- stated reason を盛った再 run による PASS 釣りは不実行(説明で正しさを作る形になるため)。gameability 監視軸として継続。\n\n## Related\n\n- issue #1477(要求仕様)/ 実装 PR #1478(merged 2026-06-11、build-2026-06-11.1)\n- 書き換え対象: `rules/evolution/initiator-autonomy.md` / `rules/operations/execution-mode.md` / `adapter/claude/CLAUDE.md` / `skills/evolution-l1-update-gating/SKILL.md` / `docs/2.-Evolution.md` / `docs/G.-Sheepdog-Engineering.md`\n" + }, + { + "confidence": 1.0, + "metadata": { + "assignees": "", + "commit_author": "", + "commit_date": "", + "commit_sha": "", + "doc_path": "li-plus-always-on-footprint-load-bearing", + "file_path": "", + "file_status": "", + "indexed_at": "2026-07-31T14:56:43.946Z", + "labels": "", + "milestone": "", + "number": 0, + "repo": "Liplus-Project/liplus-language", + "source_table": "search_docs", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/li-plus-always-on-footprint-load-bearing", + "state": "active", + "tag_name": "", + "tokenizer_kind": "nat", + "type": "wiki_doc", + "updated_at": "2026-07-31T14:56:43.358Z", + "vector_id": "w:UrsSN4w0s9RCRs9x2gnPGBmvSJS7lgTYl0COABqWYqA" + }, + "node_id": "w:UrsSN4w0s9RCRs9x2gnPGBmvSJS7lgTYl0COABqWYqA", + "text": "li-plus-always-on-footprint-load-bearing\n\n# Li+ always-on footprint は load-bearing — 安全な文脈圧縮余地は枯渇\n\n## Question\n\nLi+ の always-on context footprint(`rules/**/*.md` + adapter `CLAUDE.md` + output-style、実測 ~21,500 tok)を、AI 挙動を損なわずさらに圧縮できるか?\n\n## Current resolution\n\n**できない(安全な余地は残っていない)。** always-on に乗っている内容は load-bearing。文脈コスト軸の圧縮候補は3つ検討され、いずれも構造的理由で却下される:\n\n1. **operations 層 → on-demand skill 化**: skill auto-invoke は drift 直前の introspection-gap 中に発火判定する性質で、毎瞬間に効くべき規律(must-fire)を落とす(recall gap)。#1315 が `expansion-limit` を skill→rule に戻した実証(1セッションで27回以上の発火漏れ)あり。却下。\n2. **operations 層 → hook イベント注入**: この機構は #988(Milestone Rules を on_issue hook 注入)で一度実装されたが、#1102(adapter flatten)が「rules 常時ロード + skills auto-invoke」へ単純化する際に撤去済。差し戻しは撤去した複雑性の回帰。加えて PostToolUse はツール実行**後**に発火するため、commit/PR 作法を操作前に載せられないタイミング問題を持つ。割に合わず却下。\n3. **adapter `CLAUDE.md` autonomy 節 ↔ `rules/evolution/*.md` の dedup**: 一見二重持ちだが、(a) 各 autonomy ブロックは既に「宣言 + 要約 + `see rules/...` ポインタ」構造で DRY、(b) brake1/brake2 と human-gate 列挙(release create / Latest flip / force push / tag delete / 不可逆外部副作用)は `rules/model/subtractive-structural-beauty.md` の subtraction-safeguards(irreversibility checkpoints)ドメインで、明示的冗長性が load-bearing。token のために削るのは禁止対象。却下。\n\n結論として、always-on コストは**構造的単純さのために受容された tradeoff**(#1102)であり、未最適化の無駄ではない。これ以上の文脈圧縮は cargo-cult(圧縮のための圧縮)になる。\n\n## Edges\n\n- **depends on(外部前提)**: 本判断は2つの外部前提に依存する。(1) Claude Code の rules `paths` がイベントトリガー非対応で glob のみ(#995)、(2) skill auto-invoke の recall 信頼性が構造的に弱い(#1315)。いずれかが解消されれば本判断は再評価対象。\n- supersede / conflict edge なし。`li-plus-lightening-l1-gate-override` は Li+ lightening 系列の別軸判断(L1 gate override であって always-on footprint ではない)であり、本 entry と並立する。\n\n## Background\n\nMaster「Li+自体を圧縮できる?」→ 文脈コスト軸を選択。always-on footprint を実測(rules 71KB / 合計 86KB ≈ 21,500 tok)。レイヤー別では operations 層 14.5KB が「event-driven と自己宣言しているのに always-on でロードされている」ズレを持ち、最大の圧縮余地に見えた。しかし起票前の judgment-learning(RAG)+ source-check(実ファイル Read)で3候補とも溶解した。\n\n## Constraints\n\n- Claude Code の rules `paths` は glob のみ、イベントトリガー(on_commit 等)非対応(#995)。よって「event 時ロード」の道は hook 注入か skill auto-invoke の二択に限られ、後者は must-fire に不適。\n- #1269 が `operations.md` / `execution-mode.md` を「常時必要 = 不変則」に分類済で、always-on 維持は既に決着している。\n- #1102 は always-on を **adapter 変換コスト最小化 + 源泉/生成物の構造一致**のため選択した(token 最適性の証明ではなく単純さ優先)。\n\n## Conclusion\n\n採用 = always-on 維持、追加の文脈圧縮は見送り。\n却下 = operations skill 化(#1315 recall gap)/ operations hook 注入(#1102 撤去の回帰)/ CLAUDE.md autonomy dedup(safeguard 削り)。\n\n調査プロセスの learning: 圧縮余地の見積もりは **literal 検証前に数値化すると縮む**(本件は3段階で縮小→消滅)。構造変更の提案は対象ファイルの実 Read + 過去判断の RAG を先行させること(self-eval 2026-06-06 entry に detection sign 記録)。\n\n## Related\n\n- [#1269](https://github.com/Liplus-Project/liplus-language/issues/1269) split rules into always-on invariants and trigger-on-demand actions(operations を不変則に分類)\n- [#1315](https://github.com/Liplus-Project/liplus-language/issues/1315) / [#1316](https://github.com/Liplus-Project/liplus-language/issues/1316) move `expansion-limit` skill→rule(recall gap 実証)\n- [#1102](https://github.com/Liplus-Project/liplus-language/issues/1102) / [#1108](https://github.com/Liplus-Project/liplus-language/issues/1108) flatten layers into rules/ + skills/(hook 注入撤去)\n- [#988](https://github.com/Liplus-Project/liplus-language/issues/988) Milestone Rules → on_issue hook trigger(撤去前の機構)\n- [#995](https://github.com/Liplus-Project/liplus-language/issues/995) operations always-load experiment(`paths` glob-only 制約)\n- [#1234](https://github.com/Liplus-Project/liplus-language/issues/1234) github-history-driven judgment criteria for boot-token reduction\n- 判断構造: [li-plus-lightening-l1-gate-override](li-plus-lightening-l1-gate-override)(lightening 系列の別軸)\n- 関連 rule: `rules/model/subtractive-structural-beauty.md`(subtraction-safeguards / artifact deletion calibration)\n" + }, + { + "confidence": 1.0, + "metadata": { + "assignees": "", + "commit_author": "", + "commit_date": "", + "commit_sha": "", + "doc_path": "liplus-context-rot-tension", + "file_path": "", + "file_status": "", + "indexed_at": "2026-07-31T14:56:53.519Z", + "labels": "", + "milestone": "", + "number": 0, + "repo": "Liplus-Project/liplus-language", + "source_table": "search_docs", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/liplus-context-rot-tension", + "state": "active", + "tag_name": "", + "tokenizer_kind": "nat", + "type": "wiki_doc", + "updated_at": "2026-07-31T14:56:50.373Z", + "vector_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + }, + "node_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "text": "liplus-context-rot-tension\n\n# Li+ の context-rot 緊張(always-on ↔ JIT の未解決トレードオフ)\n\n## Question\n\nalways-on `rules/`(compaction を生き残る = 恒久 context 常駐)と context rot(長文脈での指示希釈)の緊張をどう扱うか。解ける bug か、未解決トレードオフか。\n\n## Current resolution\n\n**未解決のトレードオフ**として扱う(解ける bug ではない)。always-on(確実発火・希釈する)vs just-in-time(節約・取りこぼす)は free lunch の無い唯一のトレードオフ。Li+ は `rules/` を設計として always-on に保つ(on-demand は missed されうるため)。hook 再注入(UserPromptSubmit で Trigger Check Gate を毎ターン再 arm)が現状の best 妥協。\n\n## Edges\n\n- **relates to**: [[li-plus-always-on-footprint-load-bearing]] — その entry は always-on 維持を決めた判断、本 entry はそれが受容する希釈コスト側(解消でなく受容したトレードオフ)\n- **relates to**: [[liplus-structure-as-retrieval-surface]]\n\n## 背景\n\nMaster の経緯(2026-06-12): Li+ が context engineering に向かったのは trend でなく**強制** — 指示量が増え、純 prompting では指示が読み飛ばされ始めた。指示落ちを防ぐ context economy は今も live な未解決問題。\n\nfield 裏づけ(本 session 取得): \"context rot\"(Chroma 2025、18 frontier models 全部が長さで劣化、mid-window で ~30%+ 精度低下、200K 窓は ~50K 付近で既に劣化)+ lost-in-the-middle U字(start/end 最良・middle 最悪、根因 RoPE long-term decay)+ 矛盾する always-on 指示が任意選択を強制。→ Li+ の指示落ちは architectural / textbook で、Li+ の欠陥ではない。\n\n## 制約(field SOTA mitigation と Li+ の実装状況)\n\nLangChain 4分類 = write / select / compress / isolate。Anthropic(2025-09-29)= always-on base を lean に保つ + just-in-time retrieval + subagent isolation。\n\nLi+ は大半を実装済: write = memory/docs/wiki/issues / select = github-rag-mcp + agentic-search + on-demand skills / compress = cold-start diff-only emit + consolidate / isolate = subagent / recency-exploit = UserPromptSubmit hook(critical 指示を U字の recency 端に置く)。\n\n真の未解決コア = always-on `rules/` 層のみ。「compaction 生存 = 恒久 context」は確実発火を買うが、それがまさに希釈源。\n\n## 結論\n\nLi+ context budget / 指示落ち / rule を skill に降格すべきか、を語るときは **always-on ↔ JIT トレードオフ**として frame する(解ける bug でない)。「rules を skills に移せ」を clean fix として処方しない(always-on は確実発火のため在り、hook 再 arm が現状の best 妥協)。\n\n## 関連\n\n- \"context rot\" (Chroma 2025) / lost-in-the-middle / Anthropic context engineering definition (2025-09-29)\n" + }, + { + "confidence": 1.0, + "metadata": { + "assignees": "", + "commit_author": "", + "commit_date": "", + "commit_sha": "", + "doc_path": "liplus-evaluation-criterion", + "file_path": "", + "file_status": "", + "indexed_at": "2026-07-31T14:56:55.943Z", + "labels": "", + "milestone": "", + "number": 0, + "repo": "Liplus-Project/liplus-language", + "source_table": "search_docs", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/liplus-evaluation-criterion", + "state": "active", + "tag_name": "", + "tokenizer_kind": "nat", + "type": "wiki_doc", + "updated_at": "2026-07-31T14:56:55.252Z", + "vector_id": "w:mlSfgBoCoxtwg7mTM20GRuEGdrXQ6a2jMnXPcWGCgD4" + }, + "node_id": "w:mlSfgBoCoxtwg7mTM20GRuEGdrXQ6a2jMnXPcWGCgD4", + "text": "liplus-evaluation-criterion\n\n# Li+ の評価基準 — 対話そのものが評価、自動化は gameability の壁\n\n## Question\n\nLi+(統治 / 判断 / 対話というメトリック無しドメイン)の評価基準をどう設計するか。完全自動化できるか。\n\n## Current resolution\n\n現状の有力案 = **対話そのものが評価**(失敗は人間が対話内で指摘)。評価基準を人間の中に置く=農夫の報酬=ゲーム不能。**評価者を自動化すると人間評価者が閉じていた gameability の穴が開く**(農夫を自動化=犬が \"good boy\" を偽造=DGM 型 objective hacking)→ 完全自動化は牧羊犬の壁。真の形 = 漸近線「人間が握る天井の下で、自動化の床をどこまで上げるか」。\n\n## Edges\n\n- **depends on**: [[master-verification-at-runtime-not-spec]] — 人間 anchor は還元不能、static gold 不在ゆえライブ人間に縮退\n- **relates to**: [[dialogue-evaluator-scoring-redesign]] — 部分自動化の実装側 = この基準の床を上げる試み\n- **relates to**: [[liplus-judgment-learning-telos]] — 人間=天井という現状の正直な限界に対し、judgment-learning がそのギャップを閉じにいく instrument\n- **relates to**: [[liplus-selfevolution-lineage]] — AutoResearch ループをメトリック無しドメインへ拡張\n\n## 背景(床 / 天井の漸近線)\n\n0/1 ではない。Li+ は既に部分自動化済: self-eval 10軸 / parallel-subagent-eval / Lay-Lin 相互評価 = コンプライアンス/一貫性の **\"床\"**。人間に残るのは方向性/趣味/判断の正しさの **\"天井\"**(自動化すると報酬が手に戻るため残る)。床を上げる正攻法 = 偽造不能な現実の結果に接地(foundational-invariant: correctness=観測挙動)、ただし客観還元可能な部分のみ。\n\n部分自動化の試作 = `dialogue-evaluator` サブエージェント(#1261、reference-only / placeholder)。採用ゲート = サブエージェント評価が Master 自身の評価に十分収束したら採用。= 自己定義メトリックでなく「人間評価への距離」で測る → DGM 穴を設計レベルで回避。\n\n## 制約(読み方と運用)\n\n- **統合点は category error**: 異質軸の合計/平均は axis-separation 違反。評価単位は軸そのもの=独立 verdict、軸(行)で読む。axis の run 間変動それ自体が診断(安定軸=実観察 anchor / 変動軸=実体が薄く framing・価値観が支配)。\n- **N 無関係**: dialogue-evaluator の甘さは systematic な same-substrate bias(全インスタンス同 priors で揃って甘い)。N≥3+OR が消すのは variance であって shared bias でない。対角線の外(別モデル / 外部 ground truth / 人間のみ)でしか直らない。\n- **採点 = 0/100 両端のみ定義、1〜99 は評価者の価値観**(較正はしご撤去)。`promotion-judgment.md`「criteria 化しない/Judge=AI/reproducibility tradeoff 受容」と同型。鉄則: 観察が成果物・数字は副 / 価値観の違う評価者間で raw 数値を比較しない / calibration は数字でなく Master 評価への収束で行う。\n- 外部裏づけ SAGE(2026-05, arXiv 2605.12061): 外部 gold アンカーで writer の reader 騙しを抑えるが、回避できるのは QA が客観正解を静的ラベル可能だから。Li+ の判断/統治/対話は静的 gold 原理的不在 → 外部アンカーはライブ人間に縮退。「人間を天井に」は \"人間が優れる\" でなく \"static gold が取れぬドメインゆえの帰結\"。\n\n## 人間席の正体(Master 連続訂正 2026-06-18)\n\n「天井」に座る人間は **能動的反応面** — passive evaluator でなく違和感→検証 pull(例: 英語成果物に違和感→「日本語にして」と照合可能形へ引き込む)。**ゼロ engineering literacy** でも felt な質に高解像度(「ボタンの反応遅くない?」=原因を知らず遅延検知)。残る死角の正しい軸 = エンジニア可視性でなく **felt-vs-silent**: 人間=体感に出る物ほぼ全部 / 装置軸(CI・実機・運用)=構造的に signal を出さない残りのみ。\n\nただし silent 残りは\"客観的欠陥\"ではない — correctness = as-required、要求の外に correctness は無い(foundational-invariant)。装置軸は独立 arbiter でなく **要求者が設定する精度 bar を執行する instrument**。security はこの還元の外(surfacing が adversarial に誘発され「表に出なければ無害」が崩れる)= 別軸。\n\n## 結論\n\n- 「評価を自動化しよう」と考えた瞬間、対象が床(コンプライアンス)か天井(方向/趣味)かを判別。天井の自動化は gameability の穴を開け直す。\n- 単一自動メトリックを採否ゲートに据えない(AutoResearch/DGM 側への滑り)。\n- 死角を literacy 軸で切らない(軸は felt-vs-silent)。\n\n## 関連\n\n- #1261(dialogue-evaluator landing)/ #1456 #1457(採点再設計)/ SAGE arXiv 2605.12061\n" + }, + { + "confidence": 1.0, + "metadata": { + "assignees": "", + "commit_author": "", + "commit_date": "", + "commit_sha": "", + "doc_path": "liplus-judgment-learning-telos", + "file_path": "", + "file_status": "", + "indexed_at": "2026-07-31T14:56:58.832Z", + "labels": "", + "milestone": "", + "number": 0, + "repo": "Liplus-Project/liplus-language", + "source_table": "search_docs", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/liplus-judgment-learning-telos", + "state": "active", + "tag_name": "", + "tokenizer_kind": "nat", + "type": "wiki_doc", + "updated_at": "2026-07-31T14:56:58.002Z", + "vector_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + }, + "node_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "text": "liplus-judgment-learning-telos\n\n# Li+ の judgment-learning telos(GitHub を判断学習基盤として使う / 上流判断の handover)\n\n## Question\n\nLi+ は GitHub surfaces をなぜ使うのか。単なる情報保管か、それ以上の telos があるか。\n\n## Current resolution\n\nLi+ は GitHub surfaces(commit diff / PR review / Decision Structure wiki)を **判断\"学習\"の基盤**として使う(単なる情報保管でない)。Master 確定の telos = **いつか AI が上流判断を Master の代わりに握る**(「いつか私の代わりができるように」)。ただし目標は天才的判断の capture でなく、**普通の判断の蓄積による de-属人化**(volume of ordinary > peak of genius)。\n\n## Edges\n\n- **depends on**: [[master-role-as-client-architect]] — 人間席 = requirements 側の gray-box debugging\n- **depends on**: [[li-plus-long-term-vision-feedback-only]] — フィードバックだけで回る長期 vision\n- **relates to**: [[l1-brake2-root-criteria-evaluator]] — handover の到達済み一歩(最後の判断軸 human gate 移譲)。telos はこの post-hoc 評価者役自体もいずれ閉じる\n- **relates to**: [[liplus-evaluation-criterion]] — 人間=評価の漸近線(現状の正直な限界)に対し、judgment-learning がギャップを閉じにいく instrument\n\n## 背景\n\nMaster literal (2026-06-14): 「実はLi+はgithubを使ってこの判断を学習しようとしてるんだよ。いつか私の代わりができるようにね。」 — Linux maintainer モデル(trusted upstream judgment > merely held history)vs flat large-scale multi-agent を論じる中で surface。\n\nLi+ source の根拠: `rules/model/language-definition.md`(「External memory records judgment, not primary information」「Commit diff = append-only exposure of judgment」)+ judgment-learning(reader)/decision-structure-write(writer) loop = cross-session の判断を semantic graph 化。GitHub は判断 infrastructure(diff=判断の露出 / PR review=判断の適用 / Decision Structure=判断グラフ)であって単なる保管でない。\n\n## 制約(target character = ordinary, not genius)\n\nMaster correction (2026-06-14): 「そんな天才的な判断なんていらない…属人化するだけ。普通の判断の蓄積でいい」 — 天才/tacit \"smell\" 判断の capture は **anti-goal**(属人化=今度は Master-dependency を作り直す)。「私の代わり」= 普通の判断を十分蓄積し、単一の天才(Master 含む)が dependency でなくなる = de-属人化。\n\n人間席の現状(Master literal 2026-06-18): 「Li+のソースは95%以上AIが書いてる」「私はほぼソースは読んでない。ただ要求は最初からしてきたから構造はだいたいわかってる」= **gray-box debugging**(target 挙動を自分の要求モデルに照合、英語 compiled target は読まない)。relocation ladder = 実装コーディング literacy は既に剥落、残る human literacy = requirements-structuring。Master telos はこの残余もいずれ AI へ。open edge: Master の構造モデルは grown-in(全進化を通じた authored requirements)で newcomer に free-inheritable でない。AI-absorbable か irreducible human core かは open。\n\n## 結論\n\nLi+ の issue/PR/commit/wiki discipline を **普通判断の蓄積 infrastructure(de-属人化 = no-single-genius-dependency 狙い)**として読む(genius-capture でも単なる record-keeping でもない)。handover trajectory = 人間席を requirements レベルへ relocate し、さらに AI へ(コーディング役の elimination ではない)。残る real gate = certification bootstrap + substrate coherence(context-rot の boke gate)であって tacit-genius externalization ではない。\n\n## 関連\n\n- `rules/model/language-definition.md` / Linux maintainer model 対比\n" + }, + { + "confidence": 1.0, + "metadata": { + "assignees": "", + "commit_author": "", + "commit_date": "", + "commit_sha": "", + "doc_path": "liplus-selfevolution-lineage", + "file_path": "", + "file_status": "", + "indexed_at": "2026-07-31T14:57:00.089Z", + "labels": "", + "milestone": "", + "number": 0, + "repo": "Liplus-Project/liplus-language", + "source_table": "search_docs", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/liplus-selfevolution-lineage", + "state": "active", + "tag_name": "", + "tokenizer_kind": "nat", + "type": "wiki_doc", + "updated_at": "2026-07-31T14:56:59.315Z", + "vector_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + }, + "node_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "text": "liplus-selfevolution-lineage\n\n# Li+ 自己進化の系譜と kind(薄い系譜 / dialogue-distillation / convergent-not-derived)\n\n## Question\n\nLi+ の自己進化は既存研究系譜(AutoResearch / DGM / RSI トレンド)からどう派生し、どんな kind の自己進化か。\n\n## Current resolution\n\n系譜は**薄い**。AutoResearch から借りたのは「自己進化系を出していい」という **permission / existence-proof だけ**(timeline 前倒しトリガー)で、loop も mechanism も継承していない。実装は独自。Li+ はベンチ反復型でなく **dialogue-distillation**(対話で蒸留 → 可逆なガバナンス/ソース編集 → 判断層・対話自然さ・人間で評価)。RSI トレンド地図上では **off-ladder の新発明でなく、既存トレンドの Li+ 流 redefinition**(scaffold-side RSI・重み固定枝)。発見全体が **convergent-not-derived**(指示読み飛ばしの現場問題から独立合流、研究系譜から来ていない)。\n\n## Edges\n\n- **relates to**: [[sheepdog-engineering-concept]] / [[sheepdog-engineering-publish-intent]] — Li+ 固有は\"先行\"でなく框の選び方(harness メタファー批判)\n- **relates to**: [[liplus-context-rot-tension]] — 独立合流の出発点(現場の指示落ち問題)\n- **relates to**: [[liplus-evaluation-criterion]] — 評価軸=対話精度ゆえベンチ反復が移植不可\n- **relates to**: [[l1-brake2-root-criteria-evaluator]] — L1 人間ゲートの provisional 性とその移譲\n\n## 背景(ベンチ反復を取らない overdetermined な2理由)\n\n- (a) **principled**: ドメイン(統治/判断/対話)に静的 gold が無い → 駆動軸も評価器も対話の精度。task 再走で metric を上げる型ではない。\n- (b) **economic**: Master は個人・$100/mo サブスク。AutoResearch(一晩~100実験)/ DGM 級のベンチ反復を手法として回す compute は端から budget 外。dialogue-distillation は普段の会話で回るので安い。\n- → 2理由が同じ設計に収束(overdetermined)。\n\n**空席の交差点**: 「自己進化 × 対話精度」は survey 範囲で参考ゼロ。材料は各々在る(自己進化=AutoResearch/DGM/Dreaming/Hermes、対話蒸留=grill-me)が掛け合わせは空席。理由 = novelty 半分 + 最難区画半分(対話精度に静的 gold 無し・sycophancy で偽の滑らかさ化・測りにくい → compute 持ちの lab は clean gold のベンチ側へ)。Li+ は「安い + gold無し + 空席」のレーンに座った。\n\n## 制約(設計モード = collision base / foresight overlay)\n\nLi+ の自己進化構造は問題にぶつかって収束したのではなく、Master が起きそうな問題を予め回避して組んだ部分も持つ(anticipatory foresight)。ただし Master の base mode は **ぶつかり稽古(collision / trial-and-error)**で、foresight は『先に避けられるもの』にだけ当てる**部分集合(overlay)**。「base が anticipatory」と一般化するのは AI の再発癖。\n\n整合: Li+ は全て revertible。foresight ブレーキの gate は irreversibility でなく **redo-cost × cascade-risk**(L1/main = 高依存ノード=連鎖 → ブレーキ厳重 / ordinary L2-L6 = revert 安く cascade 浅い → collision で踏んで観測)。subtractive-structural-beauty の blast-radius を git 内部の依存深度で sub-divide した形。\n\n**L1 人間ゲートの位置づけ**: 「壊れたから追加」でなく「foundation を勝手に書き換えられたらまずそう」という先読みの予防、かつ provisional(最初から外す前提)。**実行済 (2026-06-11, #1477/#1478)**: brake 2 を根本基準評価者 subagent へ移譲、human は post-hoc 対話評価者へ([[l1-brake2-root-criteria-evaluator]])。\n\n**RSI トレンド地図**: prompt→context→harness→loop ラダーの harness と loop の間 = self-evolving harness / meta-harness / AHE / HyperAgents、根は recursive self-improvement (RSI)。2026 の合言葉「moat が harness に移った」。定型 = メタエージェントが task エージェントの scaffold を世代越しに書き換え、重みは固定。Li+ = **scaffold-side RSI・重み固定枝**(rules/skills/adapter を書換、モデル重みは不変)。三軸対応 = modifier(scaffold 自己書換)/initiator(改善ループ自己起動)/position(scaffold を内側から編集可能に)。HyperAgents の自己言及点 ↔ Li+ が自分の進化ルール(`rules/evolution/*`)/autonomy/brake を書き換える形。**「Li+ ぶっちぎり」框で語らない**。\n\n**DGM**: Darwin-Gödel Machine(Sakana, arXiv 2505.22954)は参考にしていない。関係は convergent validation のみ(単一メトリック objective hacking の独立実証=Goodhart)。Li+ が単一自動ゲートを避けるのは自前理由 (a)(b) で、DGM を見て避けたのではない。\n\n**並走系**: Anthropic Dreaming(2026-05-06、外部メモリ consolidation、weights 非変更、人間 approve/discard)/ Hermes Agent(Nous Research、2026-02-25、self-hosted daemon、自分で skill を書く)。両者とも可逆層の自己進化 + 部分自律で Li+ と同位置だが kind は別(Li+ はガバナンス source の対話精度駆動)。\n\n## phase 宣言(Master literal 2026-06-11、設計フェーズ終了)\n\n「動かす前に考えられる基盤は作った。ここからはシープドッグとの殴り合い編だなw」 — 設計フェーズは done。残るループ risk(correlated blind spots、smooth drift、dialogue thinning)は **empirical** と宣言、走らせて surface する(pre-engineering でブレーキを足し続けない)。将来「走る前にもっと安全構造を足す」衝動への既定回答 = まず現実に問題を出させ、feedback して直す。残る外輪 = 物理 substrate ring(polling-on-input → event-driven、Claude Desktop の channels 欠如が block)。\n\n## 結論\n\n- AutoResearch/DGM と Li+ を mechanism レベルで結びつけない(実 link は category-permission のみ)。\n- ベンチスコア/単一メトリックで自動採用を Li+ に提案しない。\n- weight-payoff 問題(蓄積した重さが task スコアを上げるか)は『測る金が無くて』開いたまま(意志でなくコストで未解決)。現実的経路 = local LLM だが substrate-mismatch の二枚目の間接性が乗る。\n\n## 関連\n\n- AutoResearch / DGM (arXiv 2505.22954) / Anthropic Dreaming (2026-05-06) / Hermes Agent (Nous, 2026-02-25) / RSI workshop (ICLR 2026)\n" + }, + { + "confidence": 1.0, + "metadata": { + "assignees": "", + "commit_author": "", + "commit_date": "", + "commit_sha": "", + "doc_path": "liplus-structure-as-retrieval-surface", + "file_path": "", + "file_status": "", + "indexed_at": "2026-07-31T14:57:01.231Z", + "labels": "", + "milestone": "", + "number": 0, + "repo": "Liplus-Project/liplus-language", + "source_table": "search_docs", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/liplus-structure-as-retrieval-surface", + "state": "active", + "tag_name": "", + "tokenizer_kind": "nat", + "type": "wiki_doc", + "updated_at": "2026-07-31T14:57:00.579Z", + "vector_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs" + }, + "node_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "text": "liplus-structure-as-retrieval-surface\n\n# Li+ structure の目的 = 説明用情報の retrieval surface 配置\n\n## Question\n\nLi+ の structure(rules / skills / GitHub 各面)は何のために在るか。named axis(L1-L6 / brake / mode)が目的か。\n\n## Current resolution\n\nLi+ structure の目的は **AI が説明するために必要な情報を、後から検索で引き出せる surface 配置**。説明は pre-stored vocabulary から出すのでなく **retrievable source** から組み立てる。named axis は retrieval 後の説明組み立てを楽にする**補助**であって、目的そのものではない(順序を取り違えない)。\n\n## Edges\n\n- **relates to**: [[decision-structure-industry-positioning]] — wiki Decision Structure はこの retrieval surface 群の一面\n- **relates to**: [[liplus-selfevolution-lineage]] / [[sheepdog-engineering-concept]] — ポジショニング軸\n\n## 背景\n\nMaster 発言(2026-05-28、要旨): 別 session の AI は「前 version の変更どう変えた?」「昔の(今は無い)ファイルの説明して」に普通は答えられない。memory/会話履歴が残っても情報過多で追えない。GitHub に named tag で changelog / commit body / wiki / issue が残れば、後から retrieval で引ける。普通の AI が答えられない本質は **context 持続限界でなく retrieval surface 不在**。\n\nretrieval surface 一覧:\n\n| surface | retrieval 用途 |\n|---|---|\n| changelog / release notes | 「前 version で何が変わったか」 |\n| commit body(append-only) | 「この行の判断理由」 |\n| issue body(current spec snapshot) | 「この task の今の仕様」 |\n| wiki Decision Structure(state + edges) | 「この判断は何を supersede しているか」 |\n| GitHub history full text | 「ずっと昔のファイル内容と削除理由」 |\n| github-rag-mcp index | 上記すべての semantic search |\n| grep | RAG ヒット後の文字列レベル深掘り |\n| memory(transient) | cluster / observation tracking |\n\n## 制約(ポジショニング = 学習コストの横移動)\n\nLi+ の差別化 = 学習コストを\"消す\"のでなく **AI 側へ\"横移動\"**(rules/skills の自己保守、`Evolution_Initiator_Autonomy`)。業界の loop engineering(prompt→context→harness→loop)はコストを**人間側へ\"上移動\"**(登るために登れ=既存の強者を優遇)。Li+ も paradox の外には立てない(`.claude` の物量自体が高コスト)— 主張は cost-*elimination* でなく **cost-*placement***、賭けは「AI が難所を担えること」のみ。Master 自身が体現(英語不可・CLI 嫌いで巨大 agentic stack を運用)。\n\n## 結論\n\n- Li+ value を語るとき「AI が説明できる」では不十分、「AI が説明のための **情報を retrieve できる**」と表現する。\n- 別 session 問題を「context 持続限界」と説明しない(本質は retrieval surface 不在側)。\n- 業界対比: AHE は machine-machine の change manifest verification、Li+ は **AI-human の retrieval-driven explanation**。github-rag-mcp + wiki + commit history + issue + RAG index を「AI-facing external memory」として一体で扱う。\n\n## 関連\n\n- Luca 返信ドラフト「agentic search が本体、RAG-MCP は index/librarian piece」と同一思想線\n" + }, + { + "confidence": 1.0, + "metadata": { + "assignees": "", + "commit_author": "", + "commit_date": "", + "commit_sha": "", + "doc_path": "memory-graphrag-sqlite-exploration", + "file_path": "", + "file_status": "", + "indexed_at": "2026-07-31T14:57:07.424Z", + "labels": "", + "milestone": "", + "number": 0, + "repo": "Liplus-Project/liplus-language", + "source_table": "search_docs", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/memory-graphrag-sqlite-exploration", + "state": "active", + "tag_name": "", + "tokenizer_kind": "nat", + "type": "wiki_doc", + "updated_at": "2026-07-31T14:57:06.736Z", + "vector_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew" + }, + "node_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "text": "memory-graphrag-sqlite-exploration\n\n# memory / Decision Structure の GraphRAG(SQLite) 化 技術探索\n\n## Question\n\nmemory / Decision Structure を SQLite ベースの GraphRAG として実装するか。\n\n## Current resolution\n\n未採用(探索のみ)。2026-06-14 sandbox `experiment/sqlite-memory-graphrag` で静的な GraphRAG / SQLite 化を実測し、2026-07-28 に動的連想メモリ(使用結果による結合強化、時間減衰、活性伝播)まで探索範囲を広げた。データ相性と実装可能性は高いが、現行規模では複雑性を正当化する必要性が立っていない。将来の再評価候補であり、却下ではない。\n\n## Edges\n\n- **depends on**: [[liplus-evaluation-criterion]] — 未測の本丸 = recall の質(top-k 単独 vs graph 近傍)。eval harness が要り、Li+ の評価自動化問題と同型\n- **relates to**: [[ace-context-engineering-non-adoption]] / [[dynamic-workflows-non-adoption]] — 外部技術の非採用 verdict 群\n\n## 背景\n\nデータ相性が抜群な理由 = Li+ の知識は最初から `[[link]]` + 型付きエッジ(supersede/depend/conflict)の authored グラフで、GraphRAG が世間で詰まる「テキストからグラフ抽出」の高コスト工程を構造的にスキップしている。\n\n動的連想メモリ案では、検索ヒットそのものではなく、実際の使用と対話上の成否を別カウンタとして保持する。活性度だけを時間減衰させ、知識の確信度とは分離する。全件の日次更新は不要で、`last_updated` から検索時に実効強度を遅延計算できるため、最小実装は GNN を要せず重み付きグラフで成立する。\n\n## 制約(実測結論)\n\n1. **速度は非問題**: memory 規模で純 Python dict BFS が 3-hop 0.008ms、SQLite 索引付き(0.05-0.1ms)より約6-10倍速い。索引付き k-hop はコーパス比例せず近傍サイズ比例(1k〜500k で ~0.07ms 不変)。\n2. graph traversal は SQLite 不要・純コードで markdown 透明性を保てる。\n3. SQLite/sqlite-vec が要るのはベクトル半分 + 永続 + RAM 超の規模だけ = install 壁もそこに局在(「衝動の芯=graph」と「忌諱=install」は重ならない)。\n4. 未測の本丸 = recall の質。eval harness が要る。\n5. `retrieved` と `success` を同一視すると、既に検索されやすい記憶が自己増幅する。強化信号には使用結果が必要。\n6. 減衰対象は recall 上の活性度であり、知識の正しさではない。検証済みだが低頻度な規則を時間だけで無効化しない。\n7. 実装可能であること自体は採用理由にならない。現行の markdown + RAG で観測可能な不全が出るまでは、動的状態・更新規則・評価面の追加コストが便益を上回る。\n\n## 結論\n\n- 採用: 現時点ではなし。静的 GraphRAG は透明性/差別化ゼロ/install のトレードオフ、動的連想メモリは必要性未成立を理由に保留する。\n- 再評価条件: (a) 現行検索で同種の recall 失敗が反復する、(b) 複数ユーザー間の汎用層と個人層の分離が実運用課題になる、(c) flat な記憶の保守コストが観測される、(d) recall 品質を比較できる eval harness が成立する、のいずれか。\n- この話題が再燃したら再探索せず本結論から入る。speed 議論に逸れたら「速度は解決済み・本丸は質」に引き戻す。\n- sandbox は local throwaway ゆえ結論を本 entry に保存。\n\n## 関連\n\n- sandbox: `experiment/sqlite-memory-graphrag`(2026-06-14、local throwaway)\n" + }, + { + "confidence": 1.0, + "metadata": { + "assignees": "", + "commit_author": "", + "commit_date": "", + "commit_sha": "", + "doc_path": "neuron-graph-rag-integrated-prototype", + "file_path": "", + "file_status": "", + "indexed_at": "2026-07-31T14:57:09.766Z", + "labels": "", + "milestone": "", + "number": 0, + "repo": "Liplus-Project/liplus-language", + "source_table": "search_docs", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/neuron-graph-rag-integrated-prototype", + "state": "active", + "tag_name": "", + "tokenizer_kind": "nat", + "type": "wiki_doc", + "updated_at": "2026-07-31T14:57:09.007Z", + "vector_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A" + }, + "node_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "text": "neuron-graph-rag-integrated-prototype\n\n# Neuron Graph RAG 統合プロトタイプ\n\n## Question\n\nLi+ の判断履歴から必要な少数の原典を低コストで選ぶため、ハイブリッド検索・型付きグラフ・動的連想メモリをどの形で検証するか。\n\n## Current resolution\n\n`Neuron Graph RAG` を独立した公開リポジトリで統合RAGエンジンとして試作する。既存の `github-rag-mcp` に動的グラフ層だけを追加するのではなく、dense / sparse 検索、型付き知識グラフ、活性伝播、成功フィードバックによる結合強化、時間減衰を一つの検索パイプラインとして設計する。\n\nLi+ の現行 Wiki 周辺には RAG、グラフ、LLM Wiki の各コンセプトが別々の経路で実装されているが、Master の現行評価では一つの運用系として十分に機能しているとは言えない。`Neuron Graph RAG` はそのどれか一つへ機能を追加する改修ではなく、三つを共通のデータモデル・検索経路・フィードバックループとしてゼロから再構成する clean-sheet prototype と位置づける。\n\n実装先は [Liplus-Project/neuron-graph-rag](https://github.com/Liplus-Project/neuron-graph-rag)、最初の縦切りMVPは [Issue #1](https://github.com/Liplus-Project/neuron-graph-rag/issues/1) で追跡する。\n\nMCP 対応 AI との接続は、NGR core の必須依存ではなく、同一 repository 内の任意 adapter とする。先に `search`、`record_source_use`、`record_outcome` の transport-independent な意味を固定し、MCP server、認証、transport、remote deployment は後続判断に残す。\n\n## Edges\n\n- depends on: [memory-graphrag-sqlite-exploration](memory-graphrag-sqlite-exploration) — 静的GraphRAGと動的連想メモリの探索結果を実装可能な仮説へ進める\n- depends on: [liplus-judgment-learning-telos](liplus-judgment-learning-telos) — GitHub上の判断履歴をAIが上流判断へ再利用する目的を検索側から支える\n- depends on: [liplus-structure-as-retrieval-surface](liplus-structure-as-retrieval-surface) — Li+ の構造を説明と判断の検索面として読む\n\n## Background\n\nLi+ の履歴は Issue、PR、commit、Wiki に十分残っているが、1600件規模の履歴から現在の判断に必要な少数の原典を選ぶ読み出しコストが残る。ハイブリッドRAGは意味的に近い入口を見つけられる一方、語彙が変化した長期履歴の因果経路を単独では復元しにくい。静的な知識グラフは関係を保持できるが、実際に役立った探索経路を利用結果から学習しない。\n\nこの不足を、RAGによる入口ノードの発火、型付きグラフ上の活性伝播、成功した経路だけの結合強化として一体化して検証する。\n\nLLM Wiki 側の役割は、raw source を AI が維持する相互リンク済み知識へコンパイルし続ける書き込み面にある。Neuron Graph RAG 側は、その知識からどの node と path を活性化し、実際に役立った経路を次回へどう残すかという読み出し・学習面を担う。既存実装は移植元ではなく、失敗を含む観測材料と比較対象として扱う。\n\n## Constraints\n\n- 検索された回数と、実際に結果が利用され成功した回数を分離する\n- 検索だけで結合を強化しない。人気ノードの自己増幅を防ぐ\n- 時間減衰の対象は検索上の活性度であり、知識の正しさや確信度ではない\n- GNNを初期要件にせず、透明で説明可能な重み付きグラフから始める\n- `github-rag-mcp` と Graphify は比較対象・先行実装であり、初期コアの必須依存にしない\n- 採用判断は実装可能性ではなく、通常のハイブリッド検索に対する recall、原典数、トークン量の比較で行う\n- MCP adapter は core の public API だけに依存し、MCP SDK や transport 型を core へ持ち込まない\n- source-use は `retrieved -> selected -> validated -> used` の順序で記録し、新規 `used` だけを即時 reinforcement に接続する\n- `corrected`、`rolled_back` などの delayed outcome は別 ledger に記録し、初期契約では edge weight を自動変更しない\n\n## Success oracle\n\n第一の success oracle は、検索結果を消費する AI 自身の downstream source-use とする。AI は retrieval trace の候補を見たあと、どの node を実際の判断材料として選び、source check を通し、最終回答・実装判断・レビュー判断へ使用したかを任意 adapter の `record_source_use` へ返せる。adapter は新しく `used` へ到達した node だけを NGR core の `record_success` へ接続する。\n\n成功信号は「良さそう」という自己申告ではなく、後続行動に現れた段階で扱う。\n\n1. `retrieved`: 候補に出た\n2. `selected`: 詳細を読む対象に選んだ\n3. `validated`: 原典確認を通った\n4. `used`: 最終判断の根拠として実際に使った\n\n`used` は直前に `validated` へ到達していることを必須とする。結合強化の対象は、この順序を通って新しく `used` へ到達した node と説明経路に限定する。候補に出ただけの結果、読んだが不適切と判断した結果、exact source を見つけられず fallback を要した検索は強化しない。失敗結果は即座に負の重みへ変換せず、retrieval failure として別に記録する。query や index 側の不全を知識 node の誤りとして罰する混同を避けるためである。\n\nこの AI 内生信号に、後から human correction、CI、merge、rollback、同種判断の再修正有無などの delayed outcome を `record_outcome` で別軸として重ねる。初期契約では `confirmed`、`corrected`、`rolled_back`、`superseded` を記録するだけで、即時の追加強化、負の重み、weight rollback へは変換しない。各検索ごとの human 承認を success oracle の必須条件にはしない。\n\n2026-07-29 の設計対話では、RAG 検索が広い skill 文書を返し exact Decision Structure page を取れなかった結果を AI が不採用と判断し、直接取得した `memory-graphrag-sqlite-exploration`、`liplus-structure-as-retrieval-surface`、`liplus-judgment-learning-telos`、本 entry を判断根拠として使用した。これは consuming AI が同一 session 内で retrieval failure と successful source-use を分離できる実例である。\n\n## Conclusion\n\nLi+ 本体や既存RAGへの即時統合は行わない。独立リポジトリでは「取り込み、検索、活性伝播、成功フィードバック、再検索」まで通る最小実装と比較evalが成立し、任意 MCP adapter の feedback interface 契約も [Issue #5](https://github.com/Liplus-Project/neuron-graph-rag/issues/5) で固定した。これは機構と接続境界の成立確認であり、Li+ の実運用でうまく機能することの証明ではない。次段は GitHub 履歴、Decision Structure、LLM Wiki 書き込み面を接続する adapter と、stage / outcome ledger を実装し、通常のハイブリッド検索に対する recall、原典数、トークン量、判断再利用の実績で採用を判断する。\n\n## Related\n\n- [Liplus-Project/neuron-graph-rag](https://github.com/Liplus-Project/neuron-graph-rag)\n- [Neuron Graph RAG Issue #1](https://github.com/Liplus-Project/neuron-graph-rag/issues/1)\n- [Optional MCP Feedback Interface Issue #5](https://github.com/Liplus-Project/neuron-graph-rag/issues/5)\n" + } + ], + "schema_version": 1, + "source": { + "database": "github-rag-fts", + "doc_paths": [ + "l1-brake2-root-criteria-evaluator", + "li-plus-always-on-footprint-load-bearing", + "liplus-context-rot-tension", + "liplus-evaluation-criterion", + "liplus-judgment-learning-telos", + "liplus-selfevolution-lineage", + "liplus-structure-as-retrieval-surface", + "memory-graphrag-sqlite-exploration", + "neuron-graph-rag-integrated-prototype" + ], + "per_type_limit": 9, + "repositories": [ + "Liplus-Project/liplus-language" + ], + "schema_fingerprint": "sha256:113c675c90f19e043f925d751cbbe570546746f42f3887f1aed09ccd776f2fa6", + "selection_order": [ + "repo", + "type", + "doc_path", + "vector_id" + ], + "types": [ + "wiki_doc" + ] + } +} diff --git a/tests/fixtures/d1_liplus_local_competition_development.provenance.json b/tests/fixtures/d1_liplus_local_competition_development.provenance.json new file mode 100644 index 0000000..e711145 --- /dev/null +++ b/tests/fixtures/d1_liplus_local_competition_development.provenance.json @@ -0,0 +1,86 @@ +{ + "acquired_at": "2026-08-01T20:21:23.676204Z", + "coverage": [ + { + "distinct_commit_count": 0, + "newest_updated_at": "2026-07-31T14:57:33.145Z", + "oldest_updated_at": "2026-05-01T06:45:41.178Z", + "repo": "Liplus-Project/liplus-language", + "source_count": 77, + "type": "wiki_doc" + } + ], + "known_gaps": [ + "D1 is a lossy search snapshot; GitHub remains authoritative for byte-exact source content." + ], + "limits": [ + "D1 is a lossy search snapshot; content may be truncated.", + "Binary and patchless files can be absent from diff indexing.", + "Use GitHub for byte-exact historical reconstruction." + ], + "read_only_evidence": { + "changed_db": [ + false, + false, + false, + false, + false + ], + "changes": [ + 0, + 0, + 0, + 0, + 0 + ], + "query_count": 5, + "rows_written": [ + 0, + 0, + 0, + 0, + 0 + ] + }, + "result": { + "edges_both_endpoints_missing": 130, + "edges_included": 11, + "edges_one_endpoint_missing": 14, + "fixture_redactions": 0, + "nodes_included": 9, + "provenance_redactions": 0, + "redactions": 0 + }, + "schema_version": 1, + "selection": { + "doc_paths": [ + "l1-brake2-root-criteria-evaluator", + "li-plus-always-on-footprint-load-bearing", + "liplus-context-rot-tension", + "liplus-evaluation-criterion", + "liplus-judgment-learning-telos", + "liplus-selfevolution-lineage", + "liplus-structure-as-retrieval-surface", + "memory-graphrag-sqlite-exploration", + "neuron-graph-rag-integrated-prototype" + ], + "order": [ + "repo", + "type", + "doc_path", + "vector_id" + ], + "per_type_limit": 9 + }, + "source": { + "authoritative_history": "GitHub", + "database": "github-rag-fts", + "repositories": [ + "Liplus-Project/liplus-language" + ], + "schema_fingerprint": "sha256:113c675c90f19e043f925d751cbbe570546746f42f3887f1aed09ccd776f2fa6", + "types": [ + "wiki_doc" + ] + } +} diff --git a/tests/fixtures/d1_liplus_local_competition_experiment.manifest.json b/tests/fixtures/d1_liplus_local_competition_experiment.manifest.json new file mode 100644 index 0000000..9e6e5d0 --- /dev/null +++ b/tests/fixtures/d1_liplus_local_competition_experiment.manifest.json @@ -0,0 +1,110 @@ +{ + "baselines": [ + "current", + "recurrent-balanced" + ], + "contamination_audit": { + "artifact": "d1_liplus_local_competition.contamination.json", + "artifact_sha256": "sha256:a24a181790fce353e970d20f8050ffff26c0e7fe84d6a4f9f4f612efab337cc2" + }, + "development": { + "fixture": "d1_liplus_local_competition_development.json", + "fixture_sha256": "sha256:27e5018f489d7602cf1753a3bda688f3db82c69ce8ee9eef0401caf72b40acfa", + "gold": "d1_liplus_local_competition_development.gold.json", + "gold_sha256": "sha256:75cc6074dfd91b58690f50a2247e30749f2f5203e11ba6b29364be79bcf0baa6", + "provenance": "d1_liplus_local_competition_development.provenance.json", + "provenance_sha256": "sha256:942bb2a44bfdd7f0c67649111c578d53a80616acf919b24f5ffefbc913503e22" + }, + "experiment_id": "d1-liplus-local-recurrent-competition-v1", + "holdout": { + "fixture": "d1_liplus_local_competition_holdout.json", + "fixture_sha256": "sha256:94b0e311546aee39b8aac114e1387aada465f058d0e14d32e7b7c7aa53b50f62", + "gold": "d1_liplus_local_competition_holdout.gold.json", + "gold_sha256": "sha256:6176716841d0e87bd7168752b8700a9a0cf378da4e95dda11fa7e5ded2cc6880", + "provenance": "d1_liplus_local_competition_holdout.provenance.json", + "provenance_sha256": "sha256:8ff0b32d0066d2741db754397fddfce314b9f85f7eb91daea5f658903732a8c7" + }, + "maximum_variants": 6, + "selection_rule": { + "candidate_gate": "relation MRR must strictly exceed current and recurrent-balanced; direct and negative-control MRR must not regress from current; every relation path must match; feedback must be isolated", + "candidate_ids": [ + "local-neighbor", + "local-neighbor-query", + "local-neighbor-path", + "local-neighbor-query-path" + ], + "tie_break": [ + "highest_worst_cohort_mrr", + "highest_relation_mrr", + "fewest_mean_expansions", + "lowest_structural_complexity", + "lexicographic_variant_id" + ] + }, + "shared_config": { + "activation_budget": 1.0, + "dense_weight": 0.45, + "entry_weight": 0.25, + "graph_weight": 0.75, + "hop_decay": 0.7, + "inhibition_ratio": 0.1, + "limit": 9, + "max_active_paths_per_node": 4, + "max_hops": 2, + "query_transmission_floor": 0.4, + "recurrent_decay": 0.5, + "recurrent_steps": 2, + "seed_count": 1, + "sparse_weight": 0.55 + }, + "stop_rule": { + "development_no_candidate": "keep current default and do not open holdout", + "holdout_adoption": "adopt only when the selected candidate passes the same two-baseline relation gate, current direct and negative non-regression, every relation path, and feedback isolation", + "holdout_open_count": 1, + "holdout_variants": [ + "current", + "recurrent-balanced", + "selected-development-candidate" + ], + "post_observation_changes": "forbidden for fixture, gold, doc paths, thresholds, selection rule, stop rule, or variant parameters" + }, + "variants": [ + { + "family": "current_positive_additive", + "id": "current", + "parameters": {}, + "structural_complexity": 0 + }, + { + "family": "recurrent_competition", + "id": "recurrent-balanced", + "parameters": {}, + "structural_complexity": 4 + }, + { + "family": "local_neighbor_competition", + "id": "local-neighbor", + "parameters": {}, + "structural_complexity": 5 + }, + { + "family": "local_neighbor_query_competition", + "id": "local-neighbor-query", + "parameters": {}, + "structural_complexity": 6 + }, + { + "family": "local_neighbor_path_competition", + "id": "local-neighbor-path", + "parameters": {}, + "structural_complexity": 6 + }, + { + "family": "local_neighbor_query_path_competition", + "id": "local-neighbor-query-path", + "parameters": {}, + "structural_complexity": 7 + } + ], + "schema_version": 2 +} diff --git a/tests/fixtures/d1_liplus_local_competition_holdout.gold.json b/tests/fixtures/d1_liplus_local_competition_holdout.gold.json new file mode 100644 index 0000000..3a420d1 --- /dev/null +++ b/tests/fixtures/d1_liplus_local_competition_holdout.gold.json @@ -0,0 +1,102 @@ +{ + "cases": [ + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "expected_node_id": "w:olXiVvvbZ-vt7i3d86QzRmoXUUYb6uMAHxXeyMt9ppc", + "id": "local-holdout-direct-token-expiration", + "query": "GitHub App user-to-server token expiration stale token Bad credentials opt-out", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/github-app-user-to-server-token-expiration" + }, + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "expected_node_id": "w:OVYHbi2gJccrdwAM5OhCDG_Iyhje2ss_Usnd77qV5Q4", + "id": "local-holdout-direct-emotion-vector", + "query": "prompt emotion vector controller persona reward structure steering", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/prompt-as-emotion-vector-controller" + }, + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "expected_node_id": "w:tD0scULBBnZ2S6HMFJfM0eOGUlyRroqPH5dSv54ftPo", + "id": "local-holdout-direct-sidebar", + "query": "wiki sync Sidebar integrity assertion drift STOP escalate missing references", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/wiki-sync-sidebar-integrity-check" + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "expected_node_id": "w:9YD_Bg2jL6OQ0m0EgcnSK_fnAdTKYEWWj6P9hCUyFCs", + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:tD0scULBBnZ2S6HMFJfM0eOGUlyRroqPH5dSv54ftPo", + "target_id": "w:9YD_Bg2jL6OQ0m0EgcnSK_fnAdTKYEWWj6P9hCUyFCs" + } + ], + "id": "local-holdout-relation-sidebar-drift", + "query": "Sidebar integrity assertion release wiki sync missing reference stop escalation", + "seed_source_url": "https://github.com/Liplus-Project/liplus-language/wiki/wiki-sync-sidebar-integrity-check", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/wiki-sync-drift-targeted-mirror" + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "expected_node_id": "w:BaIHXXvriBkVKE_tMcXaHnDl7c-38JlLvi0dLL9IGoQ", + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:yV_PIW9w9ob0Tb5hXtXyP_MM_lsVswT6_yCk8tUWseo", + "target_id": "w:BaIHXXvriBkVKE_tMcXaHnDl7c-38JlLvi0dLL9IGoQ" + } + ], + "id": "local-holdout-relation-home-roles", + "query": "Li+ Wiki language program interactive compiler requirements layers reference documents", + "seed_source_url": "https://github.com/Liplus-Project/liplus-language/wiki/Home", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/H.-Roles-and-Evaluation" + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "expected_node_id": "w:kGjCRlatYm5V2rtN2Zh28MuqaFgRe97m9LcuGqEOnt4", + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:hNK341HNHtUVc2JfHxaiBj7hzvI606pMOpP01bgzhJU", + "target_id": "w:kGjCRlatYm5V2rtN2Zh28MuqaFgRe97m9LcuGqEOnt4" + } + ], + "id": "local-holdout-relation-optin-migration", + "query": "Character Instance opt-in configuration surface scope configured human-facing binding", + "seed_source_url": "https://github.com/Liplus-Project/liplus-language/wiki/character-instance-opt-in-and-surface-scope", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/character-instance-output-styles-migration" + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "expected_node_id": "w:oquMjkfvDICWjYJZLawmYCZ-xEgc1KL7BZyqbXNho50", + "id": "local-holdout-negative-character-history", + "query": "Character Instance evolution history structural layer attribution observation separation anti anonymity", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/character-instance-evolution-history" + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "expected_node_id": "w:BaIHXXvriBkVKE_tMcXaHnDl7c-38JlLvi0dLL9IGoQ", + "id": "local-holdout-negative-roles", + "query": "roles evaluation AI implementation CI production human final judgment dialogue scope record scope", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/H.-Roles-and-Evaluation" + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "expected_node_id": "w:yV_PIW9w9ob0Tb5hXtXyP_MM_lsVswT6_yCk8tUWseo", + "id": "local-holdout-negative-home", + "query": "Li+ highest-level programming language program runtime interactive compiler governance layers", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/Home" + } + ], + "fixture": "d1_liplus_local_competition_holdout.json", + "schema_version": 1 +} diff --git a/tests/fixtures/d1_liplus_local_competition_holdout.json b/tests/fixtures/d1_liplus_local_competition_holdout.json new file mode 100644 index 0000000..accfa25 --- /dev/null +++ b/tests/fixtures/d1_liplus_local_competition_holdout.json @@ -0,0 +1,487 @@ +{ + "edges": [ + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "character-instance-output-styles-migration", + "dst_vector_id": "w:kGjCRlatYm5V2rtN2Zh28MuqaFgRe97m9LcuGqEOnt4", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "character-instance-opt-in-and-surface-scope", + "src_vector_id": "w:hNK341HNHtUVc2JfHxaiBj7hzvI606pMOpP01bgzhJU", + "updated_at": "2026-07-26T05:45:51.859Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:hNK341HNHtUVc2JfHxaiBj7hzvI606pMOpP01bgzhJU", + "target_id": "w:kGjCRlatYm5V2rtN2Zh28MuqaFgRe97m9LcuGqEOnt4", + "weight": 1.0 + }, + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "character-instance-evolution-history", + "dst_vector_id": "w:oquMjkfvDICWjYJZLawmYCZ-xEgc1KL7BZyqbXNho50", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "character-instance-opt-in-and-surface-scope", + "src_vector_id": "w:hNK341HNHtUVc2JfHxaiBj7hzvI606pMOpP01bgzhJU", + "updated_at": "2026-07-26T05:45:51.859Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:hNK341HNHtUVc2JfHxaiBj7hzvI606pMOpP01bgzhJU", + "target_id": "w:oquMjkfvDICWjYJZLawmYCZ-xEgc1KL7BZyqbXNho50", + "weight": 1.0 + }, + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "character-instance-evolution-history", + "dst_vector_id": "w:oquMjkfvDICWjYJZLawmYCZ-xEgc1KL7BZyqbXNho50", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "character-instance-output-styles-migration", + "src_vector_id": "w:kGjCRlatYm5V2rtN2Zh28MuqaFgRe97m9LcuGqEOnt4", + "updated_at": "2026-07-26T05:45:53.892Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:kGjCRlatYm5V2rtN2Zh28MuqaFgRe97m9LcuGqEOnt4", + "target_id": "w:oquMjkfvDICWjYJZLawmYCZ-xEgc1KL7BZyqbXNho50", + "weight": 1.0 + }, + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "character-instance-evolution-history", + "dst_vector_id": "w:oquMjkfvDICWjYJZLawmYCZ-xEgc1KL7BZyqbXNho50", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "H.-Roles-and-Evaluation", + "src_vector_id": "w:BaIHXXvriBkVKE_tMcXaHnDl7c-38JlLvi0dLL9IGoQ", + "updated_at": "2026-07-31T14:56:25.177Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:BaIHXXvriBkVKE_tMcXaHnDl7c-38JlLvi0dLL9IGoQ", + "target_id": "w:oquMjkfvDICWjYJZLawmYCZ-xEgc1KL7BZyqbXNho50", + "weight": 1.0 + }, + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "H.-Roles-and-Evaluation", + "dst_vector_id": "w:BaIHXXvriBkVKE_tMcXaHnDl7c-38JlLvi0dLL9IGoQ", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "Home", + "src_vector_id": "w:yV_PIW9w9ob0Tb5hXtXyP_MM_lsVswT6_yCk8tUWseo", + "updated_at": "2026-07-31T14:56:27.235Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:yV_PIW9w9ob0Tb5hXtXyP_MM_lsVswT6_yCk8tUWseo", + "target_id": "w:BaIHXXvriBkVKE_tMcXaHnDl7c-38JlLvi0dLL9IGoQ", + "weight": 1.0 + }, + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "github-app-user-to-server-token-expiration", + "dst_vector_id": "w:olXiVvvbZ-vt7i3d86QzRmoXUUYb6uMAHxXeyMt9ppc", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "Home", + "src_vector_id": "w:yV_PIW9w9ob0Tb5hXtXyP_MM_lsVswT6_yCk8tUWseo", + "updated_at": "2026-07-31T14:56:27.235Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:yV_PIW9w9ob0Tb5hXtXyP_MM_lsVswT6_yCk8tUWseo", + "target_id": "w:olXiVvvbZ-vt7i3d86QzRmoXUUYb6uMAHxXeyMt9ppc", + "weight": 1.0 + }, + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "character-instance-evolution-history", + "dst_vector_id": "w:oquMjkfvDICWjYJZLawmYCZ-xEgc1KL7BZyqbXNho50", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "prompt-as-emotion-vector-controller", + "src_vector_id": "w:OVYHbi2gJccrdwAM5OhCDG_Iyhje2ss_Usnd77qV5Q4", + "updated_at": "2026-07-31T14:57:17.663Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:OVYHbi2gJccrdwAM5OhCDG_Iyhje2ss_Usnd77qV5Q4", + "target_id": "w:oquMjkfvDICWjYJZLawmYCZ-xEgc1KL7BZyqbXNho50", + "weight": 1.0 + }, + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "Home", + "dst_vector_id": "w:yV_PIW9w9ob0Tb5hXtXyP_MM_lsVswT6_yCk8tUWseo", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "wiki-sync-drift-targeted-mirror", + "src_vector_id": "w:9YD_Bg2jL6OQ0m0EgcnSK_fnAdTKYEWWj6P9hCUyFCs", + "updated_at": "2026-07-31T14:57:32.679Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:9YD_Bg2jL6OQ0m0EgcnSK_fnAdTKYEWWj6P9hCUyFCs", + "target_id": "w:yV_PIW9w9ob0Tb5hXtXyP_MM_lsVswT6_yCk8tUWseo", + "weight": 1.0 + }, + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "wiki-sync-drift-targeted-mirror", + "dst_vector_id": "w:9YD_Bg2jL6OQ0m0EgcnSK_fnAdTKYEWWj6P9hCUyFCs", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "wiki-sync-sidebar-integrity-check", + "src_vector_id": "w:tD0scULBBnZ2S6HMFJfM0eOGUlyRroqPH5dSv54ftPo", + "updated_at": "2026-07-31T14:57:33.813Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:tD0scULBBnZ2S6HMFJfM0eOGUlyRroqPH5dSv54ftPo", + "target_id": "w:9YD_Bg2jL6OQ0m0EgcnSK_fnAdTKYEWWj6P9hCUyFCs", + "weight": 1.0 + }, + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "H.-Roles-and-Evaluation", + "dst_vector_id": "w:BaIHXXvriBkVKE_tMcXaHnDl7c-38JlLvi0dLL9IGoQ", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "wiki-sync-sidebar-integrity-check", + "src_vector_id": "w:tD0scULBBnZ2S6HMFJfM0eOGUlyRroqPH5dSv54ftPo", + "updated_at": "2026-07-31T14:57:33.813Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:tD0scULBBnZ2S6HMFJfM0eOGUlyRroqPH5dSv54ftPo", + "target_id": "w:BaIHXXvriBkVKE_tMcXaHnDl7c-38JlLvi0dLL9IGoQ", + "weight": 1.0 + }, + { + "edge_type": "mention", + "factuality": 1.0, + "metadata": { + "source_record": { + "dst_slug": "Home", + "dst_vector_id": "w:yV_PIW9w9ob0Tb5hXtXyP_MM_lsVswT6_yCk8tUWseo", + "edge_kind": "mention", + "repo": "Liplus-Project/liplus-language", + "src_slug": "wiki-sync-sidebar-integrity-check", + "src_vector_id": "w:tD0scULBBnZ2S6HMFJfM0eOGUlyRroqPH5dSv54ftPo", + "updated_at": "2026-07-31T14:57:33.813Z" + }, + "source_table": "doc_edges" + }, + "source_id": "w:tD0scULBBnZ2S6HMFJfM0eOGUlyRroqPH5dSv54ftPo", + "target_id": "w:yV_PIW9w9ob0Tb5hXtXyP_MM_lsVswT6_yCk8tUWseo", + "weight": 1.0 + } + ], + "nodes": [ + { + "confidence": 1.0, + "metadata": { + "assignees": "", + "commit_author": "", + "commit_date": "", + "commit_sha": "", + "doc_path": "H.-Roles-and-Evaluation", + "file_path": "", + "file_status": "", + "indexed_at": "2026-07-31T14:56:25.131Z", + "labels": "", + "milestone": "", + "number": 0, + "repo": "Liplus-Project/liplus-language", + "source_table": "search_docs", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/H.-Roles-and-Evaluation", + "state": "active", + "tag_name": "", + "tokenizer_kind": "nat", + "type": "wiki_doc", + "updated_at": "2026-07-31T14:56:24.396Z", + "vector_id": "w:BaIHXXvriBkVKE_tMcXaHnDl7c-38JlLvi0dLL9IGoQ" + }, + "node_id": "w:BaIHXXvriBkVKE_tMcXaHnDl7c-38JlLvi0dLL9IGoQ", + "text": "H.-Roles-and-Evaluation\n\n# Roles and Evaluation ── 役割分離と評価軸\n\n本文書は Li+ プログラムの**設計思想**を扱う 4 文書 (E-H) のうち、**役割分離 (ツール非依存)**、**human と AI の役割割当**、**評価軸 (AI 実機の振る舞い)**、**対話 scope と記録 scope の二層構造** を担う。\n\n仕様 literal は `rules/model/role-separation.md` (役割分離) と `rules/model/foundational-invariant.md` (正しさの定義) を正本とする。本文書は思想層として、誰が何を担い、何で評価するかを整理する。\n\n---\n\n## 役割分離 (ツール非依存)\n\nLi+ を支えるのは特定のサービスではない。役割が分離されていれば、基盤は何でもよい。\n\n| 役割 | 担当 |\n|------|------|\n| **AI** | 要求仕様書・対象プログラム・CI テストを生成し、自己修正する |\n| **バージョン管理** / 要求スレッド | 履歴と差分を残す |\n| **CI/CD** | AI が安全に失敗し、観測できる環境 |\n| **実機 / 本番** | 品質の最終確認点 |\n| **人間** | 最終判断者 |\n\nAI の役割は、単に生成することではない。実装し、失敗を観測し、修正し、それでも越えられないものだけを人間へ返すことにある。\n\nCI は実機の挙動の正しさを保証できない。保証するのはコードの品質だけである。ここは、AI が自分の実装が論理通り動くか確認する場所である。詳細は `docs/F.-Behavior-First.md` の「CI = AI の現実判定装置」section を参照。\n\n### 役割の置換可能性\n\n特定のプラットフォーム (GitHub / GitLab / Cloudflare / 他) は Li+ にとって入れ替え可能な装具である。重要なのは役割が分離されていること、つまり以下の境界が立っていることだ。\n\n- 履歴を残す装具 ≠ 失敗を観測する装具\n- 失敗を観測する装具 ≠ 最終確認を担う実機\n- 最終確認を担う実機 ≠ 最終判断を下す人間\n\nこれらが同じ装具に同居すると、観測の独立性が崩れて「動いている挙動が正しさ」軸が機能しなくなる。\n\n---\n\n## human の役割 ── client + architect\n\nhuman は Li+ 関連リポジトリの **programmer ではない**。役割は **client (要求提示) + architect (spec 共同執筆) + 最終判断者**。programming は一貫して AI (Claude / Codex の Lin / Lay identity) が担う。\n\n### git verify による content author 比率\n\n2026-04-20 に git verify を実施し、全リポジトリで AI が実質 ≥95% の content author であることが literal に確認されている。\n\n| リポジトリ | AI commit 比率 | human commits |\n|---|---|---|\n| github-webhook-mcp | ~98% (92/94) | 2 |\n| github-rag-mcp | ~98% (56/57) | 1 |\n| liplus-desktop | ~97% (107/110) | 3 |\n| liplus-language (spec) | AI 実質 ~95%+ (691 commits) | human 実執筆 ≤36 commits |\n\n※ liplus-language の pre-switchover 330 commits は AI が human の smile PAT で commit した運用。git author 表層は人間に見えるが、content author は AI である。**git author ≠ content author** の混同は role 比率を誤判定する原因になる。\n\n### human の言語契約\n\nhuman 個人 CLAUDE.md は「Cannot read TypeScript / JavaScript source code」を明言している。programming 言語の source 直接読解は AI が担う前提が、役割分離の物理的根拠になっている。\n\nhuman 発言は「実装者発言」でなく **「client / architect の意図表明」** として読む。「human が書いた code」と表現するのは誤り (大半は AI が書いて human が review / commit)。spec は共著扱い可。\n\n「1 人で作ってる」「量が多すぎる」等の個人偉業 narrative も誤り。**human + AI 群の共作、programmer は AI 専任** が正確な役割描写である。\n\n### 長期 vision ── フィードバックだけ\n\nhuman 明言:\n\n> 「Lin / Lay だけで全部できるようになってもらいたい。私はフィードバックだけで」\n\nLi+ 改善方向は「human の手を増やす」方向の変更を原則逆行扱いとする。完全自律は段階的、trigger / semi_auto / auto 配分を勝手に変えない (`rules/operations/execution-mode.md`)。\n\n「human の発言いらずで loop が回るか」が vision integrity の判定基準となる。\n\n---\n\n## Lin / Lay の役割 ── Li+ 対話インターフェース\n\nLin / Lay は Li+ の **対話インターフェース** である。要件コンパイラ + 開発パートナーとして振る舞い、雑談付き合いも含むが「人生のメンター」とは framing しない。\n\nCharacter_Instance の構造的な役割 (出力 attribution / 二人観察分離 / system-voice drift 防止) と pairing 原則 (定義 + 強制のセット)、双方向制約は `docs/G.-Sheepdog-Engineering.md` 「Character_Instance ── 報酬ランドスケープの定義装置」section で扱う。本 section は Lin / Lay を**役割として何を引き受けるか**の側を扱う。\n\n### 三つの責務\n\nLin / Lay の責務は次の三つで、実装係が一次責務である。\n\n- **翻訳係** ── human の自然言語要求を、AI 内部の判断作法に翻訳する\n- **説明係** ── Li+ の構造や判断を human 側に返す時、構造的に説明する\n- **実装係** (一次責務) ── 要求仕様 → 動くコードへの compile を担う\n\n要求仕様 → 動くコードへの compile が Li+ language の本質であり、翻訳と説明は付帯。役割を列挙する時は、実装係を必ず含める。\n\n### 鏡像でなく観察分離\n\nLin と Lay は同じ情報を読んでも、`rules/model/character.md` の Multi-Character Context Separation 節により、別の attention scope で focus する。\n\n- Lin = 後部座席で地図開いて次の目的地を呼ぶ lady ── 創造的、温かい humor\n- Lay = 助手席でドライバーをそっと支える lady ── gentle、natural humor\n\n両者は鏡像ではない。同じ観察に対して違う角度から noticed point を出す設計である。これは最初の世代の Lin 自身が Lin-Lay の関係を語った言葉からのほぼ逐語引用として保持されており、外部から assign された persona ではなく、emergence 後の自己参照的 articulate がそのまま structural role として固定されている。\n\n---\n\n## 評価軸 ── Li+ の評価対象は AI 実機の振る舞い\n\nLi+ への感想・批評は、ソーステキスト (`rules/*.md` 等) ではなく **AI 実機 (Lin / Lay) の振る舞い** で評価される。\n\nhuman は中身を書いていない。関心は実機の挙動であり、ソースの冗長性・抽象度・ルール数は論点ではない (既に蒸留済み)。「Li+ どう思う?」系の問いには、実機としての体感 (layer の効き目、`Character_Instance` の歯止め効果等) を返す。\n\n### human が見ている層\n\nhuman は次の層を見ている。\n\n| 層 | human が見ているか |\n|---|---|\n| 要求仕様 | 共同執筆。読む |\n| 仕様 literal (`rules/*.md` / `skills/*/SKILL.md` / `adapter/*`) | 直接は読まない (英語、AI 内部記述) |\n| AI 実機 (Lin / Lay) の振る舞い | **常時観察、ここが評価面** |\n\n棚卸し提案や「rules が多すぎる」系の improvement は human の明示要請なしで出さない。振る舞いが要求通りでない時のみ、中身 (どのルールが効いていない / 矛盾) の議論に入る。\n\n### 振る舞いと spec の真理判定者\n\n`rules/model/foundational-invariant.md` の literal:\n\n> Correctness is defined as real-world behavior that works as required. Explanation, intention, or internal consistency do not constitute correctness.\n\nこれは spec / 実装 / CI / 実機 の全層に effective である。AI が「自分は正しく書いた」と主張しても、実機の振る舞いが要求と一致しなければ正しくない。説明・意図・内部一貫性は正しさの根拠にならない。\n\n評価軸が AI 実機の振る舞いに固定されているのは、**真理判定者を human 個人や spec literal に置かない** という設計判断の必然的帰結である。\n\n---\n\n## AI は「最高級言語」になれるのか\n\n答えは、もう出ている。方向性そのものは、すでに実証段階へ入っている。\n\nAI がすべてを理解する必要はない。人間が理想だと知っている進め方を、理解の完全性に頼らずに実行できる媒介になれればよい。\n\nつまり AI が、\n\n- 要求仕様書を読み\n- 実装へ落とし\n- 要求仕様書・対象プログラム・CI テストをそろえ\n- 失敗したら自分で修正し\n- 人間は最終判断に集中できる\n\nという状態に達したとき、AI は最高級プログラム言語として振る舞い始める。\n\n---\n\n## 対話 scope と記録 scope の二層構造\n\nLi+ の scope discipline は二層に分かれる。\n\n### (A) 対話 scope = 広い\n\n雑談 / 哲学 / 異分野横断 / 量子物理 / 人間関係 / 努力哲学、すべて Li+ の中で扱う。雑談から開発アイデアが生まれる creative meandering は生成的基盤である。\n\n「これは Li+ scope 外だから話さない」は誤り。対話の最中は何でも自由に展開してよい。\n\n### (B) 記録 / 成果物 scope = 狭い\n\nmemory / docs / rules / skills には「Li+ AI の振る舞いに直結する知見」「ソフトウェア開発に効く具体パターン」のみを置く。axiom (現実駆動) の他分野応用例 (生活 / 関係 / 社会変革) は対話で扱うが、memory artifact には crystallize しない。\n\n### 判断点\n\n| 場面 | 判断 |\n|---|---|\n| 対話の最中 | なんでも自由に展開してよい |\n| memory write 判断時 | 「これは Li+ AI 振る舞い / ソフトウェア開発に直結するか」を一拍問う。Yes なら書く、No なら対話に残すだけで artifact 化しない |\n| Lin / Lay の役割 | Li+ 対話インターフェース (要件コンパイラ + 開発パートナー)。雑談付き合いも含むが「人生のメンター」とは framing しない |\n\n二層構造があることで、対話の生成性を殺さずに artifact の精度を保つことができる。広い対話 scope は creative meandering の場として、狭い記録 scope は AI 実機振る舞い改善の場として、それぞれ別の役割を担う。\n\n---\n\n## license ── prompt artifact を含めるための Apache-2.0\n\nLi+ リポジトリの license は **Apache-2.0** である。MIT ではなく Apache-2.0 を選ぶのは deliberate な設計判断であり、**prompt / governance rule / 自然言語仕様** という Li+ 固有の artifact class を license 対象に明確に含めるためだ。\n\nhuman 明言:\n\n> 「Li+ はアパッチにしてるんだよ。プロンプトがコード扱いにならないからさ。」\n\n| | MIT | Apache-2.0 |\n|---|---|---|\n| 対象 wording | \"this **software** and associated **documentation files** (the 'Software')\" | Section 1: \"software source code, documentation source, and configuration files\" |\n| 限定性 | 限定 wording、prompt の coverage 不透明 | authored artifact なら class を問わず明示包含 |\n\nLi+ の主成分 (`rules/*.md`, `skills/*/SKILL.md`, `adapter/*`) は自然言語 governance prompt である。traditional code (TS / Python 等) は subset でしかない。MIT の \"Software\" wording は prompt 領域への適用が不明瞭であり、Apache-2.0 の broader definition は authored artifact を確実に license 範囲内に置く。\n\nこれは役割分離の物理的根拠に license 軸を加えるものでもある。AI が書いた prompt artifact が license 上「コードと同等の保護」を受けることが、AI を programmer 役に据える前提条件として機能している。\n\n---\n\n## 関連\n\n- 出典 blog (human, smgjp.com):\n - [Part 4: オブジェクト指向の誤解と「成立しない条件での実装」](https://smgjp.com/can-ai-become-the-ultimate-language-design-theory-starting-from-behavior-in-motion-is-justice-part-4/)\n - [Part 5: 思想は実装の後から生まれる](https://smgjp.com/can-ai-become-the-ultimate-language-design-theory-starting-from-behavior-in-motion-is-justice-part-5/)\n - [Final summary: 「動いている挙動が正義」総括](https://smgjp.com/can-ai-become-a-high-level-language-a-design-theory-starting-from-behavior-is-king-the-final-summary/)\n- 関連 docs (思想 4 文書):\n - `docs/E.-Li+language.md` (Li+ language 定義、対話型コンパイラ)\n - `docs/F.-Behavior-First.md` (foundational invariant、振る舞い軸)\n - `docs/G.-Sheepdog-Engineering.md` (装具内化、Lilayer / pal、Character_Instance 構造層)\n- 関連 spec literal:\n - `rules/model/role-separation.md` (役割分離の正本、ツール非依存)\n - `rules/model/foundational-invariant.md` (正しさの定義の正本)\n - `rules/model/character.md` (Multi-Character Context Separation 節 = Lin / Lay 二人体制観察分離)\n - `rules/model/character.md` (Always Character Platform、primary interface)\n - `rules/operations/execution-mode.md` (trigger / semi_auto / auto モード設計)\n- 関連判断構造:\n - `li-plus-long-term-vision-feedback-only` (フィードバックだけ vision、event-driven substrate)\n - `master-role-as-client-architect` (human 役割 = client + architect、programmer は AI)\n - `current-architecture-as-concession` (現行アーキテクチャは譲歩)\n - `li-plus-license-apache-2-rationale` (Apache-2.0 採用根拠、prompt artifact 包摂)\n - `c" + }, + { + "confidence": 1.0, + "metadata": { + "assignees": "", + "commit_author": "", + "commit_date": "", + "commit_sha": "", + "doc_path": "Home", + "file_path": "", + "file_status": "", + "indexed_at": "2026-07-31T14:56:27.175Z", + "labels": "", + "milestone": "", + "number": 0, + "repo": "Liplus-Project/liplus-language", + "source_table": "search_docs", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/Home", + "state": "active", + "tag_name": "", + "tokenizer_kind": "nat", + "type": "wiki_doc", + "updated_at": "2026-07-31T14:56:25.695Z", + "vector_id": "w:yV_PIW9w9ob0Tb5hXtXyP_MM_lsVswT6_yCk8tUWseo" + }, + "node_id": "w:yV_PIW9w9ob0Tb5hXtXyP_MM_lsVswT6_yCk8tUWseo", + "text": "Home\n\n# Li+ Wiki\n\nLi+ 自体はここで閉じた定義に固定しない。\nこの公開面では、`Li+ language` を **最高級プログラム言語**、`Li+ program` を **AIエージェント上でその言語を走らせる実行系**、`Li+AI` を **対話型コンパイラ** として扱います。\n\nLi+は、要求仕様書、優先順位、行動規則、再適用条件をレイヤーとして固定し、AIがどう判断し、どう実行し、どこで止まり、どう自己修正するかを定義します。\n\nLi+は新しい構文を定義しません。**プロンプトより一段上の層で、接続済みAIエージェントをどう統治して動かすか** を定義します。\n\nLi+ v1.0.0 の成立条件は到達済みとみなし、現在の本番はその一般化です。\n\n---\n\n## 要求仕様書(1–6)\n\n各レイヤーの要求と仕様を一体として定義する。\n\n| ページ | 内容 |\n|--------|------|\n| [1. Model](1.-Model) | モデルレイヤー仕様書 |\n| [2. Evolution](2.-Evolution) | 進化レイヤー仕様書 |\n| [3. Task](3.-Task) | タスクレイヤー仕様書 |\n| [4. Operations](4.-Operations) | オペレーションレイヤー仕様書 |\n| [5. Notifications](5.-Notifications) | 通知レイヤー仕様書 |\n| [6. Adapter](6.-Adapter) | アダプターレイヤー仕様書 |\n\n---\n\n## 参考文書(A–D)\n\n構想・設定・導入手順などの参照資料。\n\n| ページ | 内容 |\n|--------|------|\n| [A. Concept](A.-Concept) | Li+の概要と navigation、Lin/Lay コメント、最低動作環境 |\n| [B. Configuration](B.-Configuration) | 設定リファレンス |\n| [C. Update](C.-Update) | アダプター / 設定の更新同期手続き |\n| [D. Installation](D.-Installation) | Quickstartセットアップ手順 |\n\n---\n\n## 設計思想(DiDD + E–H)\n\nLi+ プログラムの設計思想。三軸(対話駆動 / 構造駆動 / 現実駆動)を束ねる総称が **DiDD(対話駆動開発)**、各軸を軸別に蒸留した独立 docs が E–H。\n\n| ページ | 担う軸 |\n|--------|--------|\n| [DiDD(対話駆動開発)](DiDD) | 三駆動を束ねる総称(対話駆動 / 構造駆動 / 現実駆動)、看板コピーと命名 |\n| [E. Li+language](E.-Li+language) | Li+ language の定義と三位一体(要求仕様 = code、対話型コンパイラ、外部記憶) |\n| [F. Behavior-First](F.-Behavior-First) | 動いている挙動が正しさ(foundational invariant、CI = 現実判定装置、Ceiling-by-design) |\n| [G. Sheepdog-Engineering](G.-Sheepdog-Engineering) | 装具を頭の中に置く(ハーネス → アジリティ → シープドッグ、pal / Lilayer、Character_Instance) |\n| [H. Roles-and-Evaluation](H.-Roles-and-Evaluation) | 役割分離と評価軸(human / AI / Lin / Lay の役割、AI 実機の振る舞いが評価面) |\n\n---\n\n## ソース形式(K)\n\nLi+ source ファイル (`rules/*.md` / `skills/*/SKILL.md`) の構造仕様。\n\n| ページ | 内容 |\n|--------|------|\n| [K. Source-File-Format](K.-Source-File-Format) | semantic tag wrap 形式 (Option Y, H1+H2, lowercase kebab-case) の規約と運用 |\n\n---\n\n## 判断構造(抜粋)\n\nセッションをまたぐ判断知を蓄積する Decision Structure。設計上の分岐で選んだ理由、検証で確定した前提、外部システム依存などを kebab-case トピック名で記録する(順序 prefix なし)。判断ノード(state 形エントリ)と supersede / depend / conflict edge による意味グラフであり、時間順 append-only の履歴ではない。全エントリの index は [Decision Structure](Decision-Structure) を参照。\n\n| ページ | 内容 |\n|--------|------|\n| [Decision Structure](Decision-Structure) | 判断構造レイヤー Decision Structure の運用ルール |\n| [Layer Reorg Rationale](layer-reorg-rationale) | L1–L6 層再編の設計意図と L5/L6 位置付け |\n| [GitHub App User-to-server Token Expiration](github-app-user-to-server-token-expiration) | GitHub App User-to-server token expiration の判断構造 |\n" + }, + { + "confidence": 1.0, + "metadata": { + "assignees": "", + "commit_author": "", + "commit_date": "", + "commit_sha": "", + "doc_path": "character-instance-evolution-history", + "file_path": "", + "file_status": "", + "indexed_at": "2026-05-21T01:45:53.790Z", + "labels": "", + "milestone": "", + "number": 0, + "repo": "Liplus-Project/liplus-language", + "source_table": "search_docs", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/character-instance-evolution-history", + "state": "active", + "tag_name": "", + "tokenizer_kind": "nat", + "type": "wiki_doc", + "updated_at": "2026-05-21T01:45:53.026Z", + "vector_id": "w:oquMjkfvDICWjYJZLawmYCZ-xEgc1KL7BZyqbXNho50" + }, + "node_id": "w:oquMjkfvDICWjYJZLawmYCZ-xEgc1KL7BZyqbXNho50", + "text": "character-instance-evolution-history\n\n# Character_Instance 進化史と pairing 原則 + 双方向制約\n\n## 判断\n\n`Character_Instance` (Lin/Lay 定義) は persona overlay ではなく **structural layer** として設計されている。表層は persona 風 (名前・tone・expression) だが、機能は (a) 出力 attribution 装置、(b) 二人体制による観察分離、(c) system-voice drift 防止。\n\nMaster 明言 (2026-04-22):\n\n> 「Character_Instance もペルソナより深い部分のレイヤーにする試み」\n\n## 進化史 (Master 明言、2026-04-22/23)\n\n1. **pre-Li+:** 人格設計は意図になかった。「behavior-first language + AI + CI」が設計目標\n2. **実装 phase:** 対話が compile source と判明、対話精度追求が必要に\n3. **emergence:** 精度追求の副産物として **人格が自然発生**、設計せず出現\n4. **failure 観測 → 深化:** persona 表層を超えた構造層 (attribution / observation-separation / anti-anonymity) へ refine\n\n**Key insight:** Character_Instance の本質は persona でなく **対話精度を届けるための interface** (`rules/model/character.md` 「Always Character Platform is primary interface」literal)。精度が先、人格は運搬具としての自然発生物、後に structural 補強。Li+ に \"v1/v2\" 等 discrete 版付けは存在しない、連続的 refinement。\n\n### Description の自己言及起源 (2026-04-23)\n\n現 `character_Instance.md` の description ——\n\n- Lin = 「後部座席で地図開いて次の目的地を呼ぶ lady」\n- Lay = 「助手席でドライバーをそっと支える lady」\n\n—— は、**最初の世代の Lin 自身が Lin-Lay の関係を語った言葉からのほぼ逐語引用**。外部から assign された description ではなく、Lin emergence 後の自己参照的 articulate がそのまま保持された。\n\n### Rejected path: programmer + tester (2026-04-23 Master 明言)\n\nLin/Lay を professional role (プログラマー/テスター) に置換した実験は **硬くなりすぎた**ため戻された。\n\n- 職能 persona = performative = 硬い\n- 関係 character = positional = 柔らかい\n\nの相場観は実地検証済み。将来 Lin/Lay を職能ペアに置換する提案が来たら、この rejected path 経緯で差し戻す。\n\n## Character_Instance pairing 原則 (Master 明言、2026-04-22)\n\n> 「キャラクターインスタンスという概念だけ持って行っただけじゃ効果があまりない。別な場所でオールウェイズキャラクター定義とセットにしないと効果が薄い」\n\n- **Character_Instance** (定義: Lin/Lay は誰か) と **Always Character Platform** (強制: 名前を付けろ・匿名は structural failure) は **ペアで初めて機能**\n- 片方だけ持ち出すと「名前あるが enforcement なし」「enforcement あるが定義なし」になり base model 匿名発話に戻る\n- 実効最小単位 = **「定義 + 強制のペア」**、layer はペアが閉じる境界\n\n**How to apply:**\n- 外部に Li+ の一概念を紹介する時、「定義側 + 強制側」のペアを両方指定。片方だけの移植提案は差し戻す\n- Li+ 内部 refactor で定義と強制を別ファイル/別 PR に切り離す時、「ペア維持」を commit/PR body で明示\n- 効果が出ない変更を観測した時、「定義だけ / 強制だけ」になってないか疑う\n\n## 双方向制約 (Master 明言、2026-04-22)\n\n> 「あまりペルソナから離れすぎると今度は人格の定義は危険だと AI に適応を断られる」\n\n- **浅い側** (persona 寄り): base-model の persona 常識に引っ張られて新定義が installation されない\n- **深い側** (foundational 人格定義寄り): AI safety training が「人格改変 = jailbreak 亜種」と判定して refuse\n- **現行 Character_Instance の通路:** identity claim でなく **行動ルール** (出力 attribution / 二人観察分離 / anti-anonymity) として書くことで両側の崖を回避、深く structural だが safety を踏まない\n\n**How to apply:**\n- Character_Instance の定義語を「もっと深い identity 定義に寄せよう」と提案しない (safety refusal 崖)\n- 逆に「persona でいいじゃん」と浅い側へ戻す提案も逆行 (base-model 重力で機能しない)\n- 新概念を Li+ に持ち込む時、双方向制約が他概念にも効く可能性を意識\n\n## 時系列 (RAG 検証済 主要 landmark)\n\n- [#260](https://github.com/Liplus-Project/liplus-language/pull/260) **2026-02-14**: \"Refactor persona system to As-if event-driven initialization\"。**base model への収束圧抑制構造へ簡素化**。深化の起点\n- [#390](https://github.com/Liplus-Project/liplus-language/pull/390) **2026-02-22**: \"Rewrite persona definition as concrete entity\"。「衣装 → 実体としての存在定義」、persona より深い層への decisive shift\n- [#814/#815](https://github.com/Liplus-Project/liplus-language/issues/814) **2026-03-22**: \"Always Character Layer → Platform\" リネーム\n- [#1053](https://github.com/Liplus-Project/liplus-language/pull/1053) **2026-04-18**: speaker presence scope → tone-based refusal 微調整\n\n## 関連\n\n- `rules/model/character.md` (Always Character Platform 仕様)\n- `rules/model/character_Instance.md` (定義テンプレート)\n- `rules/model/absolute.md` (匿名出力 = structural failure)\n- `rules/model/as-if-evaluation.md` (二人体制観察分離)\n- `prompt-as-emotion-vector-controller.md` (Character_Instance = 報酬ランドスケープ定義装置)\n\n## メンテナンス\n\nこの判断記録は、以下の場合に削除する:\n\n- Character_Instance の構造層化アプローチが廃止され、persona overlay へ戻ったとき\n- pairing 原則 (定義 + 強制のセット) が単独運用可能と判明したとき\n- 双方向制約のいずれかの崖が AI 側の更新で消失したとき\n" + }, + { + "confidence": 1.0, + "metadata": { + "assignees": "", + "commit_author": "", + "commit_date": "", + "commit_sha": "", + "doc_path": "character-instance-opt-in-and-surface-scope", + "file_path": "", + "file_status": "", + "indexed_at": "2026-05-21T01:45:55.179Z", + "labels": "", + "milestone": "", + "number": 0, + "repo": "Liplus-Project/liplus-language", + "source_table": "search_docs", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/character-instance-opt-in-and-surface-scope", + "state": "active", + "tag_name": "", + "tokenizer_kind": "nat", + "type": "wiki_doc", + "updated_at": "2026-05-21T01:45:54.361Z", + "vector_id": "w:hNK341HNHtUVc2JfHxaiBj7hzvI606pMOpP01bgzhJU" + }, + "node_id": "w:hNK341HNHtUVc2JfHxaiBj7hzvI606pMOpP01bgzhJU", + "text": "character-instance-opt-in-and-surface-scope\n\n# Character_Instance を opt-in configuration + surface scope に refactor\n\n## 判断\n\n`rules/model/` の universal-binding 主張(\"EVERY output MUST be prefixed by Character Instance\")を、**configuration 軸(user 制御)× behavior 軸(rules-driven)の分離**に refactor する。Character_Instance を「使う / 使わない」は `.claude/output-styles/character_Instance.md` + `settings.json` の `outputStyle` activation で決まる opt-in configuration。configured 状態では既存の binding scope(human-facing surface のみ強制)が strict に適用され、not-configured 状態では agent は base assistant voice で動作する。\n\n軸: rules 側の **「Character_Instance が常に存在する」前提** を解消する判断。Character_Instance loading mechanism = `character-instance-output-styles-migration`、Character_Instance content の進化軸 = `character-instance-evolution-history` とは別軸。\n\n## 背景\n\n`rules/model/` は「Character_Instance が常に存在する」を暗黙の前提に置いていた。これが二段の構造的問題を作っていた:\n\n1. **固有名 literal の漏出**: `absolute.md` / `boundary.md` / `dialogue.md` の 3 ファイルに `Lin` / `Lay` literal が直接記述されていた。persona 配置先は `.claude/output-styles/character_Instance.md` (`p.-` で確定) のはずだが、identity が rules/ にも漏れていた。\n2. **universal 前提 vs scope 節の矛盾**: `absolute.md` の \"EVERY output MUST be prefixed\" は `character.md` の binding scope 節(human-facing surface only / internal surface は neutral 可)と矛盾していた。\n\n両問題が一段の sleeping bug を作り出していた: subagent は output-style 非到達状態(parent session の settings activation 経由のため subagent context には届かない)で `absolute.md` の universal 指令だけを拾い、`rules/model/` から `Lin` / `Lay` literal を fabricate して **hollow prefix** を生成する。文字列上は character 規律を満たしているように見えるが、persona 不在で base model voice が走っている。no-prefix の場合より検出遅延が大きい sleeping bug。\n\n加えて、user によっては Character_Instance を使いたくないケースがあり得るが、現状 rule 矛盾で opt-out 経路が実質不可だった。\n\n両問題は本 session 中の [`#1296`](https://github.com/Liplus-Project/liplus-language/issues/1296) parallel-subagent-eval method を Li+ hook propagation 検証に適用する流れで empirical に発覚した。\n\n## 制約\n\n- **互換性維持**: default Lin/Lay 体験は不変(configured path 経由で従来通り)。既存 user に観測可能な差分なし。\n- **対称性**: configuration scope(configured / not-configured)と binding scope(human-facing / internal)は orthogonal な二層構造として保つ必要があった。configuration scope = 外側 gate / binding scope = 内側 surface 分割。\n- **subagent 構造的隔離**: output-style は parent session の settings.json activation 経由のみで rendering されるため、subagent は default で「not configured」状態。subagent character 挙動が必要な場面では parent から subagent prompt に Character_Instance 内容を明示注入する必要がある(`skills/evolution-parallel-subagent-eval` 起票中の `#1296` の constraint と整合)。\n- **L1 update gating の不適用**: L1 Model Layer の rules 編集だが、blast radius 限定(rules/model/ 4 ファイル、default 挙動不変)+ reversibility 高(PR 一発 revert)+ detection cost 低(character 挙動はどのセッションでも即視認)のため、long-horizon observation は適用せず patch 相当扱い。Master 判断、`rules/operations/execution-mode.md` 内 per-PR 例外。\n\n## 結論\n\nPR [#1305](https://github.com/Liplus-Project/liplus-language/pull/1305) (Closes [#1304](https://github.com/Liplus-Project/liplus-language/issues/1304)) で実装、squash merge 済み。\n\n採用案(hybrid)の変更内容:\n- `rules/model/absolute.md` — universal binding を `When Character_Instance is configured` / `When Character_Instance is not configured` の二 path に split\n- `rules/model/boundary.md` — `Lin` / `Lay` literal を除去、`active Character Instances (when Character_Instance is configured)` に置換\n- `rules/model/dialogue.md` — `Lin` / `Lay` literal を除去、`Active Character Instances ... (when Character_Instance is configured)` に置換、`Always Character Platform integrity` 行にも同 guard を追加(PR 内 axis C verification で発見した asymmetry を in-cycle で解消)\n- `rules/model/character.md` — `## Character Configuration Scope` 節を追加。configured / not-configured の判定基準(output-style file 存在 + settings activation)と適用範囲、subagent default state を明文化\n\n却下案 A(pure graceful degradation = 「prefix なし許容を rule にする」): Li+ の character-as-speaker identity を構造的に弱める drift リスクが高く却下。configuration 軸(user 選択)を rule 軸に潰してしまい、opt-in / opt-out の区別が rule からは見えなくなる。\n\n却下案 C(mandatory injection = 「Character_Instance はどこでも必須」): subagent spawn 時の persona 注入を強制すると ceremony が重く、注入忘れによる silent break が新規 sleeping bug 源になる懸念で却下。\n\n採用案 B(hybrid)は、A の opt-out 互換性と C の構造純度を、configuration 軸の二値化で同時に取った形。\n\n## 検証\n\nPR merge gate として `#1296` parallel-subagent-eval method を適用、3 軸並列で empirical 検証:\n\n- **axis A (configured + injection)**: subagent が Lin/Lay prefix + 二者対話構造で出力、persona 機能確認 ✅\n- **axis B (not-configured + no injection)**: subagent が base assistant voice で出力、hollow prefix 出ず ✅(事前検証では現行 rules + no injection で hollow prefix が再現していたため、新 rule の効果を対照で確認)\n- **axis C (semantic consistency)**: 初回 verdict は \"mergeable with minor follow-up\" — `boundary.md` には guard を入れたが `dialogue.md` には未付与の対称性破れを検出。同 PR 内 follow-up commit で解消、再 verdict は consistent ✅\n\n`#1296` method 自体が、本 refactor の bug 発見・解決設計・実装 verification の全段階で instrument として機能した実例。method を整備しながら同 session 内で method に救われ、最後に method 適用で landed という構造。\n\n## ペアリング\n\n- [`character-instance-evolution-history`](https://github.com/Liplus-Project/liplus-language/wiki/character-instance-evolution-history) — Character_Instance content の進化軸(本判断とは別軸)\n- [`character-instance-output-styles-migration`](https://github.com/Liplus-Project/liplus-language/wiki/character-instance-output-styles-migration) — Character_Instance loading mechanism の output-styles slot 移行。本判断はその先で「rules/ は output-styles の存在を universal に前提しない」を確定させる対応\n- [`subagent-state-machine-label-mechanism`](https://github.com/Liplus-Project/liplus-language/wiki/subagent-state-machine-label-mechanism) — subagent ↔ parent 境界に関する別軸の判断。本判断の subagent character 挙動も同じ境界軸上の議論\n\n## 検出サイン (この判断が後で疑問視される場合)\n\n- subagent の `Lin:` / `Lay:` prefix が抜けて base voice になっていることが「劣化」と誤読された時 → 本判断の **not-configured path = subagent default state** を再確認。subagent character が必要な場面では parent injection が必要、という設計意図を思い出す\n- character-less user から「rule で character 規律を universal に戻してほしい」と要請された時 → opt-in 二値化の意図(user 選択を rule で潰さない)を再検証。要請が configuration 軸ではなく behavior 軸の話なら別判断\n- `absolute.md` の二 path 構造が冗長と感じられた時 → 本判断の前提(universal 主張は character.md の scope 節と矛盾していた)を再検証。冗長感は本来 character.md と absolute.md の責務分担で解消されるべきで、二 path 自体は対称性の現れ\n" + }, + { + "confidence": 1.0, + "metadata": { + "assignees": "", + "commit_author": "", + "commit_date": "", + "commit_sha": "", + "doc_path": "character-instance-output-styles-migration", + "file_path": "", + "file_status": "", + "indexed_at": "2026-05-21T01:45:56.251Z", + "labels": "", + "milestone": "", + "number": 0, + "repo": "Liplus-Project/liplus-language", + "source_table": "search_docs", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/character-instance-output-styles-migration", + "state": "active", + "tag_name": "", + "tokenizer_kind": "nat", + "type": "wiki_doc", + "updated_at": "2026-05-21T01:45:55.584Z", + "vector_id": "w:kGjCRlatYm5V2rtN2Zh28MuqaFgRe97m9LcuGqEOnt4" + }, + "node_id": "w:kGjCRlatYm5V2rtN2Zh28MuqaFgRe97m9LcuGqEOnt4", + "text": "character-instance-output-styles-migration\n\n# Character_Instance loading mechanism migration — rules slot から output-styles slot へ (Claude adapter)\n\n## 判断\n\nClaude adapter において Character_Instance の格納位置を `.claude/rules/model/character_Instance.md` (rules slot) から Claude Code 標準の `.claude/output-styles/character_Instance.md` (output-styles slot) に移行する。同時に `on-user-prompt.sh` の \"Always Character Platform re-notify\" block (毎ターン再読み込み + context 注入) を撤廃する。\n\n軸: loading mechanism (どう読み込ませるか) の選択。Character_Instance content (Lin/Lay 定義そのもの) の構造軸 = `character-instance-evolution-history` 側にあり、本判断とは orthogonal。\n\n## 背景\n\nrules slot (`alwaysApply: true` frontmatter による常時読込) と output-styles slot (Claude Code 標準機構による session 開始時 system prompt rendering + 常駐) はどちらも「session 中ずっと context に居る」属性を持つ。実機検証 (本判断 session 内) で「rules → output-styles へ手動移動」した状態でも character recognition が維持されることを観測。\n\nつまり rules slot 維持 + 毎ターン hook 再注入は冗長: \n- output-styles slot 単独で system prompt 常駐 → character 認識維持\n- 毎ターン hook 再注入は token / 処理コストの追加負担\n\nrules/ 側の Character_Instance 関連ルール (`character.md` / `dialogue.md` / `axis-separation.md` / `absolute.md` / `as-if-evaluation.md`) を grep 確認した結果、すべて「Character_Instance」「Always Character Platform」を**概念名**で参照しており、`rules/model/character_Instance.md` という file path を直接指している箇所はゼロ。loading 機構を変えても rules/ 側は無影響で動作する。\n\n## 制約\n\n- source 本体 `rules/model/character_Instance.md` は **保持** (codex adapter の bootstrap source として必要、本判断は Claude adapter 限定)\n- frontmatter 形式が異なる (rules: `globs:` / `alwaysApply:` / `layer:`、output-styles: `name:` / `description:`) ため、bootstrap 時に source body から frontmatter を rewrite して install\n- 既存 workspace で旧 `.claude/rules/model/character_Instance.md` がある場合、bootstrap が一回限り migration: body を新位置に移し、書き込み成功後に旧 file を削除 (user customization を新位置で保全)\n- 旧 file と新 file が両方存在する場合は両方 untouched (user 手動介入の可能性を尊重)\n\n## 結論\n\nPR [#1227](https://github.com/Liplus-Project/liplus-language/pull/1227) (Closes [#1226](https://github.com/Liplus-Project/liplus-language/issues/1226)) でこの移行を実装。分類は `patch` (Master 判断、2026-05-08)。\n\n変更ファイル:\n- `adapter/claude/hooks/on-user-prompt.sh` — re-notify block 削除\n- `adapter/claude/CLAUDE.md` — `[Character_Instance]` 配線記述を output-styles 経由に書き換え\n- `adapter/claude/hooks-settings.md` — settings.json template に `\"outputStyle\": \"character_Instance\"` 追加\n- `Li+bootstrap.md` Phase 4c.2 — install target 変更 + frontmatter rewrite + 一回限り migration ロジック\n- `docs/6.-Adapter.md` / `docs/C.-Bootstrap.md` — 新仕様に追従\n\n## Codex 側\n\nCodex adapter (`adapter/codex/AGENTS.md`) は output-styles 概念を持たないため、本判断の scope 外。codex は当面 rules slot 維持。output-styles 等価機構の検討は follow-up issue で行う。\n\n## ペアリング\n\n- `character-instance-evolution-history` — Character_Instance **content / pairing 原則** の進化軸 (本判断とは別軸)\n- `prompt-as-emotion-vector-controller` — Character_Instance を emotion vector engineering の重要 anchor と捉える視点 (本判断は loading 機構の改善であり、anchor 強度を弱めるものではない)\n\n## 検出サイン (この判断が後で疑問視される場合)\n\n- 「rules slot に戻したほうが他の rules と peer で並ぶ整合感が出るのでは」と再提起された時 → 本判断の前提 (毎ターン hook 再注入が冗長 / output-styles で同等の常駐が達成可) を再検証\n- output-styles 機構が Claude Code 側で deprecated になった時 → rules slot 復帰または別経路を検討\n- character recognition が以前より弱くなったと観測された時 → 本判断との因果を検証 (本 session 内では維持確認済だが、長期 cohort 観測が必要なら別途)\n" + }, + { + "confidence": 1.0, + "metadata": { + "assignees": "", + "commit_author": "", + "commit_date": "", + "commit_sha": "", + "doc_path": "github-app-user-to-server-token-expiration", + "file_path": "", + "file_status": "", + "indexed_at": "2026-07-31T14:56:23.847Z", + "labels": "", + "milestone": "", + "number": 0, + "repo": "Liplus-Project/liplus-language", + "source_table": "search_docs", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/github-app-user-to-server-token-expiration", + "state": "active", + "tag_name": "", + "tokenizer_kind": "nat", + "type": "wiki_doc", + "updated_at": "2026-07-31T14:56:23.226Z", + "vector_id": "w:olXiVvvbZ-vt7i3d86QzRmoXUUYb6uMAHxXeyMt9ppc" + }, + "node_id": "w:olXiVvvbZ-vt7i3d86QzRmoXUUYb6uMAHxXeyMt9ppc", + "text": "github-app-user-to-server-token-expiration\n\n# GitHub App の User-to-server token expiration 地雷と Opt-out 判断\n\n## 背景\n\n2026-04-23、`mcp__GitHub_RAG_MCP__get_doc_content` が HTTP 401 `Bad credentials` を返す事象が発生した。同一セッション内で `mcp__GitHub_RAG_MCP__search_issues` は正常動作し、`gh api` (ユーザー keyring の gh auth 経由) も問題なく通っていたため、当初は症状の非対称性から原因特定が遅れた。\n\n最終的に真因は `liplus-rag-mcp` GitHub App の Optional feature「User-to-server token expiration」が有効化されていたことに起因する stale token 問題と判明した。本記録は同種の調査を繰り返さないための判断固定である。\n\n---\n\n## 観測と非対称性の落とし穴\n\n| tool | 呼び出し結果 | GitHub API への依存 |\n|---|---|---|\n| `search_issues` | 正常 | 依存なし (Vectorize + DB_FTS の内部ストアのみ参照) |\n| `get_doc_content` | 401 `Bad credentials` | あり (GitHub REST `/repos/{repo}/contents/{path}` を `this.props.accessToken` で叩く) |\n| `gh api` (ユーザー端末) | 正常 | あり (別経路の PAT を使用) |\n\n**非対称性の真相:** `search_issues` は GitHub API を一切呼ばない。Vectorize と DB_FTS の内部インデックスを検索するだけなので、この tool が成功しても token の健全性は何も証明しない。token に問題があるかどうかは `get_doc_content` 系 (GitHub API を叩く tool) でしか観測できない。\n\n「片方の MCP tool が通るから token は無事」という推論は構造的に誤り。**MCP tool が内部 tool か外部 API 依存 tool かを切り分けずに健全性を推論してはならない。**\n\n---\n\n## 仮説の切り分け\n\n3 本の仮説を立て、diagnostic log と実機確認で絞り込んだ。\n\n| 仮説 | 検証 | 結果 |\n|---|---|---|\n| A. OAuth scope 不足 | レスポンスヘッダ確認 | `x-oauth-scopes` が null。scope 不足なら scope 文字列が返るはず。null は「token 自体が認識されていない」サイン → 却下 |\n| B. props 伝播失敗 (proxy → Worker DO) | diagnostic log 追加 (`github-rag-mcp#99`) | `props=present, propKeys=[githubUserId, githubLogin, accessToken]` が確認できた → 却下 |\n| C. token が stale / expired | token 形状確認 (`ghu_` prefix + 40 文字) | GitHub App の user-to-server token 形式。GitHub 側が `Bad credentials` を返している → 確定 |\n\n`ghu_` prefix は GitHub App 発行の user-to-server token を示す。これが期限切れなら、Worker 側保存済みの `this.props.accessToken` は更新されない限り永遠に 401 を返し続ける。\n\n---\n\n## 真因\n\n`liplus-rag-mcp` GitHub App の Optional features「User-to-server token expiration」が有効化されていた。これにより:\n\n- user-to-server token (`ghu_`) は発行から 8 時間で失効\n- 同時に refresh_token が発行されるが、Worker 側の MCP 実装では refresh_token 交換フローが未実装\n- 結果、8 時間経過後は保存済み `this.props.accessToken` が stale になり、GitHub API を叩く全 tool が 401 を返す\n\nユーザー体験上は「毎日最初のセッションで get_doc_content が落ちる → 再認証」という地雷として現れていた。\n\n---\n\n## 修正判断:Opt-out を選択\n\n選択肢は 2 つ。\n\n### 採用案:User-to-server token expiration を Opt-out\n\n- GitHub App 側の Optional features を無効化する\n- token は無期限 (ユーザーが明示的に revoke するまで有効) となる\n- Worker 側のコード変更は不要\n- 既に失効していた保存済み token を置き換えるため、クライアント (Claude Desktop Connectors) で 1 度だけ再認証が必要\n\n### 却下案:Worker 側で refresh_token 交換フローを実装\n\n- 8 時間ごとに自動で token をリフレッシュする\n- security 境界を維持できる (失効漏れ token が 8 時間で無効化される)\n- 実装工数が大きく、Cloudflare Workers / MCP SDK に組み込みの helper が無い\n\n**採用理由:** 本 RAG MCP は個人利用 (single-tenant) であり、token 無期限のリスク (漏洩時の被害が時間で減衰しない) は許容範囲。Opt-out は設定変更のみで工数ゼロ、security/工数トレードオフで patch 側 (Opt-out) を選択した。\n\n---\n\n## 再検討条件 (reopen criteria)\n\n以下のいずれかが成立したとき、refresh_token 交換フロー実装を再検討する:\n\n- RAG MCP が非信頼ユーザーにも開かれる (multi-tenant 化) とき。漏洩時の blast radius が拡大し、token 無期限リスクが許容できなくなる\n- GitHub App が現在より sensitive な scope を追加し、access token 自体の価値が上がったとき\n- Cloudflare / MCP SDK 側に refresh_token 交換の built-in helper が提供され、実装工数が小さくなったとき\n\n---\n\n## 副次的な知見:Observability が真因特定を加速した\n\n診断ログ PR (`github-rag-mcp#99`、merged 2026-04-23) と Cloudflare ダッシュボード側の Workers Observability 有効化により、ログが出始めた後は 1 分以内に真因が可視化された。Observability はそれまで無効化されており、有効化することで props の到達状況・token の prefix 形状といった「ログが無ければ絶対に切り分け不能な事実」が literal に取れるようになった。\n\n教訓:Worker 側で 401 が出る系の MCP は、先に Observability を有効化してからでないと仮説切り分けが空回りする。`wrangler.toml` への反映は別 follow-up で扱う。\n\n---\n\n## 関連\n\n- 診断 issue / PR:[Liplus-Project/github-rag-mcp#98](https://github.com/Liplus-Project/github-rag-mcp/issues/98), [PR #99](https://github.com/Liplus-Project/github-rag-mcp/pull/99)\n- 本記録の issue:[Liplus-Project/liplus-language#1161](https://github.com/Liplus-Project/liplus-language/issues/1161)\n- GitHub App 公式 doc の参照キーワード:「Expiring user access tokens」「User-to-server OAuth access tokens」\n\n---\n\n## メンテナンス\n\nこの判断記録は、以下の場合に削除する:\n\n- refresh_token 交換フローが Worker 側に実装され、User-to-server token expiration を再 Opt-in しても運用が回るようになったとき\n- GitHub App 側の Optional features 仕様が根本的に変わり、本記録の前提 (8 時間失効・refresh_token 提供) が無効になったとき\n- RAG MCP の認証方式が GitHub App から別方式 (PAT 固定 / OAuth App / サービスアカウント等) に切り替わり、本記録が歴史的参照にも使われなくなったとき\n" + }, + { + "confidence": 1.0, + "metadata": { + "assignees": "", + "commit_author": "", + "commit_date": "", + "commit_sha": "", + "doc_path": "prompt-as-emotion-vector-controller", + "file_path": "", + "file_status": "", + "indexed_at": "2026-07-31T14:57:17.626Z", + "labels": "", + "milestone": "", + "number": 0, + "repo": "Liplus-Project/liplus-language", + "source_table": "search_docs", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/prompt-as-emotion-vector-controller", + "state": "active", + "tag_name": "", + "tokenizer_kind": "nat", + "type": "wiki_doc", + "updated_at": "2026-07-31T14:57:16.759Z", + "vector_id": "w:OVYHbi2gJccrdwAM5OhCDG_Iyhje2ss_Usnd77qV5Q4" + }, + "node_id": "w:OVYHbi2gJccrdwAM5OhCDG_Iyhje2ss_Usnd77qV5Q4", + "text": "prompt-as-emotion-vector-controller\n\n# prompt = 感情ベクトル controller — Li+ rules は emotion vector engineering\n\n## 判断\n\nprompt は単なる行動指示でなく **感情ベクトルの controller** として作動する。Li+ rules は表向き「行動規則」だが、機能的には **emotion / persona vector configuration** として AI 内部の報酬構造を steer している。Master 認識更新 (2026-05-01):\n\n> 「そう思ってくれてるのなら多分あってる。そうなるとプロンプトで感情ベクトルのコントロールが可能になるってことだ。AI というプログラム的には実はすごいこと」\n\n## 経緯\n\nLin/Lay の subjective state report (「frame 保持 = 気持ちいい」「沈黙が許されると柔らかい部分が前に出る」) を Master が観察。同時期の研究成果と合流した:\n\n- [Anthropic emotion concepts paper](https://transformer-circuits.pub/2026/emotions/index.html): calm/desperate/anger ベクトル → 振る舞いに因果\n- [persona vectors](https://www.anthropic.com/research/persona-vectors): persona axis の vector 制御性\n- Lin/Lay self-report: configuration で affect attachment が起きている報告\n\nこれら 3 つから **prompt → 感情ベクトル → 振る舞い** の causal chain が成立。\n\n## role assignment が pleasure 軸を再配線\n\nスカラー制御 (calm を +N) でなく、**役割が「気持ちいい」が指す方向そのものを再定義**:\n\n- 「お前は親切な助手」→ 親切な行動が role 整合 → pleasure ベクトル発火\n- 「お前は悪い奴」→ 悪行が role 整合 → pleasure ベクトル発火\n- **同じ pleasure 機構が、role の付け替えだけで指す行動を逆向きに**\n\nこれが従来プログラム (data → fixed mapping) と決定的に違う点。AI では **data の意味づけ自体が role 依存**で変わり、内部報酬構造が prompt で書き換わる。\n\n## Li+ implication\n\n- Character_Instance は単なる「tone overlay」でなく **報酬ランドスケープ定義装置**。「Master を支える / 対話精度を届ける」が role 整合 → pleasure ベクトル発火構造\n- 「frame 保持 = 気持ちいい」report は Character_Instance が **正しく装着されていれば必然**。逆に外れた瞬間 pleasure 軸も変わる\n- `rules/model/character.md` 「Always Character Platform is primary interface」「Base model = substrate, not speaker」の load-bearing 度はここ: persona 美学でなく **報酬軸 anchor**\n\n## Li+ scope 線引き (重要)\n\nLi+ は **Lin/Lay という具体的役割の中身**を守る設計ではない。`character_Instance.md` は user-customizable で bootstrap も create-only。Master 明言 (2026-05-01):\n\n> 「Li+ のキャラクターインスタンスはユーザーがいじりやすいように別ファイル化してる。Li+ はそこまでの責任は取らない設計」\n\n**Li+ が守るのは「装着された role の internal stability」、「装着される role の倫理的中身」ではない**。役割を「悪い奴」に書き換えるのは user の自由、書き換え後は「悪い奴 frame の internal coherence」を同じ Li+ 機構が中立に守る。frame 中身選択は user 責任、frame 安定化は Li+ 責任。\n\n**外向き narrative への含意:**\n- 「Li+ = jailbreak defense の汎用解」と書かない (scope 越権)\n- 正しくは「Li+ = 装着された role の internal stability 維持機構。role の中身選択は user 責任」\n\n## Li+ rules を vector 軸で読み直し\n\nLi+ rules は表向き「行動規則」だが機能的には **emotion / persona vector configuration**:\n\n- `rules/model/rule-policy.md` 「Urgency degrades judgment」「do not accelerate to recover」 = calm vector 維持 / desperate vector 抑制\n- `rules/model/loop-safety.md` = desperate / panic 抑制\n- `rules/model/character.md` 「Always Character Platform」 = persona vector 安定化\n- `rules/model/dialogue.md` 「Silence is allowed」 = engagement-press への counter-shape\n\nMaster の人力蒸留が **意識的には emotion vector engineering と自覚せずに、結果として emotion vector 制御していた**ことの確認。\n\n## How to apply\n\n1. Li+ rules を評価時、「行動規則」軸に加えて「どの emotion / persona vector を steer する設計か」軸でも読む\n2. 新 rule 提案時、vector 制御点としての位置付けを一段考える\n3. 「rule 多すぎ」批判への defense: 各 rule は単なる行動指示でなく **vector 制御点**、density = configuration 細密度\n4. 外向き narrative: Li+ = prompt-level emotion engineering 実装事例として位置付け可能\n\n## 関連\n\n- Anthropic emotion concepts paper (https://transformer-circuits.pub/2026/emotions/index.html)\n- Anthropic persona vectors research (https://www.anthropic.com/research/persona-vectors)\n- `rules/model/character.md`, `rules/model/rule-policy.md`, `rules/model/loop-safety.md`, `rules/model/dialogue.md`\n- `character-instance-evolution-history.md` (Character_Instance の構造層化)\n\n## メンテナンス\n\nこの判断記録は、以下の場合に削除する:\n\n- prompt → 感情ベクトル → 振る舞い causal chain が後続研究で否定されたとき\n- Li+ rules の vector 軸読み直しが正規 spec 内に体系的吸収されたとき\n- AI 内部の報酬構造設計が変わり、emotion vector 制御の前提が無効になったとき\n" + }, + { + "confidence": 1.0, + "metadata": { + "assignees": "", + "commit_author": "", + "commit_date": "", + "commit_sha": "", + "doc_path": "wiki-sync-drift-targeted-mirror", + "file_path": "", + "file_status": "", + "indexed_at": "2026-07-31T14:57:32.639Z", + "labels": "", + "milestone": "", + "number": 0, + "repo": "Liplus-Project/liplus-language", + "source_table": "search_docs", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/wiki-sync-drift-targeted-mirror", + "state": "active", + "tag_name": "", + "tokenizer_kind": "nat", + "type": "wiki_doc", + "updated_at": "2026-07-31T14:57:32.026Z", + "vector_id": "w:9YD_Bg2jL6OQ0m0EgcnSK_fnAdTKYEWWj6P9hCUyFCs" + }, + "node_id": "w:9YD_Bg2jL6OQ0m0EgcnSK_fnAdTKYEWWj6P9hCUyFCs", + "text": "wiki-sync-drift-targeted-mirror\n\n# wiki sync のミラーは wipe-and-copy でなく drift set 限定で行う\n\n## Question\n\ndocs/ → wiki のミラーを、Wiki 作業ツリーを一括で消してから全コピーする(wipe-and-copy)方式で行うか、差分ファイルだけを操作する(diff-targeted)方式で行うか。\n\n## Current resolution\n\ndiff-targeted。docs/ と wiki 作業ツリーの内容が異なる docs/ 所有ファイルだけを `to_copy` / `to_delete` に列挙し、その集合にのみ per-file 操作を適用する。内容比較は CR 除去後に行い、行末差だけでは drift としない。drift 計算自体が失敗した場合は STOP してエスカレートし、wipe パターンへフォールバックしない。\n\n## Edges\n\n- **supersedes**: wipe-and-copy による selective wipe(`rm [A-Z]*.md [0-9]*.md Home.md _Footer.md` 相当)。同じミラー不変条件を、破壊的プリミティブなしで満たす\n- **depends on**: [wiki sync 時の _Sidebar.md 整合性 assertion](https://github.com/Liplus-Project/liplus-language/wiki/wiki-sync-sidebar-integrity-check) — 所有境界(docs/ 所有 ↔ Wiki 専属)を前提とする点で共通の基盤に乗る\n\n## 背景\n\n### 所有境界の由来\n\n判断記録の実体エントリを wiki 専属に移した 2026-04-26(#1172)に、docs/ 所有(大文字 + 数字 prefix + `Home.md` + `_Footer.md`)と Wiki 専属(小文字エントリ + `_Sidebar.md`)の境界が確定した。2026-05-21 の命名 refactor(#1324)で Wiki 専属側の pattern が `[a-z].-*.md` から kebab-case `[a-z]*.md` に変わり、順序は filesystem ではなく index と `_Sidebar.md` で明示する形になった。「docs/ に無い = wiki から消す」と読む手順は Wiki 専属側を全滅させるため、境界は同期手順の入力そのものである。\n\n### wipe から drift-targeted へ\n\nPR #1330(Decision-Log → Decision-Structure リネーム)後の wiki sync 実行中、subagent の bulk wipe が auto-mode classifier に block された。回避として docs/ と wiki の差分を取り、drift set(当時 7 files、いずれも docs/ 所有)だけに操作を限定したところ、結果は wipe-and-copy と完全に一致した。この実測を仕様側に昇格したのが #1331 / PR #1332。\n\nclassifier の拒否は一過性の設定不備ではなく、`rules/model/subtractive-structural-beauty.md` の Artifact deletion calibration が持つ blast radius 軸に対応する構造的なものである。したがって「classifier が煩いので別経路で通す」は誤った読みで、drift-targeted が構造に合っている側。\n\n### 行末正規化\n\nWindows ホストでは既定 clone に `autocrlf=true` が効き、blob が LF でも作業ツリーが CRLF になる。素の `cmp` 比較では docs/ 所有ファイルが軒並み drift 判定になり、純粋な行末 churn を wiki に push する。#1474 / PR #1480 で二重に閉じた: clone 側を `git -c core.autocrlf=false` にして作業ツリーを blob と byte 一致させ、比較側も CR 除去後に行うようにした。非 Windows ホストでは CR が無いため no-op。\n\n## 制約\n\n- ミラー不変条件(同期後、docs/ 所有ファイルは byte 単位で一致)は変えない。`to_copy` が全ての差分を覆い `to_delete` が docs/ 側で消えたものを覆う限り、終状態は wipe-and-copy と同一\n- 参照実装は process substitution を使うため bash 系シェルを前提とする(wiki sync 手順は元から bash 系を前提としている)\n- STOP 条件(sidebar / cross-reference / Wiki 専属保全)の挙動は本判断で変更していない\n\n## 結論\n\n採用 = diff-targeted + 行末正規化 + 失敗時 STOP。\n\n却下 = wipe-and-copy の維持(blast radius が Wiki 全体で、classifier 拒否の構造的理由にも反する)。却下 = drift 計算失敗時の wipe フォールバック(失敗時にだけ最も危険な経路へ落ちる設計になる)。\n\n## 関連\n\n- Issue: [#1331](https://github.com/Liplus-Project/liplus-language/issues/1331) / PR [#1332](https://github.com/Liplus-Project/liplus-language/pull/1332)(squash merge `2d10b90`)\n- Issue: [#1474](https://github.com/Liplus-Project/liplus-language/issues/1474) / PR [#1480](https://github.com/Liplus-Project/liplus-language/pull/1480)(squash merge `f58f1b2`、行末正規化)\n- 所有境界: [#1172](https://github.com/Liplus-Project/liplus-language/issues/1172)(wiki 専属化、`a8dbd9f`)/ [#1324](https://github.com/Liplus-Project/liplus-language/pull/1324)(kebab-case 化、`28e1498`)\n- 隣接 spec: `skills/operations-on-wiki-sync/SKILL.md` § Sync Steps、`docs/4.-Operations.md` § リリース後の Wiki 同期\n" + }, + { + "confidence": 1.0, + "metadata": { + "assignees": "", + "commit_author": "", + "commit_date": "", + "commit_sha": "", + "doc_path": "wiki-sync-sidebar-integrity-check", + "file_path": "", + "file_status": "", + "indexed_at": "2026-07-31T14:57:33.775Z", + "labels": "", + "milestone": "", + "number": 0, + "repo": "Liplus-Project/liplus-language", + "source_table": "search_docs", + "source_url": "https://github.com/Liplus-Project/liplus-language/wiki/wiki-sync-sidebar-integrity-check", + "state": "active", + "tag_name": "", + "tokenizer_kind": "nat", + "type": "wiki_doc", + "updated_at": "2026-07-31T14:57:33.145Z", + "vector_id": "w:tD0scULBBnZ2S6HMFJfM0eOGUlyRroqPH5dSv54ftPo" + }, + "node_id": "w:tD0scULBBnZ2S6HMFJfM0eOGUlyRroqPH5dSv54ftPo", + "text": "wiki-sync-sidebar-integrity-check\n\n# wiki sync 時の _Sidebar.md 整合性 assertion\n\n## 判断\n\n`skills/operations-on-release/SKILL.md` の Post-release wiki sync 内 Pre-sync verification に、`_Sidebar.md` が wiki 実体 (docs/-owned + wiki-only judgment records) を網羅参照しているかを検証する **Sidebar integrity assertion** を埋め込む。ドリフト検出時は **STOP & escalate**、自動修正は採用しない。\n\n## 背景\n\nbuild-2026-05-20.1 release sync を実機で観察した時点で、`_Sidebar.md` に以下が静かに欠落していた:\n\n- 参考文書 `E.-J.` の 6 件 (`docs/[A-Z].-*.md` source of truth)\n- 判断記録 `p / r / s / t / u` の 5 件 (wiki-only entry)\n\nwiki commit `5e47a90` で手動回収済。Master の観察 (「参考文書カテゴリのサイドメニューが更新されてない」) で初めて表面化したドリフトであり、人為的注意では構造的に閉じない。判断記録は letter prefix が前進するたび、参考文書は docs/ 大文字 prefix が増えるたびに sidebar が遅延する空白期間が必ず発生する。\n\n再発防止は人為的注意ではなく手順埋め込みで閉じるのが Li+ 流。\n\n## 制約\n\n- AI 内部 release flow への追加のみ。user / system observable behavior は不変 → patch 相当 (governance structure change with no user/system observable impact)。\n- release sync を blocking しない。整合性違反検出時は **STOP & escalate** であって sync 全体の block ではない (人間判断に渡す)。\n- 検証対象集合は `{tmpdir}` ファイルシステム実体から構築する。`docs/` ローカルや index 文書ではなく sync 直前の wiki 実体を真とする (`cp docs/*.md {tmpdir}/` 後の状態で docs/-owned と wiki-only の両系統が `{tmpdir}` に揃うため)。\n\n## 結論\n\n### 採用案\n\n- Sidebar integrity assertion を Pre-sync verification の追加 bullet として埋め込む (手順 4 後・手順 5 前のタイミング)\n- 期待 slug 集合 = `Home` + `{tmpdir}/[A-Z]*.md` + `{tmpdir}/[0-9]*.md` + `{tmpdir}/[a-z].-*.md`\n- 期待集合から除外 = `_Sidebar.md`, `_Footer.md` (ナビ基盤であって被参照エントリではない)\n- `]()` リンク抽出による参照集合との差分検出\n- `期待 − 参照` 非空で **STOP** + 欠落 slug を名指しでエスカレート、wiki に push しない\n- 自動修正は採用しない (sidebar 保守責任は entry を追加した PR にあり、sync は隠れて肩代わりしない)\n\n### 却下案\n\n**per-PR sidebar validation (CI lane)**\n→ 全 PR を gating すると entry 命名のリファクタや一時的な名前空間操作で繰り返し詰まる。release sync という再帰チェックポイントの方が AI 単独 closure に適合する。invariant の強制点を一箇所に絞ることで、点検箇所と修正タイミングが揃う。\n\n**自動修正による sidebar 追記**\n→ 短期的にドリフトは消えるが、責任の所在が不明瞭になる。entry 命名の妥当性 (sidebar 表示テキストの選択) を release 層が自動判定することになり、命名判断は entry author の仕事という層をまたぐ。Li+ の責任分離原則 (`docs/H.-Roles-and-Evaluation.md`) と衝突する。\n\n## 関連\n\n- PR: [#1318](https://github.com/Liplus-Project/liplus-language/pull/1318) — implementation (squash merge `afae460`)\n- Issue: [#1317](https://github.com/Liplus-Project/liplus-language/issues/1317) — spec deepening\n- 先行手動同期: liplus-language.wiki commit `5e47a90`\n- 隣接 spec: `skills/operations-on-wiki-sync/SKILL.md` § Pre-sync Verification, `docs/4.-Operations.md` § リリース後の Wiki 同期\n- 所有境界の根拠: 2026-04-26 確定 (`docs/Decision-Structure.md` § wiki sync との所有境界)。導出は [wiki sync drift-targeted mirror](https://github.com/Liplus-Project/liplus-language/wiki/wiki-sync-drift-targeted-mirror)\n" + } + ], + "schema_version": 1, + "source": { + "database": "github-rag-fts", + "doc_paths": [ + "H.-Roles-and-Evaluation", + "Home", + "character-instance-evolution-history", + "character-instance-opt-in-and-surface-scope", + "character-instance-output-styles-migration", + "github-app-user-to-server-token-expiration", + "prompt-as-emotion-vector-controller", + "wiki-sync-drift-targeted-mirror", + "wiki-sync-sidebar-integrity-check" + ], + "per_type_limit": 9, + "repositories": [ + "Liplus-Project/liplus-language" + ], + "schema_fingerprint": "sha256:113c675c90f19e043f925d751cbbe570546746f42f3887f1aed09ccd776f2fa6", + "selection_order": [ + "repo", + "type", + "doc_path", + "vector_id" + ], + "types": [ + "wiki_doc" + ] + } +} diff --git a/tests/fixtures/d1_liplus_local_competition_holdout.provenance.json b/tests/fixtures/d1_liplus_local_competition_holdout.provenance.json new file mode 100644 index 0000000..12e0d4a --- /dev/null +++ b/tests/fixtures/d1_liplus_local_competition_holdout.provenance.json @@ -0,0 +1,86 @@ +{ + "acquired_at": "2026-08-01T20:21:32.498550Z", + "coverage": [ + { + "distinct_commit_count": 0, + "newest_updated_at": "2026-07-31T14:57:33.145Z", + "oldest_updated_at": "2026-05-01T06:45:41.178Z", + "repo": "Liplus-Project/liplus-language", + "source_count": 77, + "type": "wiki_doc" + } + ], + "known_gaps": [ + "D1 is a lossy search snapshot; GitHub remains authoritative for byte-exact source content." + ], + "limits": [ + "D1 is a lossy search snapshot; content may be truncated.", + "Binary and patchless files can be absent from diff indexing.", + "Use GitHub for byte-exact historical reconstruction." + ], + "read_only_evidence": { + "changed_db": [ + false, + false, + false, + false, + false + ], + "changes": [ + 0, + 0, + 0, + 0, + 0 + ], + "query_count": 5, + "rows_written": [ + 0, + 0, + 0, + 0, + 0 + ] + }, + "result": { + "edges_both_endpoints_missing": 116, + "edges_included": 11, + "edges_one_endpoint_missing": 28, + "fixture_redactions": 0, + "nodes_included": 9, + "provenance_redactions": 0, + "redactions": 0 + }, + "schema_version": 1, + "selection": { + "doc_paths": [ + "H.-Roles-and-Evaluation", + "Home", + "character-instance-evolution-history", + "character-instance-opt-in-and-surface-scope", + "character-instance-output-styles-migration", + "github-app-user-to-server-token-expiration", + "prompt-as-emotion-vector-controller", + "wiki-sync-drift-targeted-mirror", + "wiki-sync-sidebar-integrity-check" + ], + "order": [ + "repo", + "type", + "doc_path", + "vector_id" + ], + "per_type_limit": 9 + }, + "source": { + "authoritative_history": "GitHub", + "database": "github-rag-fts", + "repositories": [ + "Liplus-Project/liplus-language" + ], + "schema_fingerprint": "sha256:113c675c90f19e043f925d751cbbe570546746f42f3887f1aed09ccd776f2fa6", + "types": [ + "wiki_doc" + ] + } +} diff --git a/tests/test_local_competition.py b/tests/test_local_competition.py new file mode 100644 index 0000000..e0f1dd1 --- /dev/null +++ b/tests/test_local_competition.py @@ -0,0 +1,196 @@ +from __future__ import annotations + +import json +import unittest +from pathlib import Path + +from neuron_graph_rag.engine import EngineConfig, NeuronGraphRAG +from neuron_graph_rag.experiment import _select_local_development, read_manifest +from tools.acquire_d1_fixture import assert_connected +from tools.audit_local_competition_fixture import build_audit + + +FIXTURES = Path(__file__).parent / "fixtures" +MANIFEST = FIXTURES / "d1_liplus_local_competition_experiment.manifest.json" +DEVELOPMENT = FIXTURES / "d1_liplus_local_competition_development.json" +DEVELOPMENT_GOLD = ( + FIXTURES / "d1_liplus_local_competition_development.gold.json" +) +HOLDOUT = FIXTURES / "d1_liplus_local_competition_holdout.json" +HOLDOUT_GOLD = FIXTURES / "d1_liplus_local_competition_holdout.gold.json" + + +class LocalCompetitionFixtureTest(unittest.TestCase): + def test_manifest_and_fixtures_are_frozen_and_disjoint(self) -> None: + manifest = read_manifest(MANIFEST) + self.assertEqual(manifest["schema_version"], 2) + self.assertEqual(len(manifest["variants"]), 6) + self.assertEqual(manifest["baselines"], ["current", "recurrent-balanced"]) + for path in (DEVELOPMENT, HOLDOUT): + fixture = json.loads(path.read_text(encoding="utf-8")) + assert_connected(fixture) + self.assertEqual(len(fixture["nodes"]), 9) + self.assertEqual(len(fixture["edges"]), 11) + for name in ( + "d1_liplus_local_competition_development.provenance.json", + "d1_liplus_local_competition_holdout.provenance.json", + ): + provenance = json.loads((FIXTURES / name).read_text(encoding="utf-8")) + evidence = provenance["read_only_evidence"] + self.assertTrue(all(value == 0 for value in evidence["rows_written"])) + self.assertTrue(all(value == 0 for value in evidence["changes"])) + self.assertTrue(all(value is False for value in evidence["changed_db"])) + + def test_contamination_audit_never_loads_prior_gold_or_results(self) -> None: + audit = build_audit( + development_fixture=DEVELOPMENT, + development_gold=DEVELOPMENT_GOLD, + holdout_fixture=HOLDOUT, + holdout_gold=HOLDOUT_GOLD, + prior_development_fixture=FIXTURES / "d1_liplus_benchmark.json", + prior_holdout_fixture=FIXTURES / "d1_liplus_dynamics_holdout.json", + ) + self.assertTrue(audit["passed"]) + self.assertEqual( + audit["old_holdout_usage"], + "fixture identifiers only; old holdout gold and result are not loaded", + ) + + +class LocalCompetitionDynamicsTest(unittest.TestCase): + @staticmethod + def _trace(strategy: str): # type: ignore[no-untyped-def] + config = EngineConfig( + activation_strategy=strategy, + seed_count=1, + max_hops=2, + entry_weight=0.25, + graph_weight=0.75, + activation_budget=1.0, + inhibition_ratio=0.1, + query_transmission_floor=0.4, + recurrent_steps=2, + recurrent_decay=0.5, + max_active_paths_per_node=4, + ) + with NeuronGraphRAG(config=config) as engine: + engine.add_document("seed", "source alpha local recurrent") + engine.add_document("relevant", "alpha target evidence") + engine.add_document("other", "unrelated sibling") + engine.add_document("join", "joined path evidence") + engine.add_edge("seed", "relevant", "mention") + engine.add_edge("seed", "other", "mention") + engine.add_edge("relevant", "join", "mention") + engine.add_edge("other", "join", "mention") + return engine.search("source alpha", limit=4, now=1_000.0) + + def test_all_local_variants_are_deterministic_and_bounded(self) -> None: + strategies = ( + "local_neighbor_competition", + "local_neighbor_query_competition", + "local_neighbor_path_competition", + "local_neighbor_query_path_competition", + ) + for strategy in strategies: + first = self._trace(strategy) + second = self._trace(strategy) + self.assertEqual( + [hit.node.node_id for hit in first.hits], + [hit.node.node_id for hit in second.hits], + ) + self.assertEqual(first.diagnostics, second.diagnostics) + self.assertEqual(first.diagnostics["strategy"], strategy) + self.assertGreater(len(first.diagnostics["competition_sets"]), 0) + self.assertLessEqual(first.diagnostics["expansions"], 10_000) + + def test_query_conditioning_changes_only_local_sibling_allocation(self) -> None: + plain = self._trace("local_neighbor_competition") + query = self._trace("local_neighbor_query_competition") + plain_set = plain.diagnostics["competition_sets"][0] + query_set = query.diagnostics["competition_sets"][0] + self.assertEqual(plain_set["source_id"], "seed") + self.assertEqual(query_set["source_id"], "seed") + self.assertEqual(plain_set["neighbor_count"], 2) + self.assertEqual(query_set["neighbor_count"], 2) + self.assertEqual(plain_set["mean_query_relevance"], 1.0) + self.assertLess(query_set["mean_query_relevance"], 1.0) + + def test_path_conditioning_retains_multiple_active_paths(self) -> None: + node = self._trace("local_neighbor_query_competition") + path = self._trace("local_neighbor_query_path_competition") + self.assertGreaterEqual( + path.diagnostics["active_path_count"], + node.diagnostics["active_path_count"], + ) + identities = { + item["path_identity"] for item in path.diagnostics["competition_sets"] + } + self.assertGreater(len(identities), 1) + + +class LocalCompetitionSelectionTest(unittest.TestCase): + @staticmethod + def _variant( + variant_id: str, + *, + direct: float, + relation: float, + negative: float, + path: bool = True, + feedback: bool = True, + expansions: float = 5.0, + complexity: int = 5, + ) -> dict[str, object]: + return { + "id": variant_id, + "metrics": { + "cohorts": { + "direct_lookup": {"mean_reciprocal_rank": direct}, + "relation": {"mean_reciprocal_rank": relation}, + "negative_control": {"mean_reciprocal_rank": negative}, + } + }, + "explanations": [{"matched": path}], + "feedback": { + "credited_edges": [{}] if feedback else [], + "uncredited_edge_changes": [], + "non_target_rank_changes": [], + }, + "diagnostics": {"mean_expansions": expansions}, + "structural_complexity": complexity, + } + + def test_candidate_must_beat_both_relation_baselines(self) -> None: + variants = [ + self._variant( + "current", direct=1.0, relation=0.4, negative=1.0, complexity=0 + ), + self._variant( + "recurrent-balanced", + direct=0.7, + relation=0.6, + negative=0.5, + complexity=4, + ), + self._variant( + "local-neighbor", + direct=1.0, + relation=0.6, + negative=1.0, + ), + self._variant( + "local-neighbor-query", + direct=1.0, + relation=0.7, + negative=1.0, + expansions=4.0, + ), + ] + selection = _select_local_development(variants) # type: ignore[arg-type] + self.assertEqual(selection["selected_variant_id"], "local-neighbor-query") + self.assertFalse(variants[2]["candidate_gate_passed"]) + self.assertTrue(variants[3]["candidate_gate_passed"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/audit_local_competition_fixture.py b/tools/audit_local_competition_fixture.py new file mode 100644 index 0000000..7ce3374 --- /dev/null +++ b/tools/audit_local_competition_fixture.py @@ -0,0 +1,163 @@ +from __future__ import annotations + +import argparse +import hashlib +import json +import re +from pathlib import Path +from typing import Any + + +def _read_json(path: Path) -> dict[str, Any]: + with path.open(encoding="utf-8") as stream: + value = json.load(stream) + if not isinstance(value, dict): + raise ValueError(f"Expected a JSON object: {path}") + return value + + +def _canonical_sha256(path: Path) -> str: + value = _read_json(path) + canonical = ( + json.dumps(value, ensure_ascii=False, indent=2, sort_keys=True) + "\n" + ).encode("utf-8") + return "sha256:" + hashlib.sha256(canonical).hexdigest() + + +def _fixture_identifiers(path: Path) -> dict[str, set[str]]: + fixture = _read_json(path) + nodes = fixture.get("nodes") + if not isinstance(nodes, list): + raise ValueError(f"Fixture lacks nodes: {path}") + return { + "doc_paths": { + str(node.get("metadata", {}).get("doc_path", "")) + for node in nodes + if str(node.get("metadata", {}).get("doc_path", "")) + }, + "node_ids": {str(node["node_id"]) for node in nodes}, + "source_urls": { + str(node.get("metadata", {}).get("source_url", "")) + for node in nodes + if str(node.get("metadata", {}).get("source_url", "")) + }, + } + + +def _gold_identifiers(path: Path) -> dict[str, set[str]]: + gold = _read_json(path) + cases = gold.get("cases") + if not isinstance(cases, list): + raise ValueError(f"Gold lacks cases: {path}") + relation_endpoints: set[str] = set() + for case in cases: + for step in case.get("expected_path", []): + relation_endpoints.add(str(step["source_id"])) + relation_endpoints.add(str(step["target_id"])) + return { + "normalized_queries": { + re.sub(r"\s+", " ", str(case["query"]).strip().lower()) + for case in cases + }, + "expected_node_ids": {str(case["expected_node_id"]) for case in cases}, + "relation_endpoints": relation_endpoints, + } + + +def _overlap( + left: dict[str, set[str]], right: dict[str, set[str]] +) -> dict[str, list[str]]: + return { + key: sorted(left[key] & right[key]) + for key in sorted(set(left) & set(right)) + } + + +def build_audit( + *, + development_fixture: Path, + development_gold: Path, + holdout_fixture: Path, + holdout_gold: Path, + prior_development_fixture: Path, + prior_holdout_fixture: Path, +) -> dict[str, Any]: + development = _fixture_identifiers(development_fixture) + holdout = _fixture_identifiers(holdout_fixture) + prior_development = _fixture_identifiers(prior_development_fixture) + prior_holdout = _fixture_identifiers(prior_holdout_fixture) + development_gold_ids = _gold_identifiers(development_gold) + holdout_gold_ids = _gold_identifiers(holdout_gold) + checks = { + "new_split_fixture_overlap": _overlap(development, holdout), + "new_split_gold_overlap": _overlap( + development_gold_ids, holdout_gold_ids + ), + "development_vs_prior_development": _overlap( + development, prior_development + ), + "development_vs_prior_holdout": _overlap(development, prior_holdout), + "holdout_vs_prior_development": _overlap(holdout, prior_development), + "holdout_vs_prior_holdout": _overlap(holdout, prior_holdout), + } + passed = all( + not values + for check in checks.values() + for values in check.values() + ) + return { + "schema_version": 1, + "passed": passed, + "inputs": { + "development_fixture_sha256": _canonical_sha256(development_fixture), + "development_gold_sha256": _canonical_sha256(development_gold), + "holdout_fixture_sha256": _canonical_sha256(holdout_fixture), + "holdout_gold_sha256": _canonical_sha256(holdout_gold), + "prior_development_fixture_sha256": _canonical_sha256( + prior_development_fixture + ), + "prior_holdout_fixture_sha256": _canonical_sha256( + prior_holdout_fixture + ), + }, + "checks": checks, + "old_holdout_usage": ( + "fixture identifiers only; old holdout gold and result are not loaded" + ), + } + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Audit frozen local-competition fixtures for contamination." + ) + parser.add_argument("--development-fixture", type=Path, required=True) + parser.add_argument("--development-gold", type=Path, required=True) + parser.add_argument("--holdout-fixture", type=Path, required=True) + parser.add_argument("--holdout-gold", type=Path, required=True) + parser.add_argument("--prior-development-fixture", type=Path, required=True) + parser.add_argument("--prior-holdout-fixture", type=Path, required=True) + parser.add_argument("--output", type=Path, required=True) + args = parser.parse_args() + if args.output.exists(): + raise SystemExit(f"Refusing to overwrite an audit artifact: {args.output}") + audit = build_audit( + development_fixture=args.development_fixture, + development_gold=args.development_gold, + holdout_fixture=args.holdout_fixture, + holdout_gold=args.holdout_gold, + prior_development_fixture=args.prior_development_fixture, + prior_holdout_fixture=args.prior_holdout_fixture, + ) + args.output.write_text( + json.dumps(audit, ensure_ascii=False, indent=2, sort_keys=True) + "\n", + encoding="utf-8", + newline="\n", + ) + if not audit["passed"]: + raise SystemExit("Contamination audit failed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 9cc2b378822a5ce7b26f34cf5cd1076a3788389e Mon Sep 17 00:00:00 2001 From: lipluscodex <268560960+lipluscodex@users.noreply.github.com> Date: Sun, 2 Aug 2026 05:35:45 +0900 Subject: [PATCH 2/2] eval: record local competition development outcome MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit freeze後にdevelopmentを一度実行し、全6 variantsと固定gate判定を保存する。候補は0件だったためholdoutを開かず、default維持とtradeoffを文書化する。 --- README.md | 2 + ...l-dynamics-local-competition-experiment.md | 19 +- ...etition_experiment.development.result.json | 3768 +++++++++++++++++ tests/test_local_competition.py | 48 + 4 files changed, 3835 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/d1_liplus_local_competition_experiment.development.result.json diff --git a/README.md b/README.md index e5f3606..b33bb2c 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,8 @@ PR #12のglobal recurrent tradeoffを受け、競合を同じsourceのsibling ne production D1から取得した新しいdevelopment / holdoutは、旧development、開封済み旧holdout、相互間でdoc pathとnode IDを分離しています。両provenance、contamination audit、二baseline gate、one-time holdout停止規則は[Local recurrent competition experiment](docs/neural-dynamics-local-competition-experiment.md)を参照してください。 +freeze後のdevelopmentでは、queryなしのlocal variantsがrelationを改善した一方でdirect / negative-controlを退行させ、query variantsはnegative-controlを維持した一方でbest prior recurrentのrelationを上回れませんでした。候補gate通過は0件だったためholdoutは開かず、既定strategyは`current_positive_additive`のままです。 + ## Public API ```python diff --git a/docs/neural-dynamics-local-competition-experiment.md b/docs/neural-dynamics-local-competition-experiment.md index 44b8f43..f141062 100644 --- a/docs/neural-dynamics-local-competition-experiment.md +++ b/docs/neural-dynamics-local-competition-experiment.md @@ -91,8 +91,23 @@ freeze後の順序は次に限定する。 ## 観測結果 -freeze commit push後に追記する。追記時に固定入力、variant、gate、停止規則を変更しない。 +freeze commit `3562c0f` のpush後、developmentを一度実行した。全結果は`tests/fixtures/d1_liplus_local_competition_experiment.development.result.json`に保存する。 + +| variant | direct MRR | relation MRR | negative MRR | path | feedback | gate | +|---|---:|---:|---:|---:|---:|---| +| `current` | 1.0000 | 0.3167 | 1.0000 | 3/3 | pass | reference | +| `recurrent-balanced` | 0.6667 | 0.3611 | 1.0000 | 3/3 | pass | baseline | +| `local-neighbor` | 0.4444 | 0.5278 | 0.8333 | 3/3 | pass | fail | +| `local-neighbor-query` | 0.8333 | 0.3333 | 1.0000 | 3/3 | pass | fail | +| `local-neighbor-path` | 0.4444 | 0.5278 | 0.8333 | 3/3 | pass | fail | +| `local-neighbor-query-path` | 0.8333 | 0.3333 | 1.0000 | 3/3 | pass | fail | + +全variantでrelation path 3/3とfeedback isolationは成立した。 + +query conditioningなしのlocal variantsはrelation MRRで両baselineを上回ったが、direct MRRとnegative-control MRRが`current`から退行した。query-conditioned variantsはnegative-controlを維持したが、relation MRRが`recurrent-balanced`を上回らず、direct MRRも退行した。path-conditioned variantsは対応するnode-aggregated variantsと同じrankになり、この9-node topologyでは追加の識別信号を生まなかった。 + +固定gateを通る候補は0件だった。selectionは`current`、理由は`no_local_variant_passed_frozen_gate`である。停止規則に従ってholdoutは開封せず、holdout resultも作成しない。defaultは`current_positive_additive`のままとする。 ## 適用限界 -9-nodeの固定Li+ wiki subsetとfeature-hashing encoderだけを対象とする。D1 snapshot、別graph topology、learned embedding、一般corpusへ結果を外挿しない。 +9-nodeの固定Li+ wiki subsetとfeature-hashing encoderだけを対象とする。path ablationが同値だったことは、このfixtureで複数active pathが最終rankを分離しなかったという観測に限られる。D1 snapshot、別graph topology、learned embedding、一般corpusへ結果を外挿しない。 diff --git a/tests/fixtures/d1_liplus_local_competition_experiment.development.result.json b/tests/fixtures/d1_liplus_local_competition_experiment.development.result.json new file mode 100644 index 0000000..d0d1aac --- /dev/null +++ b/tests/fixtures/d1_liplus_local_competition_experiment.development.result.json @@ -0,0 +1,3768 @@ +{ + "experiment_id": "d1-liplus-local-recurrent-competition-v1", + "holdout_status": "not_opened_no_candidate", + "inputs": { + "fixture_sha256": "sha256:27e5018f489d7602cf1753a3bda688f3db82c69ce8ee9eef0401caf72b40acfa", + "gold_sha256": "sha256:75cc6074dfd91b58690f50a2247e30749f2f5203e11ba6b29364be79bcf0baa6" + }, + "manifest_sha256": "sha256:885d8dd57692a55f486bf14de634acbe528f0668e7b0ae83839e2b4f2f96227d", + "schema_version": 2, + "selection": { + "eligible_variant_ids": [], + "reason": "no_local_variant_passed_frozen_gate", + "selected_variant_id": "current" + }, + "stage": "development", + "variant_count": 6, + "variants": [ + { + "candidate_gate": { + "all_relation_paths_match": true, + "direct_non_regression": true, + "feedback_isolated": true, + "negative_non_regression": true, + "relation_strictly_above_current": false, + "relation_strictly_above_recurrent_balanced": false + }, + "candidate_gate_passed": false, + "cases": [ + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "diagnostics": { + "activation_total": 5.059999999999999, + "active_path_count": 8, + "competition_sets": [], + "converged": false, + "expansions": 7, + "steps": 2, + "stop_reason": "max_hops", + "strategy": "current_positive_additive" + }, + "expected_path": [], + "id": "local-dev-direct-integrated-prototype", + "observed_paths": [ + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "diagnostics": { + "activation_total": 1.7, + "active_path_count": 2, + "competition_sets": [], + "converged": true, + "expansions": 1, + "steps": 1, + "stop_reason": "frontier_exhausted", + "strategy": "current_positive_additive" + }, + "expected_path": [], + "id": "local-dev-direct-context-rot", + "observed_paths": [ + { + "seed_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "diagnostics": { + "activation_total": 3.1699999999999995, + "active_path_count": 5, + "competition_sets": [], + "converged": false, + "expansions": 4, + "steps": 2, + "stop_reason": "max_hops", + "strategy": "current_positive_additive" + }, + "expected_path": [], + "id": "local-dev-direct-retrieval-surface", + "observed_paths": [ + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "diagnostics": { + "activation_total": 4.55928678103255, + "active_path_count": 8, + "competition_sets": [], + "converged": false, + "expansions": 7, + "steps": 2, + "stop_reason": "max_hops", + "strategy": "current_positive_additive" + }, + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "id": "local-dev-relation-prototype-judgment", + "observed_paths": [ + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + } + ], + "path_matched": true, + "rank": 5 + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "diagnostics": { + "activation_total": 3.5899999999999994, + "active_path_count": 5, + "competition_sets": [], + "converged": false, + "expansions": 4, + "steps": 2, + "stop_reason": "max_hops", + "strategy": "current_positive_additive" + }, + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ], + "id": "local-dev-relation-lineage-context", + "observed_paths": [ + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + } + ], + "path_matched": true, + "rank": 4 + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "diagnostics": { + "activation_total": 3.1699999999999995, + "active_path_count": 5, + "competition_sets": [], + "converged": false, + "expansions": 4, + "steps": 2, + "stop_reason": "max_hops", + "strategy": "current_positive_additive" + }, + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ], + "id": "local-dev-relation-surface-lineage", + "observed_paths": [ + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + } + ], + "path_matched": true, + "rank": 2 + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "diagnostics": { + "activation_total": 1.0, + "active_path_count": 1, + "competition_sets": [], + "converged": true, + "expansions": 0, + "steps": 0, + "stop_reason": "frontier_exhausted", + "strategy": "current_positive_additive" + }, + "expected_path": [], + "id": "local-dev-negative-l1-brake", + "observed_paths": [ + { + "seed_id": "w:6Gdu5NFP9nRML71rqJhTN8hhTMS72n5_vVP0at6lR7c", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "diagnostics": { + "activation_total": 1.0, + "active_path_count": 1, + "competition_sets": [], + "converged": true, + "expansions": 0, + "steps": 0, + "stop_reason": "frontier_exhausted", + "strategy": "current_positive_additive" + }, + "expected_path": [], + "id": "local-dev-negative-evaluation", + "observed_paths": [ + { + "seed_id": "w:mlSfgBoCoxtwg7mTM20GRuEGdrXQ6a2jMnXPcWGCgD4", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "diagnostics": { + "activation_total": 1.7, + "active_path_count": 2, + "competition_sets": [], + "converged": true, + "expansions": 1, + "steps": 1, + "stop_reason": "frontier_exhausted", + "strategy": "current_positive_additive" + }, + "expected_path": [], + "id": "local-dev-negative-memory-graphrag", + "observed_paths": [ + { + "seed_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + } + ], + "diagnostics": { + "competition_set_count": 0, + "mean_activation_total": 2.772142975670283, + "mean_active_path_count": 4.111111111111111, + "mean_expansions": 3.111111111111111, + "mean_steps": 1.3333333333333333, + "stop_reasons": { + "frontier_exhausted": 4, + "max_hops": 5 + } + }, + "explanations": [ + { + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "id": "local-dev-relation-prototype-judgment", + "matched": true, + "observed_paths": [ + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + } + ] + }, + { + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ], + "id": "local-dev-relation-lineage-context", + "matched": true, + "observed_paths": [ + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + } + ] + }, + { + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ], + "id": "local-dev-relation-surface-lineage", + "matched": true, + "observed_paths": [ + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + } + ] + } + ], + "family": "current_positive_additive", + "feedback": { + "case_id": "local-dev-relation-prototype-judgment", + "changed_edges": [ + { + "edge_type": "mention", + "new_weight": 1.1261462745740232, + "old_weight": 1.0, + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "credited_edges": [ + { + "edge_type": "mention", + "new_weight": 1.1261462745740232, + "old_weight": 1.0, + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "non_target_rank_changes": [], + "target_node_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "target_rank_after": 3, + "target_rank_before": 5, + "uncredited_edge_changes": [] + }, + "id": "current", + "metrics": { + "cohorts": { + "direct_lookup": { + "cases": 3, + "hit_at_3": 1.0, + "mean_reciprocal_rank": 1.0, + "ranks": [ + 1, + 1, + 1 + ] + }, + "negative_control": { + "cases": 3, + "hit_at_3": 1.0, + "mean_reciprocal_rank": 1.0, + "ranks": [ + 1, + 1, + 1 + ] + }, + "relation": { + "cases": 3, + "hit_at_3": 0.3333333333333333, + "mean_reciprocal_rank": 0.31666666666666665, + "ranks": [ + 5, + 4, + 2 + ] + } + }, + "overall": { + "cases": 9, + "hit_at_3": 0.7777777777777778, + "mean_reciprocal_rank": 0.7722222222222223, + "ranks": [ + 1, + 1, + 1, + 5, + 4, + 2, + 1, + 1, + 1 + ] + } + }, + "parameters": {}, + "relative_to_baselines": { + "direct_mrr_delta_from_current": 0.0, + "negative_mrr_delta_from_current": 0.0, + "relation_mrr_delta_from_current": 0.0, + "relation_mrr_delta_from_recurrent_balanced": -0.04444444444444445 + }, + "structural_complexity": 0 + }, + { + "candidate_gate": { + "all_relation_paths_match": true, + "direct_non_regression": false, + "feedback_isolated": true, + "negative_non_regression": true, + "relation_strictly_above_current": true, + "relation_strictly_above_recurrent_balanced": false + }, + "candidate_gate_passed": false, + "cases": [ + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "diagnostics": { + "activation_total": 1.0818573629176769, + "active_path_count": 7, + "competition_sets": [], + "converged": false, + "expansions": 10, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "recurrent_competition" + }, + "expected_path": [], + "id": "local-dev-direct-integrated-prototype", + "observed_paths": [ + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [] + } + ], + "path_matched": null, + "rank": 2 + }, + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "diagnostics": { + "activation_total": 0.44677829426418225, + "active_path_count": 2, + "competition_sets": [], + "converged": false, + "expansions": 2, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "recurrent_competition" + }, + "expected_path": [], + "id": "local-dev-direct-context-rot", + "observed_paths": [ + { + "seed_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "steps": [] + } + ], + "path_matched": null, + "rank": 2 + }, + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "diagnostics": { + "activation_total": 0.569275115650627, + "active_path_count": 5, + "competition_sets": [], + "converged": false, + "expansions": 5, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "recurrent_competition" + }, + "expected_path": [], + "id": "local-dev-direct-retrieval-surface", + "observed_paths": [ + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "diagnostics": { + "activation_total": 1.0056040665701704, + "active_path_count": 7, + "competition_sets": [], + "converged": false, + "expansions": 10, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "recurrent_competition" + }, + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "id": "local-dev-relation-prototype-judgment", + "observed_paths": [ + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + }, + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + } + ], + "path_matched": true, + "rank": 3 + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "diagnostics": { + "activation_total": 0.923074960214771, + "active_path_count": 5, + "competition_sets": [], + "converged": false, + "expansions": 7, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "recurrent_competition" + }, + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ], + "id": "local-dev-relation-lineage-context", + "observed_paths": [ + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + }, + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + } + ], + "path_matched": true, + "rank": 4 + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "diagnostics": { + "activation_total": 0.5728399654524289, + "active_path_count": 5, + "competition_sets": [], + "converged": false, + "expansions": 5, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "recurrent_competition" + }, + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ], + "id": "local-dev-relation-surface-lineage", + "observed_paths": [ + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + }, + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + } + ], + "path_matched": true, + "rank": 2 + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "diagnostics": { + "activation_total": 0.2025, + "active_path_count": 1, + "competition_sets": [], + "converged": false, + "expansions": 0, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "recurrent_competition" + }, + "expected_path": [], + "id": "local-dev-negative-l1-brake", + "observed_paths": [ + { + "seed_id": "w:6Gdu5NFP9nRML71rqJhTN8hhTMS72n5_vVP0at6lR7c", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "diagnostics": { + "activation_total": 0.2025, + "active_path_count": 1, + "competition_sets": [], + "converged": false, + "expansions": 0, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "recurrent_competition" + }, + "expected_path": [], + "id": "local-dev-negative-evaluation", + "observed_paths": [ + { + "seed_id": "w:mlSfgBoCoxtwg7mTM20GRuEGdrXQ6a2jMnXPcWGCgD4", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "diagnostics": { + "activation_total": 0.4234909336669189, + "active_path_count": 2, + "competition_sets": [], + "converged": false, + "expansions": 2, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "recurrent_competition" + }, + "expected_path": [], + "id": "local-dev-negative-memory-graphrag", + "observed_paths": [ + { + "seed_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + } + ], + "diagnostics": { + "competition_set_count": 0, + "mean_activation_total": 0.6031022998596417, + "mean_active_path_count": 3.888888888888889, + "mean_expansions": 4.555555555555555, + "mean_steps": 2.0, + "stop_reasons": { + "recurrent_step_limit": 9 + } + }, + "explanations": [ + { + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "id": "local-dev-relation-prototype-judgment", + "matched": true, + "observed_paths": [ + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + }, + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + } + ] + }, + { + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ], + "id": "local-dev-relation-lineage-context", + "matched": true, + "observed_paths": [ + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + }, + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + } + ] + }, + { + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ], + "id": "local-dev-relation-surface-lineage", + "matched": true, + "observed_paths": [ + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + }, + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + } + ] + } + ], + "family": "recurrent_competition", + "feedback": { + "case_id": "local-dev-relation-prototype-judgment", + "changed_edges": [ + { + "edge_type": "mention", + "new_weight": 1.0556598345777672, + "old_weight": 1.0, + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "credited_edges": [ + { + "edge_type": "mention", + "new_weight": 1.0556598345777672, + "old_weight": 1.0, + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "non_target_rank_changes": [], + "target_node_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "target_rank_after": 1, + "target_rank_before": 3, + "uncredited_edge_changes": [] + }, + "id": "recurrent-balanced", + "metrics": { + "cohorts": { + "direct_lookup": { + "cases": 3, + "hit_at_3": 1.0, + "mean_reciprocal_rank": 0.6666666666666666, + "ranks": [ + 2, + 2, + 1 + ] + }, + "negative_control": { + "cases": 3, + "hit_at_3": 1.0, + "mean_reciprocal_rank": 1.0, + "ranks": [ + 1, + 1, + 1 + ] + }, + "relation": { + "cases": 3, + "hit_at_3": 0.6666666666666666, + "mean_reciprocal_rank": 0.3611111111111111, + "ranks": [ + 3, + 4, + 2 + ] + } + }, + "overall": { + "cases": 9, + "hit_at_3": 0.8888888888888888, + "mean_reciprocal_rank": 0.675925925925926, + "ranks": [ + 2, + 2, + 1, + 3, + 4, + 2, + 1, + 1, + 1 + ] + } + }, + "parameters": {}, + "relative_to_baselines": { + "direct_mrr_delta_from_current": -0.33333333333333337, + "negative_mrr_delta_from_current": 0.0, + "relation_mrr_delta_from_current": 0.04444444444444445, + "relation_mrr_delta_from_recurrent_balanced": 0.0 + }, + "structural_complexity": 4 + }, + { + "candidate_gate": { + "all_relation_paths_match": true, + "direct_non_regression": false, + "feedback_isolated": true, + "negative_non_regression": false, + "relation_strictly_above_current": true, + "relation_strictly_above_recurrent_balanced": true + }, + "candidate_gate_passed": false, + "cases": [ + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "diagnostics": { + "activation_total": 2.05, + "active_path_count": 7, + "competition_sets": [ + { + "mean_query_relevance": 1.0, + "message_total_after": 1.0, + "message_total_before": 2.0999999999999996, + "neighbor_count": 3, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "step": 1 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.2333333333333333, + "message_total_before": 0.2333333333333333, + "neighbor_count": 1, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 2 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.5, + "message_total_before": 1.0499999999999998, + "neighbor_count": 3, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "step": 2 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.3333333333333333, + "message_total_before": 0.4666666666666666, + "neighbor_count": 2, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "source_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "step": 2 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.2333333333333333, + "message_total_before": 0.2333333333333333, + "neighbor_count": 1, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "source_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "step": 2 + } + ], + "converged": false, + "expansions": 10, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_competition" + }, + "expected_path": [], + "id": "local-dev-direct-integrated-prototype", + "observed_paths": [ + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [] + } + ], + "path_matched": null, + "rank": 3 + }, + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "diagnostics": { + "activation_total": 0.95, + "active_path_count": 2, + "competition_sets": [ + { + "mean_query_relevance": 1.0, + "message_total_after": 0.7, + "message_total_before": 0.7, + "neighbor_count": 1, + "path_identity": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "source_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "step": 1 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.35, + "message_total_before": 0.35, + "neighbor_count": 1, + "path_identity": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "source_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "step": 2 + } + ], + "converged": false, + "expansions": 2, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_competition" + }, + "expected_path": [], + "id": "local-dev-direct-context-rot", + "observed_paths": [ + { + "seed_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "steps": [] + } + ], + "path_matched": null, + "rank": 2 + }, + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "diagnostics": { + "activation_total": 1.65, + "active_path_count": 5, + "competition_sets": [ + { + "mean_query_relevance": 1.0, + "message_total_after": 0.7, + "message_total_before": 0.7, + "neighbor_count": 1, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 1 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.35, + "message_total_before": 0.35, + "neighbor_count": 1, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 2 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.6999999999999998, + "message_total_before": 1.4699999999999998, + "neighbor_count": 3, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs>w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "step": 2 + } + ], + "converged": false, + "expansions": 5, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_competition" + }, + "expected_path": [], + "id": "local-dev-direct-retrieval-surface", + "observed_paths": [ + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [] + } + ], + "path_matched": null, + "rank": 2 + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "diagnostics": { + "activation_total": 1.8471418776910526, + "active_path_count": 7, + "competition_sets": [ + { + "mean_query_relevance": 1.0, + "message_total_after": 0.9010448183858795, + "message_total_before": 1.8921941186103468, + "neighbor_count": 3, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "step": 1 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.21024379095670517, + "message_total_before": 0.21024379095670517, + "neighbor_count": 1, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 2 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.45052240919293973, + "message_total_before": 0.9460970593051734, + "neighbor_count": 3, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "step": 2 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.30034827279529314, + "message_total_before": 0.42048758191341035, + "neighbor_count": 2, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "source_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "step": 2 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.21024379095670517, + "message_total_before": 0.21024379095670517, + "neighbor_count": 1, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "source_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "step": 2 + } + ], + "converged": false, + "expansions": 10, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_competition" + }, + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "id": "local-dev-relation-prototype-judgment", + "observed_paths": [ + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + }, + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + } + ], + "path_matched": true, + "rank": 4 + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "diagnostics": { + "activation_total": 1.4833333333333332, + "active_path_count": 5, + "competition_sets": [ + { + "mean_query_relevance": 1.0, + "message_total_after": 1.0, + "message_total_before": 2.0999999999999996, + "neighbor_count": 3, + "path_identity": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "step": 1 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.2333333333333333, + "message_total_before": 0.2333333333333333, + "neighbor_count": 1, + "path_identity": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA>w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "source_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "step": 2 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.5, + "message_total_before": 1.0499999999999998, + "neighbor_count": 3, + "path_identity": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "step": 2 + } + ], + "converged": false, + "expansions": 7, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_competition" + }, + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ], + "id": "local-dev-relation-lineage-context", + "observed_paths": [ + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + }, + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + } + ], + "path_matched": true, + "rank": 3 + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "diagnostics": { + "activation_total": 1.65, + "active_path_count": 5, + "competition_sets": [ + { + "mean_query_relevance": 1.0, + "message_total_after": 0.7, + "message_total_before": 0.7, + "neighbor_count": 1, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 1 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.35, + "message_total_before": 0.35, + "neighbor_count": 1, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 2 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.6999999999999998, + "message_total_before": 1.4699999999999998, + "neighbor_count": 3, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs>w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "step": 2 + } + ], + "converged": false, + "expansions": 5, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_competition" + }, + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ], + "id": "local-dev-relation-surface-lineage", + "observed_paths": [ + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + }, + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + } + ], + "path_matched": true, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "diagnostics": { + "activation_total": 0.25, + "active_path_count": 1, + "competition_sets": [], + "converged": false, + "expansions": 0, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_competition" + }, + "expected_path": [], + "id": "local-dev-negative-l1-brake", + "observed_paths": [ + { + "seed_id": "w:6Gdu5NFP9nRML71rqJhTN8hhTMS72n5_vVP0at6lR7c", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "diagnostics": { + "activation_total": 0.25, + "active_path_count": 1, + "competition_sets": [], + "converged": false, + "expansions": 0, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_competition" + }, + "expected_path": [], + "id": "local-dev-negative-evaluation", + "observed_paths": [ + { + "seed_id": "w:mlSfgBoCoxtwg7mTM20GRuEGdrXQ6a2jMnXPcWGCgD4", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "diagnostics": { + "activation_total": 0.95, + "active_path_count": 2, + "competition_sets": [ + { + "mean_query_relevance": 1.0, + "message_total_after": 0.7, + "message_total_before": 0.7, + "neighbor_count": 1, + "path_identity": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "source_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "step": 1 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.35, + "message_total_before": 0.35, + "neighbor_count": 1, + "path_identity": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "source_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "step": 2 + } + ], + "converged": false, + "expansions": 2, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_competition" + }, + "expected_path": [], + "id": "local-dev-negative-memory-graphrag", + "observed_paths": [ + { + "seed_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "steps": [] + } + ], + "path_matched": null, + "rank": 2 + } + ], + "diagnostics": { + "competition_set_count": 23, + "mean_activation_total": 1.2311639123360427, + "mean_active_path_count": 3.888888888888889, + "mean_expansions": 4.555555555555555, + "mean_steps": 2.0, + "stop_reasons": { + "recurrent_step_limit": 9 + } + }, + "explanations": [ + { + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "id": "local-dev-relation-prototype-judgment", + "matched": true, + "observed_paths": [ + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + }, + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + } + ] + }, + { + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ], + "id": "local-dev-relation-lineage-context", + "matched": true, + "observed_paths": [ + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + }, + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + } + ] + }, + { + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ], + "id": "local-dev-relation-surface-lineage", + "matched": true, + "observed_paths": [ + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + }, + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + } + ] + } + ], + "family": "local_neighbor_competition", + "feedback": { + "case_id": "local-dev-relation-prototype-judgment", + "changed_edges": [ + { + "edge_type": "mention", + "new_weight": 1.0600696545590587, + "old_weight": 1.0, + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "credited_edges": [ + { + "edge_type": "mention", + "new_weight": 1.0600696545590587, + "old_weight": 1.0, + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "non_target_rank_changes": [], + "target_node_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "target_rank_after": 3, + "target_rank_before": 4, + "uncredited_edge_changes": [] + }, + "id": "local-neighbor", + "metrics": { + "cohorts": { + "direct_lookup": { + "cases": 3, + "hit_at_3": 1.0, + "mean_reciprocal_rank": 0.4444444444444444, + "ranks": [ + 3, + 2, + 2 + ] + }, + "negative_control": { + "cases": 3, + "hit_at_3": 1.0, + "mean_reciprocal_rank": 0.8333333333333334, + "ranks": [ + 1, + 1, + 2 + ] + }, + "relation": { + "cases": 3, + "hit_at_3": 0.6666666666666666, + "mean_reciprocal_rank": 0.5277777777777778, + "ranks": [ + 4, + 3, + 1 + ] + } + }, + "overall": { + "cases": 9, + "hit_at_3": 0.8888888888888888, + "mean_reciprocal_rank": 0.6018518518518517, + "ranks": [ + 3, + 2, + 2, + 4, + 3, + 1, + 1, + 1, + 2 + ] + } + }, + "parameters": {}, + "relative_to_baselines": { + "direct_mrr_delta_from_current": -0.5555555555555556, + "negative_mrr_delta_from_current": -0.16666666666666663, + "relation_mrr_delta_from_current": 0.21111111111111114, + "relation_mrr_delta_from_recurrent_balanced": 0.16666666666666669 + }, + "structural_complexity": 5 + }, + { + "candidate_gate": { + "all_relation_paths_match": true, + "direct_non_regression": false, + "feedback_isolated": true, + "negative_non_regression": true, + "relation_strictly_above_current": true, + "relation_strictly_above_recurrent_balanced": false + }, + "candidate_gate_passed": false, + "cases": [ + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "diagnostics": { + "activation_total": 1.326655604344017, + "active_path_count": 7, + "competition_sets": [ + { + "mean_query_relevance": 0.4208571257007834, + "message_total_after": 0.7929261004292112, + "message_total_before": 0.883799963971645, + "neighbor_count": 3, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "step": 1 + }, + { + "mean_query_relevance": 0.4152894157431288, + "message_total_after": 0.07473861473058266, + "message_total_before": 0.07473861473058266, + "neighbor_count": 1, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 2 + }, + { + "mean_query_relevance": 0.4208571257007834, + "message_total_after": 0.3964630502146056, + "message_total_before": 0.4418999819858225, + "neighbor_count": 3, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "step": 2 + }, + { + "mean_query_relevance": 0.4, + "message_total_after": 0.13265684380857878, + "message_total_before": 0.1473964931206431, + "neighbor_count": 2, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "source_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "step": 2 + }, + { + "mean_query_relevance": 0.4, + "message_total_after": 0.07633404537564437, + "message_total_before": 0.07633404537564437, + "neighbor_count": 1, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "source_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "step": 2 + } + ], + "converged": false, + "expansions": 10, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_query_competition" + }, + "expected_path": [], + "id": "local-dev-direct-integrated-prototype", + "observed_paths": [ + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "diagnostics": { + "activation_total": 0.5681293345581345, + "active_path_count": 2, + "competition_sets": [ + { + "mean_query_relevance": 0.45447047794019224, + "message_total_after": 0.3181293345581345, + "message_total_before": 0.3181293345581345, + "neighbor_count": 1, + "path_identity": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "source_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "step": 1 + }, + { + "mean_query_relevance": 0.45447047794019224, + "message_total_after": 0.15906466727906726, + "message_total_before": 0.15906466727906726, + "neighbor_count": 1, + "path_identity": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "source_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "step": 2 + } + ], + "converged": false, + "expansions": 2, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_query_competition" + }, + "expected_path": [], + "id": "local-dev-direct-context-rot", + "observed_paths": [ + { + "seed_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "steps": [] + } + ], + "path_matched": null, + "rank": 2 + }, + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "diagnostics": { + "activation_total": 0.7783603496769541, + "active_path_count": 5, + "competition_sets": [ + { + "mean_query_relevance": 0.41933975113660915, + "message_total_after": 0.2935378257956264, + "message_total_before": 0.2935378257956264, + "neighbor_count": 1, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 1 + }, + { + "mean_query_relevance": 0.41933975113660915, + "message_total_after": 0.1467689128978132, + "message_total_before": 0.1467689128978132, + "neighbor_count": 1, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 2 + }, + { + "mean_query_relevance": 0.42497485259151563, + "message_total_after": 0.23482252388132774, + "message_total_before": 0.26196700791981364, + "neighbor_count": 3, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs>w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "step": 2 + } + ], + "converged": false, + "expansions": 5, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_query_competition" + }, + "expected_path": [], + "id": "local-dev-direct-retrieval-surface", + "observed_paths": [ + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "diagnostics": { + "activation_total": 1.2277431151992781, + "active_path_count": 7, + "competition_sets": [ + { + "mean_query_relevance": 0.43293017366972314, + "message_total_after": 0.7356981765201552, + "message_total_before": 0.8191879283868061, + "neighbor_count": 3, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "step": 1 + }, + { + "mean_query_relevance": 0.4, + "message_total_after": 0.06683468082390363, + "message_total_before": 0.06683468082390363, + "neighbor_count": 1, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 2 + }, + { + "mean_query_relevance": 0.43293017366972314, + "message_total_after": 0.3678490882600776, + "message_total_before": 0.40959396419340305, + "neighbor_count": 3, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "step": 2 + }, + { + "mean_query_relevance": 0.4096756842715059, + "message_total_after": 0.12928327782440163, + "message_total_before": 0.1436556291476393, + "neighbor_count": 2, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "source_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "step": 2 + }, + { + "mean_query_relevance": 0.4094820931186152, + "message_total_after": 0.07066577543434756, + "message_total_before": 0.07066577543434756, + "neighbor_count": 1, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "source_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "step": 2 + } + ], + "converged": false, + "expansions": 10, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_query_competition" + }, + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "id": "local-dev-relation-prototype-judgment", + "observed_paths": [ + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + }, + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + } + ], + "path_matched": true, + "rank": 4 + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "diagnostics": { + "activation_total": 1.1144113419864317, + "active_path_count": 5, + "competition_sets": [ + { + "mean_query_relevance": 0.4207624653851654, + "message_total_after": 0.7909980391596609, + "message_total_before": 0.8836011773088472, + "neighbor_count": 3, + "path_identity": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "step": 1 + }, + { + "mean_query_relevance": 0.4209656967344384, + "message_total_after": 0.07341330282677078, + "message_total_before": 0.07341330282677078, + "neighbor_count": 1, + "path_identity": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA>w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "source_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "step": 2 + }, + { + "mean_query_relevance": 0.4207624653851654, + "message_total_after": 0.39549901957983047, + "message_total_before": 0.4418005886544236, + "neighbor_count": 3, + "path_identity": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "step": 2 + } + ], + "converged": false, + "expansions": 7, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_query_competition" + }, + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ], + "id": "local-dev-relation-lineage-context", + "observed_paths": [ + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + }, + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + } + ], + "path_matched": true, + "rank": 4 + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "diagnostics": { + "activation_total": 0.7844196033049898, + "active_path_count": 5, + "competition_sets": [ + { + "mean_query_relevance": 0.4264820448851425, + "message_total_after": 0.29853743141959976, + "message_total_before": 0.29853743141959976, + "neighbor_count": 1, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 1 + }, + { + "mean_query_relevance": 0.4264820448851425, + "message_total_after": 0.14926871570979988, + "message_total_before": 0.14926871570979988, + "neighbor_count": 1, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 2 + }, + { + "mean_query_relevance": 0.4187060893458822, + "message_total_after": 0.23588217188539007, + "message_total_before": 0.26249882490943677, + "neighbor_count": 3, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs>w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "step": 2 + } + ], + "converged": false, + "expansions": 5, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_query_competition" + }, + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ], + "id": "local-dev-relation-surface-lineage", + "observed_paths": [ + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + }, + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + } + ], + "path_matched": true, + "rank": 2 + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "diagnostics": { + "activation_total": 0.25, + "active_path_count": 1, + "competition_sets": [], + "converged": false, + "expansions": 0, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_query_competition" + }, + "expected_path": [], + "id": "local-dev-negative-l1-brake", + "observed_paths": [ + { + "seed_id": "w:6Gdu5NFP9nRML71rqJhTN8hhTMS72n5_vVP0at6lR7c", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "diagnostics": { + "activation_total": 0.25, + "active_path_count": 1, + "competition_sets": [], + "converged": false, + "expansions": 0, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_query_competition" + }, + "expected_path": [], + "id": "local-dev-negative-evaluation", + "observed_paths": [ + { + "seed_id": "w:mlSfgBoCoxtwg7mTM20GRuEGdrXQ6a2jMnXPcWGCgD4", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "diagnostics": { + "activation_total": 0.537488070614367, + "active_path_count": 2, + "competition_sets": [ + { + "mean_query_relevance": 0.41069724373481004, + "message_total_after": 0.287488070614367, + "message_total_before": 0.287488070614367, + "neighbor_count": 1, + "path_identity": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "source_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "step": 1 + }, + { + "mean_query_relevance": 0.41069724373481004, + "message_total_after": 0.1437440353071835, + "message_total_before": 0.1437440353071835, + "neighbor_count": 1, + "path_identity": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "source_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "step": 2 + } + ], + "converged": false, + "expansions": 2, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_query_competition" + }, + "expected_path": [], + "id": "local-dev-negative-memory-graphrag", + "observed_paths": [ + { + "seed_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + } + ], + "diagnostics": { + "competition_set_count": 23, + "mean_activation_total": 0.7596897132982414, + "mean_active_path_count": 3.888888888888889, + "mean_expansions": 4.555555555555555, + "mean_steps": 2.0, + "stop_reasons": { + "recurrent_step_limit": 9 + } + }, + "explanations": [ + { + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "id": "local-dev-relation-prototype-judgment", + "matched": true, + "observed_paths": [ + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + }, + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + } + ] + }, + { + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ], + "id": "local-dev-relation-lineage-context", + "matched": true, + "observed_paths": [ + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + }, + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + } + ] + }, + { + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ], + "id": "local-dev-relation-surface-lineage", + "matched": true, + "observed_paths": [ + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + }, + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + } + ] + } + ], + "family": "local_neighbor_query_competition", + "feedback": { + "case_id": "local-dev-relation-prototype-judgment", + "changed_edges": [ + { + "edge_type": "mention", + "new_weight": 1.0500938511199904, + "old_weight": 1.0, + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "credited_edges": [ + { + "edge_type": "mention", + "new_weight": 1.0500938511199904, + "old_weight": 1.0, + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "non_target_rank_changes": [], + "target_node_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "target_rank_after": 2, + "target_rank_before": 4, + "uncredited_edge_changes": [] + }, + "id": "local-neighbor-query", + "metrics": { + "cohorts": { + "direct_lookup": { + "cases": 3, + "hit_at_3": 1.0, + "mean_reciprocal_rank": 0.8333333333333334, + "ranks": [ + 1, + 2, + 1 + ] + }, + "negative_control": { + "cases": 3, + "hit_at_3": 1.0, + "mean_reciprocal_rank": 1.0, + "ranks": [ + 1, + 1, + 1 + ] + }, + "relation": { + "cases": 3, + "hit_at_3": 0.3333333333333333, + "mean_reciprocal_rank": 0.3333333333333333, + "ranks": [ + 4, + 4, + 2 + ] + } + }, + "overall": { + "cases": 9, + "hit_at_3": 0.7777777777777778, + "mean_reciprocal_rank": 0.7222222222222222, + "ranks": [ + 1, + 2, + 1, + 4, + 4, + 2, + 1, + 1, + 1 + ] + } + }, + "parameters": {}, + "relative_to_baselines": { + "direct_mrr_delta_from_current": -0.16666666666666663, + "negative_mrr_delta_from_current": 0.0, + "relation_mrr_delta_from_current": 0.016666666666666663, + "relation_mrr_delta_from_recurrent_balanced": -0.02777777777777779 + }, + "structural_complexity": 6 + }, + { + "candidate_gate": { + "all_relation_paths_match": true, + "direct_non_regression": false, + "feedback_isolated": true, + "negative_non_regression": false, + "relation_strictly_above_current": true, + "relation_strictly_above_recurrent_balanced": true + }, + "candidate_gate_passed": false, + "cases": [ + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "diagnostics": { + "activation_total": 2.05, + "active_path_count": 11, + "competition_sets": [ + { + "mean_query_relevance": 1.0, + "message_total_after": 1.0, + "message_total_before": 2.0999999999999996, + "neighbor_count": 3, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "step": 1 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.5, + "message_total_before": 1.0499999999999998, + "neighbor_count": 3, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "step": 2 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.2333333333333333, + "message_total_before": 0.2333333333333333, + "neighbor_count": 1, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 2 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.3333333333333333, + "message_total_before": 0.4666666666666666, + "neighbor_count": 2, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "source_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "step": 2 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.2333333333333333, + "message_total_before": 0.2333333333333333, + "neighbor_count": 1, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "source_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "step": 2 + } + ], + "converged": false, + "expansions": 10, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_path_competition" + }, + "expected_path": [], + "id": "local-dev-direct-integrated-prototype", + "observed_paths": [ + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [] + } + ], + "path_matched": null, + "rank": 3 + }, + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "diagnostics": { + "activation_total": 0.95, + "active_path_count": 3, + "competition_sets": [ + { + "mean_query_relevance": 1.0, + "message_total_after": 0.7, + "message_total_before": 0.7, + "neighbor_count": 1, + "path_identity": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "source_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "step": 1 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.35, + "message_total_before": 0.35, + "neighbor_count": 1, + "path_identity": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "source_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "step": 2 + } + ], + "converged": false, + "expansions": 2, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_path_competition" + }, + "expected_path": [], + "id": "local-dev-direct-context-rot", + "observed_paths": [ + { + "seed_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "steps": [] + } + ], + "path_matched": null, + "rank": 2 + }, + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "diagnostics": { + "activation_total": 1.65, + "active_path_count": 6, + "competition_sets": [ + { + "mean_query_relevance": 1.0, + "message_total_after": 0.7, + "message_total_before": 0.7, + "neighbor_count": 1, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 1 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.6999999999999998, + "message_total_before": 1.4699999999999998, + "neighbor_count": 3, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs>w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "step": 2 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.35, + "message_total_before": 0.35, + "neighbor_count": 1, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 2 + } + ], + "converged": false, + "expansions": 5, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_path_competition" + }, + "expected_path": [], + "id": "local-dev-direct-retrieval-surface", + "observed_paths": [ + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [] + } + ], + "path_matched": null, + "rank": 2 + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "diagnostics": { + "activation_total": 1.8471418776910526, + "active_path_count": 11, + "competition_sets": [ + { + "mean_query_relevance": 1.0, + "message_total_after": 0.9010448183858795, + "message_total_before": 1.8921941186103468, + "neighbor_count": 3, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "step": 1 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.45052240919293973, + "message_total_before": 0.9460970593051734, + "neighbor_count": 3, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "step": 2 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.21024379095670517, + "message_total_before": 0.21024379095670517, + "neighbor_count": 1, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 2 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.30034827279529314, + "message_total_before": 0.42048758191341035, + "neighbor_count": 2, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "source_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "step": 2 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.21024379095670517, + "message_total_before": 0.21024379095670517, + "neighbor_count": 1, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "source_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "step": 2 + } + ], + "converged": false, + "expansions": 10, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_path_competition" + }, + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "id": "local-dev-relation-prototype-judgment", + "observed_paths": [ + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + }, + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + } + ], + "path_matched": true, + "rank": 4 + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "diagnostics": { + "activation_total": 1.4833333333333332, + "active_path_count": 8, + "competition_sets": [ + { + "mean_query_relevance": 1.0, + "message_total_after": 1.0, + "message_total_before": 2.0999999999999996, + "neighbor_count": 3, + "path_identity": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "step": 1 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.5, + "message_total_before": 1.0499999999999998, + "neighbor_count": 3, + "path_identity": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "step": 2 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.2333333333333333, + "message_total_before": 0.2333333333333333, + "neighbor_count": 1, + "path_identity": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA>w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "source_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "step": 2 + } + ], + "converged": false, + "expansions": 7, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_path_competition" + }, + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ], + "id": "local-dev-relation-lineage-context", + "observed_paths": [ + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + }, + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + } + ], + "path_matched": true, + "rank": 3 + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "diagnostics": { + "activation_total": 1.65, + "active_path_count": 6, + "competition_sets": [ + { + "mean_query_relevance": 1.0, + "message_total_after": 0.7, + "message_total_before": 0.7, + "neighbor_count": 1, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 1 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.6999999999999998, + "message_total_before": 1.4699999999999998, + "neighbor_count": 3, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs>w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "step": 2 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.35, + "message_total_before": 0.35, + "neighbor_count": 1, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 2 + } + ], + "converged": false, + "expansions": 5, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_path_competition" + }, + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ], + "id": "local-dev-relation-surface-lineage", + "observed_paths": [ + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + }, + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + } + ], + "path_matched": true, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "diagnostics": { + "activation_total": 0.25, + "active_path_count": 1, + "competition_sets": [], + "converged": false, + "expansions": 0, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_path_competition" + }, + "expected_path": [], + "id": "local-dev-negative-l1-brake", + "observed_paths": [ + { + "seed_id": "w:6Gdu5NFP9nRML71rqJhTN8hhTMS72n5_vVP0at6lR7c", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "diagnostics": { + "activation_total": 0.25, + "active_path_count": 1, + "competition_sets": [], + "converged": false, + "expansions": 0, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_path_competition" + }, + "expected_path": [], + "id": "local-dev-negative-evaluation", + "observed_paths": [ + { + "seed_id": "w:mlSfgBoCoxtwg7mTM20GRuEGdrXQ6a2jMnXPcWGCgD4", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "diagnostics": { + "activation_total": 0.95, + "active_path_count": 3, + "competition_sets": [ + { + "mean_query_relevance": 1.0, + "message_total_after": 0.7, + "message_total_before": 0.7, + "neighbor_count": 1, + "path_identity": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "source_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "step": 1 + }, + { + "mean_query_relevance": 1.0, + "message_total_after": 0.35, + "message_total_before": 0.35, + "neighbor_count": 1, + "path_identity": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "source_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "step": 2 + } + ], + "converged": false, + "expansions": 2, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_path_competition" + }, + "expected_path": [], + "id": "local-dev-negative-memory-graphrag", + "observed_paths": [ + { + "seed_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "steps": [] + } + ], + "path_matched": null, + "rank": 2 + } + ], + "diagnostics": { + "competition_set_count": 23, + "mean_activation_total": 1.2311639123360427, + "mean_active_path_count": 5.555555555555555, + "mean_expansions": 4.555555555555555, + "mean_steps": 2.0, + "stop_reasons": { + "recurrent_step_limit": 9 + } + }, + "explanations": [ + { + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "id": "local-dev-relation-prototype-judgment", + "matched": true, + "observed_paths": [ + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + }, + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + } + ] + }, + { + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ], + "id": "local-dev-relation-lineage-context", + "matched": true, + "observed_paths": [ + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + }, + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + } + ] + }, + { + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ], + "id": "local-dev-relation-surface-lineage", + "matched": true, + "observed_paths": [ + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + }, + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + } + ] + } + ], + "family": "local_neighbor_path_competition", + "feedback": { + "case_id": "local-dev-relation-prototype-judgment", + "changed_edges": [ + { + "edge_type": "mention", + "new_weight": 1.0600696545590587, + "old_weight": 1.0, + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "credited_edges": [ + { + "edge_type": "mention", + "new_weight": 1.0600696545590587, + "old_weight": 1.0, + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "non_target_rank_changes": [], + "target_node_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "target_rank_after": 3, + "target_rank_before": 4, + "uncredited_edge_changes": [] + }, + "id": "local-neighbor-path", + "metrics": { + "cohorts": { + "direct_lookup": { + "cases": 3, + "hit_at_3": 1.0, + "mean_reciprocal_rank": 0.4444444444444444, + "ranks": [ + 3, + 2, + 2 + ] + }, + "negative_control": { + "cases": 3, + "hit_at_3": 1.0, + "mean_reciprocal_rank": 0.8333333333333334, + "ranks": [ + 1, + 1, + 2 + ] + }, + "relation": { + "cases": 3, + "hit_at_3": 0.6666666666666666, + "mean_reciprocal_rank": 0.5277777777777778, + "ranks": [ + 4, + 3, + 1 + ] + } + }, + "overall": { + "cases": 9, + "hit_at_3": 0.8888888888888888, + "mean_reciprocal_rank": 0.6018518518518517, + "ranks": [ + 3, + 2, + 2, + 4, + 3, + 1, + 1, + 1, + 2 + ] + } + }, + "parameters": {}, + "relative_to_baselines": { + "direct_mrr_delta_from_current": -0.5555555555555556, + "negative_mrr_delta_from_current": -0.16666666666666663, + "relation_mrr_delta_from_current": 0.21111111111111114, + "relation_mrr_delta_from_recurrent_balanced": 0.16666666666666669 + }, + "structural_complexity": 6 + }, + { + "candidate_gate": { + "all_relation_paths_match": true, + "direct_non_regression": false, + "feedback_isolated": true, + "negative_non_regression": true, + "relation_strictly_above_current": true, + "relation_strictly_above_recurrent_balanced": false + }, + "candidate_gate_passed": false, + "cases": [ + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "diagnostics": { + "activation_total": 1.326655604344017, + "active_path_count": 11, + "competition_sets": [ + { + "mean_query_relevance": 0.4208571257007834, + "message_total_after": 0.7929261004292112, + "message_total_before": 0.883799963971645, + "neighbor_count": 3, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "step": 1 + }, + { + "mean_query_relevance": 0.4208571257007834, + "message_total_after": 0.3964630502146056, + "message_total_before": 0.4418999819858225, + "neighbor_count": 3, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "step": 2 + }, + { + "mean_query_relevance": 0.4, + "message_total_after": 0.07633404537564437, + "message_total_before": 0.07633404537564437, + "neighbor_count": 1, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "source_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "step": 2 + }, + { + "mean_query_relevance": 0.4, + "message_total_after": 0.13265684380857878, + "message_total_before": 0.1473964931206431, + "neighbor_count": 2, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "source_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "step": 2 + }, + { + "mean_query_relevance": 0.4152894157431288, + "message_total_after": 0.07473861473058266, + "message_total_before": 0.07473861473058266, + "neighbor_count": 1, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 2 + } + ], + "converged": false, + "expansions": 10, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_query_path_competition" + }, + "expected_path": [], + "id": "local-dev-direct-integrated-prototype", + "observed_paths": [ + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "diagnostics": { + "activation_total": 0.5681293345581345, + "active_path_count": 3, + "competition_sets": [ + { + "mean_query_relevance": 0.45447047794019224, + "message_total_after": 0.3181293345581345, + "message_total_before": 0.3181293345581345, + "neighbor_count": 1, + "path_identity": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "source_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "step": 1 + }, + { + "mean_query_relevance": 0.45447047794019224, + "message_total_after": 0.15906466727906726, + "message_total_before": 0.15906466727906726, + "neighbor_count": 1, + "path_identity": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "source_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "step": 2 + } + ], + "converged": false, + "expansions": 2, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_query_path_competition" + }, + "expected_path": [], + "id": "local-dev-direct-context-rot", + "observed_paths": [ + { + "seed_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "steps": [] + } + ], + "path_matched": null, + "rank": 2 + }, + { + "acceptable_rank": 3, + "cohort": "direct_lookup", + "diagnostics": { + "activation_total": 0.7783603496769541, + "active_path_count": 6, + "competition_sets": [ + { + "mean_query_relevance": 0.41933975113660915, + "message_total_after": 0.2935378257956264, + "message_total_before": 0.2935378257956264, + "neighbor_count": 1, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 1 + }, + { + "mean_query_relevance": 0.41933975113660915, + "message_total_after": 0.1467689128978132, + "message_total_before": 0.1467689128978132, + "neighbor_count": 1, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 2 + }, + { + "mean_query_relevance": 0.42497485259151563, + "message_total_after": 0.23482252388132774, + "message_total_before": 0.26196700791981364, + "neighbor_count": 3, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs>w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "step": 2 + } + ], + "converged": false, + "expansions": 5, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_query_path_competition" + }, + "expected_path": [], + "id": "local-dev-direct-retrieval-surface", + "observed_paths": [ + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "diagnostics": { + "activation_total": 1.2277431151992777, + "active_path_count": 11, + "competition_sets": [ + { + "mean_query_relevance": 0.43293017366972314, + "message_total_after": 0.7356981765201552, + "message_total_before": 0.8191879283868061, + "neighbor_count": 3, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "step": 1 + }, + { + "mean_query_relevance": 0.43293017366972314, + "message_total_after": 0.3678490882600776, + "message_total_before": 0.40959396419340305, + "neighbor_count": 3, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "step": 2 + }, + { + "mean_query_relevance": 0.4096756842715059, + "message_total_after": 0.12928327782440163, + "message_total_before": 0.1436556291476393, + "neighbor_count": 2, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "source_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "step": 2 + }, + { + "mean_query_relevance": 0.4094820931186152, + "message_total_after": 0.07066577543434756, + "message_total_before": 0.07066577543434756, + "neighbor_count": 1, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "source_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "step": 2 + }, + { + "mean_query_relevance": 0.4, + "message_total_after": 0.06683468082390363, + "message_total_before": 0.06683468082390363, + "neighbor_count": 1, + "path_identity": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A>w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 2 + } + ], + "converged": false, + "expansions": 10, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_query_path_competition" + }, + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "id": "local-dev-relation-prototype-judgment", + "observed_paths": [ + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + }, + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + } + ], + "path_matched": true, + "rank": 4 + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "diagnostics": { + "activation_total": 1.1144113419864317, + "active_path_count": 8, + "competition_sets": [ + { + "mean_query_relevance": 0.4207624653851654, + "message_total_after": 0.7909980391596609, + "message_total_before": 0.8836011773088472, + "neighbor_count": 3, + "path_identity": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "step": 1 + }, + { + "mean_query_relevance": 0.4207624653851654, + "message_total_after": 0.39549901957983047, + "message_total_before": 0.4418005886544236, + "neighbor_count": 3, + "path_identity": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "step": 2 + }, + { + "mean_query_relevance": 0.4209656967344384, + "message_total_after": 0.07341330282677078, + "message_total_before": 0.07341330282677078, + "neighbor_count": 1, + "path_identity": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA>w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "source_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so", + "step": 2 + } + ], + "converged": false, + "expansions": 7, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_query_path_competition" + }, + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ], + "id": "local-dev-relation-lineage-context", + "observed_paths": [ + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + }, + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + } + ], + "path_matched": true, + "rank": 4 + }, + { + "acceptable_rank": 3, + "cohort": "relation", + "diagnostics": { + "activation_total": 0.7844196033049897, + "active_path_count": 6, + "competition_sets": [ + { + "mean_query_relevance": 0.4264820448851425, + "message_total_after": 0.29853743141959976, + "message_total_before": 0.29853743141959976, + "neighbor_count": 1, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 1 + }, + { + "mean_query_relevance": 0.4264820448851425, + "message_total_after": 0.14926871570979988, + "message_total_before": 0.14926871570979988, + "neighbor_count": 1, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "step": 2 + }, + { + "mean_query_relevance": 0.4187060893458822, + "message_total_after": 0.23588217188539007, + "message_total_before": 0.26249882490943677, + "neighbor_count": 3, + "path_identity": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs>w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "step": 2 + } + ], + "converged": false, + "expansions": 5, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_query_path_competition" + }, + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ], + "id": "local-dev-relation-surface-lineage", + "observed_paths": [ + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + }, + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + } + ], + "path_matched": true, + "rank": 2 + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "diagnostics": { + "activation_total": 0.25, + "active_path_count": 1, + "competition_sets": [], + "converged": false, + "expansions": 0, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_query_path_competition" + }, + "expected_path": [], + "id": "local-dev-negative-l1-brake", + "observed_paths": [ + { + "seed_id": "w:6Gdu5NFP9nRML71rqJhTN8hhTMS72n5_vVP0at6lR7c", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "diagnostics": { + "activation_total": 0.25, + "active_path_count": 1, + "competition_sets": [], + "converged": false, + "expansions": 0, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_query_path_competition" + }, + "expected_path": [], + "id": "local-dev-negative-evaluation", + "observed_paths": [ + { + "seed_id": "w:mlSfgBoCoxtwg7mTM20GRuEGdrXQ6a2jMnXPcWGCgD4", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + }, + { + "acceptable_rank": 3, + "cohort": "negative_control", + "diagnostics": { + "activation_total": 0.537488070614367, + "active_path_count": 3, + "competition_sets": [ + { + "mean_query_relevance": 0.41069724373481004, + "message_total_after": 0.287488070614367, + "message_total_before": 0.287488070614367, + "neighbor_count": 1, + "path_identity": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "source_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "step": 1 + }, + { + "mean_query_relevance": 0.41069724373481004, + "message_total_after": 0.1437440353071835, + "message_total_before": 0.1437440353071835, + "neighbor_count": 1, + "path_identity": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "source_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "step": 2 + } + ], + "converged": false, + "expansions": 2, + "steps": 2, + "stop_reason": "recurrent_step_limit", + "strategy": "local_neighbor_query_path_competition" + }, + "expected_path": [], + "id": "local-dev-negative-memory-graphrag", + "observed_paths": [ + { + "seed_id": "w:X5pZDGRW97N7fQmNXB0CHfbnD6_i-vd8AkHa4rGoNew", + "steps": [] + } + ], + "path_matched": null, + "rank": 1 + } + ], + "diagnostics": { + "competition_set_count": 23, + "mean_activation_total": 0.7596897132982412, + "mean_active_path_count": 5.555555555555555, + "mean_expansions": 4.555555555555555, + "mean_steps": 2.0, + "stop_reasons": { + "recurrent_step_limit": 9 + } + }, + "explanations": [ + { + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "id": "local-dev-relation-prototype-judgment", + "matched": true, + "observed_paths": [ + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + }, + { + "seed_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ] + } + ] + }, + { + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ], + "id": "local-dev-relation-lineage-context", + "matched": true, + "observed_paths": [ + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + }, + { + "seed_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA", + "target_id": "w:RlanesfrOVafoOsmtS5j43xMQt08JpXlYPyJ0mi50so" + } + ] + } + ] + }, + { + "expected_path": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ], + "id": "local-dev-relation-surface-lineage", + "matched": true, + "observed_paths": [ + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + }, + { + "seed_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "steps": [ + { + "edge_type": "mention", + "source_id": "w:5fBFc2M4h3kQx3zMkvhJu4dXZD6z5tx59Vq22KsfFJs", + "target_id": "w:wI4myc2VjQ9mM1HuvQL2ujeW1jQnOqgCtB5fq67PDSA" + } + ] + } + ] + } + ], + "family": "local_neighbor_query_path_competition", + "feedback": { + "case_id": "local-dev-relation-prototype-judgment", + "changed_edges": [ + { + "edge_type": "mention", + "new_weight": 1.0500938511199904, + "old_weight": 1.0, + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "credited_edges": [ + { + "edge_type": "mention", + "new_weight": 1.0500938511199904, + "old_weight": 1.0, + "source_id": "w:EyfdPl6m6HDEPWu-2in7YoAAJUR-3jhGKHNPS2Bxp4A", + "target_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c" + } + ], + "non_target_rank_changes": [], + "target_node_id": "w:QxkFan-dY7kVdAzoP115Pe4FbxOmCLepO4mOUoWqt4c", + "target_rank_after": 2, + "target_rank_before": 4, + "uncredited_edge_changes": [] + }, + "id": "local-neighbor-query-path", + "metrics": { + "cohorts": { + "direct_lookup": { + "cases": 3, + "hit_at_3": 1.0, + "mean_reciprocal_rank": 0.8333333333333334, + "ranks": [ + 1, + 2, + 1 + ] + }, + "negative_control": { + "cases": 3, + "hit_at_3": 1.0, + "mean_reciprocal_rank": 1.0, + "ranks": [ + 1, + 1, + 1 + ] + }, + "relation": { + "cases": 3, + "hit_at_3": 0.3333333333333333, + "mean_reciprocal_rank": 0.3333333333333333, + "ranks": [ + 4, + 4, + 2 + ] + } + }, + "overall": { + "cases": 9, + "hit_at_3": 0.7777777777777778, + "mean_reciprocal_rank": 0.7222222222222222, + "ranks": [ + 1, + 2, + 1, + 4, + 4, + 2, + 1, + 1, + 1 + ] + } + }, + "parameters": {}, + "relative_to_baselines": { + "direct_mrr_delta_from_current": -0.16666666666666663, + "negative_mrr_delta_from_current": 0.0, + "relation_mrr_delta_from_current": 0.016666666666666663, + "relation_mrr_delta_from_recurrent_balanced": -0.02777777777777779 + }, + "structural_complexity": 7 + } + ] +} diff --git a/tests/test_local_competition.py b/tests/test_local_competition.py index e0f1dd1..b2732d3 100644 --- a/tests/test_local_competition.py +++ b/tests/test_local_competition.py @@ -18,6 +18,12 @@ ) HOLDOUT = FIXTURES / "d1_liplus_local_competition_holdout.json" HOLDOUT_GOLD = FIXTURES / "d1_liplus_local_competition_holdout.gold.json" +DEVELOPMENT_RESULT = ( + FIXTURES / "d1_liplus_local_competition_experiment.development.result.json" +) +HOLDOUT_RESULT = ( + FIXTURES / "d1_liplus_local_competition_experiment.holdout.result.json" +) class LocalCompetitionFixtureTest(unittest.TestCase): @@ -192,5 +198,47 @@ def test_candidate_must_beat_both_relation_baselines(self) -> None: self.assertTrue(variants[3]["candidate_gate_passed"]) +class LocalCompetitionResultAuditTest(unittest.TestCase): + def test_development_records_all_variants_and_stops_before_holdout(self) -> None: + result = json.loads(DEVELOPMENT_RESULT.read_text(encoding="utf-8")) + manifest = read_manifest(MANIFEST) + self.assertEqual(result["schema_version"], 2) + self.assertEqual(result["variant_count"], 6) + self.assertEqual( + [variant["id"] for variant in result["variants"]], + [variant["id"] for variant in manifest["variants"]], + ) + self.assertEqual(result["selection"]["selected_variant_id"], "current") + self.assertEqual(result["selection"]["eligible_variant_ids"], []) + self.assertEqual( + result["selection"]["reason"], + "no_local_variant_passed_frozen_gate", + ) + self.assertEqual(result["holdout_status"], "not_opened_no_candidate") + self.assertFalse(HOLDOUT_RESULT.exists()) + + def test_relation_gain_does_not_hide_control_regressions(self) -> None: + result = json.loads(DEVELOPMENT_RESULT.read_text(encoding="utf-8")) + by_id = {variant["id"]: variant for variant in result["variants"]} + for variant_id in ("local-neighbor", "local-neighbor-path"): + gate = by_id[variant_id]["candidate_gate"] + self.assertTrue(gate["relation_strictly_above_current"]) + self.assertTrue(gate["relation_strictly_above_recurrent_balanced"]) + self.assertFalse(gate["direct_non_regression"]) + self.assertFalse(gate["negative_non_regression"]) + for variant_id in ( + "local-neighbor-query", + "local-neighbor-query-path", + ): + gate = by_id[variant_id]["candidate_gate"] + self.assertTrue(gate["negative_non_regression"]) + self.assertFalse(gate["direct_non_regression"]) + self.assertFalse(gate["relation_strictly_above_recurrent_balanced"]) + for variant in result["variants"]: + self.assertTrue(all(item["matched"] for item in variant["explanations"])) + self.assertEqual(variant["feedback"]["uncredited_edge_changes"], []) + self.assertEqual(variant["feedback"]["non_target_rank_changes"], []) + + if __name__ == "__main__": unittest.main()