From b309e4ec70b956d6515c0553b614b97fd6e5696c Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 18 May 2026 22:27:49 -0400 Subject: [PATCH 01/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/lens/affected_set.dag | 151 ++++++++++++++++++++++++++++++++++- 1 file changed, 147 insertions(+), 4 deletions(-) diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 273a67944c6..52a552af885 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -1,14 +1,14 @@ // src/v4/lens/affected_set.dag // Scope: canonical affected-set authority for CI/incremental re-execution; fail-closed B-4/B-5 node frontier. -// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, FrontierDecision, ReExecFrontier, AffectedSetRead. -// Consumes: Map, Diff, Node, Path, Symbol, Witness; B-4 resolved Symbol equality; B-5 per-Arrow signature effects. -// Status: scaffold; dissolves when T-21 read body, CI use, and .dag TestClaims land. +// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, FrontierDecision, ReExecFrontier, AffectedFrontierMode, AffectedSetRead, affected_set. +// Consumes: Map, Diff, Edit, Hash, Node, Path, Symbol, Witness; B1 content_hash receipts; B-4 resolved Symbol equality; B-5 per-Arrow signature effects. +// Status: Wave-0 front: read body + changed-subgraph gate landed; dissolves when CI use and .dag TestClaims land. module v4.lens.affected_set import v4.std.collection { Map } -import v4.std.node { Diff, Node, Path, Symbol } +import v4.std.node { Diff, Edit, Hash, Node, Path, Symbol } import v4.std.witness { Witness } @@ -17,6 +17,9 @@ type Dag = Node type AffectedDimension = Symbol +data affected_dimension_value: Symbol = affected_dimension_value +data affected_reason_no_hash_receipt: Symbol = affected_reason_no_hash_receipt + type ExclusionKey { at: Path @@ -28,6 +31,12 @@ type ExclusionKey { type ExclusionProof = EmptyDimensionDelta { subject: Path } | DimensionNotRead { producer: Path } + | UnchangedPureBoundary { subject: Path, content_hash: Hash } + + +type AffectedFrontierMode + = ChangedSubgraph { roots: List } + | WholeDagFailClosed { reason: Symbol } // 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed @@ -37,6 +46,7 @@ type FrontierDecision type ReExecFrontier { + mode: AffectedFrontierMode decisions: Map } @@ -44,3 +54,136 @@ type ReExecFrontier { type AffectedSetRead { read: fn(Dag, Diff) -> Witness } + + +type HashReceipt + = HashUnchanged { content_hash: Hash } + | HashChanged + | HashUnknown { reason: Symbol } + + +type BoundaryReceipt + = BoundaryPruned { proof: ExclusionProof } + | BoundaryExpands + | BoundaryUnknown { reason: Symbol } + + +type FrontierAcc { + mode: AffectedFrontierMode + decisions: Map + roots: List +} + + +fn empty_frontier_acc() -> FrontierAcc { + FrontierAcc { + mode: ChangedSubgraph { roots: [] }, + decisions: empty_map(), + roots: [] + } +} + + +fn frontier_from_acc(acc: FrontierAcc) -> ReExecFrontier { + ReExecFrontier { + mode: acc.mode, + decisions: acc.decisions + } +} + + +fn root_key(path: Path) -> ExclusionKey { + ExclusionKey { + at: path, + dimension: affected_dimension_value + } +} + + +fn mark_rerun(acc: FrontierAcc, key: ExclusionKey) -> FrontierAcc { + FrontierAcc { + mode: acc.mode, + decisions: map_insert(acc.decisions, key, Rerun), + roots: acc.roots + } +} + + +fn mark_excluded(acc: FrontierAcc, key: ExclusionKey, proof: ExclusionProof) -> FrontierAcc { + FrontierAcc { + mode: acc.mode, + decisions: map_insert(acc.decisions, key, Excluded { proof: proof }), + roots: acc.roots + } +} + + +fn mark_whole_dag(acc: FrontierAcc, reason: Symbol) -> FrontierAcc { + FrontierAcc { + mode: WholeDagFailClosed { reason: reason }, + decisions: acc.decisions, + roots: acc.roots + } +} + + +fn append_root(acc: FrontierAcc, path: Path) -> FrontierAcc { + FrontierAcc { + mode: ChangedSubgraph { roots: list_push(acc.roots, path) }, + decisions: acc.decisions, + roots: list_push(acc.roots, path) + } +} + + +fn cached_hash_receipt(dag: Dag, path: Path, replacement: Node) -> HashReceipt { + HashUnknown { reason: affected_reason_no_hash_receipt } +} + + +fn boundary_receipt(dag: Dag, path: Path, replacement: Node) -> BoundaryReceipt { + match cached_hash_receipt(dag: dag, path: path, replacement: replacement) { + HashUnchanged { content_hash: h } => + BoundaryPruned { + proof: UnchangedPureBoundary { + subject: path, + content_hash: h + } + } + HashChanged => BoundaryExpands + HashUnknown { reason: r } => BoundaryUnknown { reason: r } + } +} + + +fn seed_edit_frontier(dag: Dag, acc: FrontierAcc, edit: Edit) -> FrontierAcc { + match boundary_receipt( + dag: dag, + path: edit.at, + replacement: edit.replacement + ) { + BoundaryPruned { proof: p } => + mark_excluded(acc: acc, key: root_key(path: edit.at), proof: p) + BoundaryExpands => + append_root( + acc: mark_rerun(acc: acc, key: root_key(path: edit.at)), + path: edit.at + ) + BoundaryUnknown { reason: r } => + mark_whole_dag( + acc: mark_rerun(acc: acc, key: root_key(path: edit.at)), + reason: r + ) + } +} + + +fn affected_set(dag: Dag, diff: Diff) -> Witness { + Holds { + value: frontier_from_acc( + acc: fold(diff.edits, init: empty_frontier_acc(), f: fn(acc, edit) { + seed_edit_frontier(dag: dag, acc: acc, edit: edit) + }) + ) + } +} From 11c7cfb082b3b2f00ae7724684a4cd4d5d89dd18 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 18 May 2026 22:32:31 -0400 Subject: [PATCH 02/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/lens/affected_set.dag | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 52a552af885..419d05098bd 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -52,6 +52,7 @@ type ReExecFrontier { type AffectedSetRead { + hash_receipt: fn(Dag, Path, Node) -> HashReceipt read: fn(Dag, Diff) -> Witness } @@ -136,13 +137,8 @@ fn append_root(acc: FrontierAcc, path: Path) -> FrontierAcc { } -fn cached_hash_receipt(dag: Dag, path: Path, replacement: Node) -> HashReceipt { - HashUnknown { reason: affected_reason_no_hash_receipt } -} - - -fn boundary_receipt(dag: Dag, path: Path, replacement: Node) -> BoundaryReceipt { - match cached_hash_receipt(dag: dag, path: path, replacement: replacement) { +fn boundary_receipt(read_model: AffectedSetRead, dag: Dag, path: Path, replacement: Node) -> BoundaryReceipt { + match read_model.hash_receipt(dag, path, replacement) { HashUnchanged { content_hash: h } => BoundaryPruned { proof: UnchangedPureBoundary { @@ -156,8 +152,14 @@ fn boundary_receipt(dag: Dag, path: Path, replacement: Node) -> BoundaryReceipt } -fn seed_edit_frontier(dag: Dag, acc: FrontierAcc, edit: Edit) -> FrontierAcc { +fn seed_edit_frontier( + read_model: AffectedSetRead, + dag: Dag, + acc: FrontierAcc, + edit: Edit +) -> FrontierAcc { match boundary_receipt( + read_model: read_model, dag: dag, path: edit.at, replacement: edit.replacement @@ -178,11 +180,16 @@ fn seed_edit_frontier(dag: Dag, acc: FrontierAcc, edit: Edit) -> FrontierAcc { } -fn affected_set(dag: Dag, diff: Diff) -> Witness { +fn affected_set(read_model: AffectedSetRead, dag: Dag, diff: Diff) -> Witness { Holds { value: frontier_from_acc( acc: fold(diff.edits, init: empty_frontier_acc(), f: fn(acc, edit) { - seed_edit_frontier(dag: dag, acc: acc, edit: edit) + seed_edit_frontier( + read_model: read_model, + dag: dag, + acc: acc, + edit: edit + ) }) ) } From 3ff9faee6bdd70fce614563e961ed64972d7aa4e Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 18 May 2026 22:38:27 -0400 Subject: [PATCH 03/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/lens/affected_set.dag | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 419d05098bd..eeba36601dc 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -34,6 +34,7 @@ type ExclusionProof | UnchangedPureBoundary { subject: Path, content_hash: Hash } +// 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed; IRT-1 whole/changed-subgraph gate. type AffectedFrontierMode = ChangedSubgraph { roots: List } | WholeDagFailClosed { reason: Symbol } @@ -57,12 +58,14 @@ type AffectedSetRead { } +// 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves into B1 content_hash read receipts. type HashReceipt = HashUnchanged { content_hash: Hash } | HashChanged | HashUnknown { reason: Symbol } +// 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when T-21 .dag TestClaims prove boundary cases. type BoundaryReceipt = BoundaryPruned { proof: ExclusionProof } | BoundaryExpands @@ -129,10 +132,19 @@ fn mark_whole_dag(acc: FrontierAcc, reason: Symbol) -> FrontierAcc { fn append_root(acc: FrontierAcc, path: Path) -> FrontierAcc { - FrontierAcc { - mode: ChangedSubgraph { roots: list_push(acc.roots, path) }, - decisions: acc.decisions, - roots: list_push(acc.roots, path) + match acc.mode { + WholeDagFailClosed { reason: r } => + FrontierAcc { + mode: WholeDagFailClosed { reason: r }, + decisions: acc.decisions, + roots: list_push(acc.roots, path) + } + ChangedSubgraph { roots: _ } => + FrontierAcc { + mode: ChangedSubgraph { roots: list_push(acc.roots, path) }, + decisions: acc.decisions, + roots: list_push(acc.roots, path) + } } } From 1e15af237ad92bc28a5789c3c6a385fc3def0aa2 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 18 May 2026 22:59:28 -0400 Subject: [PATCH 04/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/lens/affected_set.dag | 1 - 1 file changed, 1 deletion(-) diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index eeba36601dc..a1d4c53db6e 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -18,7 +18,6 @@ type Dag = Node type AffectedDimension = Symbol data affected_dimension_value: Symbol = affected_dimension_value -data affected_reason_no_hash_receipt: Symbol = affected_reason_no_hash_receipt type ExclusionKey { From ac7a96039bfc55b54eb41d7b9aa5a9f84ec197c4 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 18 May 2026 23:02:39 -0400 Subject: [PATCH 05/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/lens/affected_set.dag | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index a1d4c53db6e..216014fc620 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -53,7 +53,6 @@ type ReExecFrontier { type AffectedSetRead { hash_receipt: fn(Dag, Path, Node) -> HashReceipt - read: fn(Dag, Diff) -> Witness } @@ -74,15 +73,13 @@ type BoundaryReceipt type FrontierAcc { mode: AffectedFrontierMode decisions: Map - roots: List } fn empty_frontier_acc() -> FrontierAcc { FrontierAcc { mode: ChangedSubgraph { roots: [] }, - decisions: empty_map(), - roots: [] + decisions: empty_map() } } @@ -106,8 +103,7 @@ fn root_key(path: Path) -> ExclusionKey { fn mark_rerun(acc: FrontierAcc, key: ExclusionKey) -> FrontierAcc { FrontierAcc { mode: acc.mode, - decisions: map_insert(acc.decisions, key, Rerun), - roots: acc.roots + decisions: map_insert(acc.decisions, key, Rerun) } } @@ -115,8 +111,7 @@ fn mark_rerun(acc: FrontierAcc, key: ExclusionKey) -> FrontierAcc { fn mark_excluded(acc: FrontierAcc, key: ExclusionKey, proof: ExclusionProof) -> FrontierAcc { FrontierAcc { mode: acc.mode, - decisions: map_insert(acc.decisions, key, Excluded { proof: proof }), - roots: acc.roots + decisions: map_insert(acc.decisions, key, Excluded { proof: proof }) } } @@ -124,8 +119,7 @@ fn mark_excluded(acc: FrontierAcc, key: ExclusionKey, proof: ExclusionProof) -> fn mark_whole_dag(acc: FrontierAcc, reason: Symbol) -> FrontierAcc { FrontierAcc { mode: WholeDagFailClosed { reason: reason }, - decisions: acc.decisions, - roots: acc.roots + decisions: acc.decisions } } @@ -135,14 +129,12 @@ fn append_root(acc: FrontierAcc, path: Path) -> FrontierAcc { WholeDagFailClosed { reason: r } => FrontierAcc { mode: WholeDagFailClosed { reason: r }, - decisions: acc.decisions, - roots: list_push(acc.roots, path) + decisions: acc.decisions } - ChangedSubgraph { roots: _ } => + ChangedSubgraph { roots: roots } => FrontierAcc { - mode: ChangedSubgraph { roots: list_push(acc.roots, path) }, - decisions: acc.decisions, - roots: list_push(acc.roots, path) + mode: ChangedSubgraph { roots: list_push(roots, path) }, + decisions: acc.decisions } } } From a284b65b9e86e98ced9d91d7d8778ed647b3c13a Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 18 May 2026 23:23:18 -0400 Subject: [PATCH 06/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/lens/affected_set.dag | 60 ++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 216014fc620..99ec50b34b7 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -2,7 +2,7 @@ // Scope: canonical affected-set authority for CI/incremental re-execution; fail-closed B-4/B-5 node frontier. // Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, FrontierDecision, ReExecFrontier, AffectedFrontierMode, AffectedSetRead, affected_set. // Consumes: Map, Diff, Edit, Hash, Node, Path, Symbol, Witness; B1 content_hash receipts; B-4 resolved Symbol equality; B-5 per-Arrow signature effects. -// Status: Wave-0 front: read body + changed-subgraph gate landed; dissolves when CI use and .dag TestClaims land. +// Status: Wave-0 front: changed-subgraph frontier expansion landed; dissolves when CI use and .dag TestClaims land. module v4.lens.affected_set @@ -26,6 +26,13 @@ type ExclusionKey { } +type FrontierEdge { + producer: Path + consumer: Path + dimension: AffectedDimension +} + + // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed type ExclusionProof = EmptyDimensionDelta { subject: Path } @@ -53,6 +60,8 @@ type ReExecFrontier { type AffectedSetRead { hash_receipt: fn(Dag, Path, Node) -> HashReceipt + downstream_edges: fn(Dag, Path) -> List + propagation_receipt: fn(Dag, FrontierEdge) -> PropagationReceipt } @@ -70,6 +79,13 @@ type BoundaryReceipt | BoundaryUnknown { reason: Symbol } +// 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when B-4/B-5 edge receipts are generated. +type PropagationReceipt + = Propagates + | DoesNotPropagate { proof: ExclusionProof } + | PropagationUnknown { reason: Symbol } + + type FrontierAcc { mode: AffectedFrontierMode decisions: Map @@ -100,6 +116,14 @@ fn root_key(path: Path) -> ExclusionKey { } +fn edge_key(edge: FrontierEdge) -> ExclusionKey { + ExclusionKey { + at: edge.consumer, + dimension: edge.dimension + } +} + + fn mark_rerun(acc: FrontierAcc, key: ExclusionKey) -> FrontierAcc { FrontierAcc { mode: acc.mode, @@ -155,6 +179,31 @@ fn boundary_receipt(read_model: AffectedSetRead, dag: Dag, path: Path, replaceme } +fn expand_path(read_model: AffectedSetRead, dag: Dag, acc: FrontierAcc, path: Path) -> FrontierAcc { + fold(read_model.downstream_edges(dag, path), init: acc, f: fn(acc2, edge) { + match read_model.propagation_receipt(dag, edge) { + Propagates => + expand_path( + read_model: read_model, + dag: dag, + acc: append_root( + acc: mark_rerun(acc: acc2, key: edge_key(edge: edge)), + path: edge.consumer + ), + path: edge.consumer + ) + DoesNotPropagate { proof: p } => + mark_excluded(acc: acc2, key: edge_key(edge: edge), proof: p) + PropagationUnknown { reason: r } => + mark_whole_dag( + acc: mark_rerun(acc: acc2, key: edge_key(edge: edge)), + reason: r + ) + } + }) +} + + fn seed_edit_frontier( read_model: AffectedSetRead, dag: Dag, @@ -170,8 +219,13 @@ fn seed_edit_frontier( BoundaryPruned { proof: p } => mark_excluded(acc: acc, key: root_key(path: edit.at), proof: p) BoundaryExpands => - append_root( - acc: mark_rerun(acc: acc, key: root_key(path: edit.at)), + expand_path( + read_model: read_model, + dag: dag, + acc: append_root( + acc: mark_rerun(acc: acc, key: root_key(path: edit.at)), + path: edit.at + ), path: edit.at ) BoundaryUnknown { reason: r } => From 98acfe347829a5ef0def4f221fac6761bfa7fe34 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 18 May 2026 23:26:26 -0400 Subject: [PATCH 07/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/lens/affected_set.dag | 98 ++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 99ec50b34b7..a07d92c25d4 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -1,6 +1,6 @@ // src/v4/lens/affected_set.dag // Scope: canonical affected-set authority for CI/incremental re-execution; fail-closed B-4/B-5 node frontier. -// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, FrontierDecision, ReExecFrontier, AffectedFrontierMode, AffectedSetRead, affected_set. +// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, FrontierDecision, ReExecFrontier, AffectedSetRead, affected_set. // Consumes: Map, Diff, Edit, Hash, Node, Path, Symbol, Witness; B1 content_hash receipts; B-4 resolved Symbol equality; B-5 per-Arrow signature effects. // Status: Wave-0 front: changed-subgraph frontier expansion landed; dissolves when CI use and .dag TestClaims land. @@ -40,27 +40,24 @@ type ExclusionProof | UnchangedPureBoundary { subject: Path, content_hash: Hash } -// 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed; IRT-1 whole/changed-subgraph gate. -type AffectedFrontierMode - = ChangedSubgraph { roots: List } - | WholeDagFailClosed { reason: Symbol } - - // 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed type FrontierDecision = Rerun | Excluded { proof: ExclusionProof } -type ReExecFrontier { - mode: AffectedFrontierMode - decisions: Map -} +// 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed; IRT-1 whole/changed-subgraph gate. +type ReExecFrontier + = ChangedSubgraphFrontier { + roots: List, + decisions: Map + } + | WholeDagFailClosed { reason: Symbol } type AffectedSetRead { hash_receipt: fn(Dag, Path, Node) -> HashReceipt - downstream_edges: fn(Dag, Path) -> List + frontier_edges: fn(Dag, Path) -> List propagation_receipt: fn(Dag, FrontierEdge) -> PropagationReceipt } @@ -86,24 +83,31 @@ type PropagationReceipt | PropagationUnknown { reason: Symbol } -type FrontierAcc { - mode: AffectedFrontierMode - decisions: Map -} +// 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed; accumulator mirrors ReExecFrontier authority. +type FrontierAcc + = ChangedSubgraphAcc { + roots: List, + decisions: Map + } + | WholeDagAcc { reason: Symbol } fn empty_frontier_acc() -> FrontierAcc { - FrontierAcc { - mode: ChangedSubgraph { roots: [] }, + ChangedSubgraphAcc { + roots: [], decisions: empty_map() } } fn frontier_from_acc(acc: FrontierAcc) -> ReExecFrontier { - ReExecFrontier { - mode: acc.mode, - decisions: acc.decisions + match acc { + ChangedSubgraphAcc { roots: roots, decisions: decisions } => + ChangedSubgraphFrontier { + roots: roots, + decisions: decisions + } + WholeDagAcc { reason: r } => WholeDagFailClosed { reason: r } } } @@ -125,40 +129,41 @@ fn edge_key(edge: FrontierEdge) -> ExclusionKey { fn mark_rerun(acc: FrontierAcc, key: ExclusionKey) -> FrontierAcc { - FrontierAcc { - mode: acc.mode, - decisions: map_insert(acc.decisions, key, Rerun) + match acc { + WholeDagAcc { reason: r } => WholeDagAcc { reason: r } + ChangedSubgraphAcc { roots: roots, decisions: decisions } => + ChangedSubgraphAcc { + roots: roots, + decisions: map_insert(decisions, key, Rerun) + } } } fn mark_excluded(acc: FrontierAcc, key: ExclusionKey, proof: ExclusionProof) -> FrontierAcc { - FrontierAcc { - mode: acc.mode, - decisions: map_insert(acc.decisions, key, Excluded { proof: proof }) + match acc { + WholeDagAcc { reason: r } => WholeDagAcc { reason: r } + ChangedSubgraphAcc { roots: roots, decisions: decisions } => + ChangedSubgraphAcc { + roots: roots, + decisions: map_insert(decisions, key, Excluded { proof: proof }) + } } } fn mark_whole_dag(acc: FrontierAcc, reason: Symbol) -> FrontierAcc { - FrontierAcc { - mode: WholeDagFailClosed { reason: reason }, - decisions: acc.decisions - } + WholeDagAcc { reason: reason } } fn append_root(acc: FrontierAcc, path: Path) -> FrontierAcc { - match acc.mode { - WholeDagFailClosed { reason: r } => - FrontierAcc { - mode: WholeDagFailClosed { reason: r }, - decisions: acc.decisions - } - ChangedSubgraph { roots: roots } => - FrontierAcc { - mode: ChangedSubgraph { roots: list_push(roots, path) }, - decisions: acc.decisions + match acc { + WholeDagAcc { reason: r } => WholeDagAcc { reason: r } + ChangedSubgraphAcc { roots: roots, decisions: decisions } => + ChangedSubgraphAcc { + roots: list_push(roots, path), + decisions: decisions } } } @@ -180,16 +185,11 @@ fn boundary_receipt(read_model: AffectedSetRead, dag: Dag, path: Path, replaceme fn expand_path(read_model: AffectedSetRead, dag: Dag, acc: FrontierAcc, path: Path) -> FrontierAcc { - fold(read_model.downstream_edges(dag, path), init: acc, f: fn(acc2, edge) { + fold(read_model.frontier_edges(dag, path), init: acc, f: fn(acc2, edge) { match read_model.propagation_receipt(dag, edge) { Propagates => - expand_path( - read_model: read_model, - dag: dag, - acc: append_root( - acc: mark_rerun(acc: acc2, key: edge_key(edge: edge)), - path: edge.consumer - ), + append_root( + acc: mark_rerun(acc: acc2, key: edge_key(edge: edge)), path: edge.consumer ) DoesNotPropagate { proof: p } => From 69bce37618de716eb9d51661e15521f1be148cd6 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Mon, 18 May 2026 23:53:29 -0400 Subject: [PATCH 08/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/lens/affected_set.dag | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index a07d92c25d4..80b0e9fc21e 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -2,7 +2,7 @@ // Scope: canonical affected-set authority for CI/incremental re-execution; fail-closed B-4/B-5 node frontier. // Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, FrontierDecision, ReExecFrontier, AffectedSetRead, affected_set. // Consumes: Map, Diff, Edit, Hash, Node, Path, Symbol, Witness; B1 content_hash receipts; B-4 resolved Symbol equality; B-5 per-Arrow signature effects. -// Status: Wave-0 front: changed-subgraph frontier expansion landed; dissolves when CI use and .dag TestClaims land. +// Status: Wave-0 front: bounded changed-subgraph frontier fold landed; dissolves when CI use and .dag TestClaims land. module v4.lens.affected_set @@ -57,7 +57,7 @@ type ReExecFrontier type AffectedSetRead { hash_receipt: fn(Dag, Path, Node) -> HashReceipt - frontier_edges: fn(Dag, Path) -> List + changed_subgraph_frontier_edges: fn(Dag, Path) -> List propagation_receipt: fn(Dag, FrontierEdge) -> PropagationReceipt } @@ -185,7 +185,7 @@ fn boundary_receipt(read_model: AffectedSetRead, dag: Dag, path: Path, replaceme fn expand_path(read_model: AffectedSetRead, dag: Dag, acc: FrontierAcc, path: Path) -> FrontierAcc { - fold(read_model.frontier_edges(dag, path), init: acc, f: fn(acc2, edge) { + fold(read_model.changed_subgraph_frontier_edges(dag, path), init: acc, f: fn(acc2, edge) { match read_model.propagation_receipt(dag, edge) { Propagates => append_root( From f6d6c3a13f1830aa40bcccc76965419480ce191a Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 00:17:27 -0400 Subject: [PATCH 09/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index 3f3595f7496..db38d22534e 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the eventual `AffectedSetRead.read` body must prune at unchanged/pure `content_hash` boundaries, expand the frontier as a pure parallel fold over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 read body and `.dag` TestClaims prove the exclusion evidence cases. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`hash_receipt`, `changed_subgraph_frontier_edges`, `propagation_receipt`) plus top-level `affected_set(read_model, dag, diff)` must prune at unchanged/pure `content_hash` boundaries, fold the bounded changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | From a22d65f74671a9ad151e0ee1d7f7fc68b391b08b Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 05:31:35 +0000 Subject: [PATCH 10/48] Unify affected frontier rerun authority --- src/v4/lens/affected_set.dag | 34 +++++----------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 80b0e9fc21e..8c582a0e641 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -49,7 +49,6 @@ type FrontierDecision // 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed; IRT-1 whole/changed-subgraph gate. type ReExecFrontier = ChangedSubgraphFrontier { - roots: List, decisions: Map } | WholeDagFailClosed { reason: Symbol } @@ -86,7 +85,6 @@ type PropagationReceipt // 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed; accumulator mirrors ReExecFrontier authority. type FrontierAcc = ChangedSubgraphAcc { - roots: List, decisions: Map } | WholeDagAcc { reason: Symbol } @@ -94,7 +92,6 @@ type FrontierAcc fn empty_frontier_acc() -> FrontierAcc { ChangedSubgraphAcc { - roots: [], decisions: empty_map() } } @@ -102,9 +99,8 @@ fn empty_frontier_acc() -> FrontierAcc { fn frontier_from_acc(acc: FrontierAcc) -> ReExecFrontier { match acc { - ChangedSubgraphAcc { roots: roots, decisions: decisions } => + ChangedSubgraphAcc { decisions: decisions } => ChangedSubgraphFrontier { - roots: roots, decisions: decisions } WholeDagAcc { reason: r } => WholeDagFailClosed { reason: r } @@ -131,9 +127,8 @@ fn edge_key(edge: FrontierEdge) -> ExclusionKey { fn mark_rerun(acc: FrontierAcc, key: ExclusionKey) -> FrontierAcc { match acc { WholeDagAcc { reason: r } => WholeDagAcc { reason: r } - ChangedSubgraphAcc { roots: roots, decisions: decisions } => + ChangedSubgraphAcc { decisions: decisions } => ChangedSubgraphAcc { - roots: roots, decisions: map_insert(decisions, key, Rerun) } } @@ -143,9 +138,8 @@ fn mark_rerun(acc: FrontierAcc, key: ExclusionKey) -> FrontierAcc { fn mark_excluded(acc: FrontierAcc, key: ExclusionKey, proof: ExclusionProof) -> FrontierAcc { match acc { WholeDagAcc { reason: r } => WholeDagAcc { reason: r } - ChangedSubgraphAcc { roots: roots, decisions: decisions } => + ChangedSubgraphAcc { decisions: decisions } => ChangedSubgraphAcc { - roots: roots, decisions: map_insert(decisions, key, Excluded { proof: proof }) } } @@ -157,18 +151,6 @@ fn mark_whole_dag(acc: FrontierAcc, reason: Symbol) -> FrontierAcc { } -fn append_root(acc: FrontierAcc, path: Path) -> FrontierAcc { - match acc { - WholeDagAcc { reason: r } => WholeDagAcc { reason: r } - ChangedSubgraphAcc { roots: roots, decisions: decisions } => - ChangedSubgraphAcc { - roots: list_push(roots, path), - decisions: decisions - } - } -} - - fn boundary_receipt(read_model: AffectedSetRead, dag: Dag, path: Path, replacement: Node) -> BoundaryReceipt { match read_model.hash_receipt(dag, path, replacement) { HashUnchanged { content_hash: h } => @@ -188,10 +170,7 @@ fn expand_path(read_model: AffectedSetRead, dag: Dag, acc: FrontierAcc, path: Pa fold(read_model.changed_subgraph_frontier_edges(dag, path), init: acc, f: fn(acc2, edge) { match read_model.propagation_receipt(dag, edge) { Propagates => - append_root( - acc: mark_rerun(acc: acc2, key: edge_key(edge: edge)), - path: edge.consumer - ) + mark_rerun(acc: acc2, key: edge_key(edge: edge)) DoesNotPropagate { proof: p } => mark_excluded(acc: acc2, key: edge_key(edge: edge), proof: p) PropagationUnknown { reason: r } => @@ -222,10 +201,7 @@ fn seed_edit_frontier( expand_path( read_model: read_model, dag: dag, - acc: append_root( - acc: mark_rerun(acc: acc, key: root_key(path: edit.at)), - path: edit.at - ), + acc: mark_rerun(acc: acc, key: root_key(path: edit.at)), path: edit.at ) BoundaryUnknown { reason: r } => From 8ea7b037388931e3355151f29afba6d0e3a97329 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 03:32:07 -0400 Subject: [PATCH 11/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 25 ++++++++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index db38d22534e..b1346005dba 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`hash_receipt`, `changed_subgraph_frontier_edges`, `propagation_receipt`) plus top-level `affected_set(read_model, dag, diff)` must prune at unchanged/pure `content_hash` boundaries, fold the bounded changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, `changed_subgraph_frontier_edges`, `propagation_receipt`) plus top-level `affected_set(read_model, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, fold the bounded changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 8c582a0e641..b3aa45cd664 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -55,15 +55,15 @@ type ReExecFrontier type AffectedSetRead { - hash_receipt: fn(Dag, Path, Node) -> HashReceipt + boundary_hash_receipt: fn(Dag, Path, Node) -> HashReceipt changed_subgraph_frontier_edges: fn(Dag, Path) -> List propagation_receipt: fn(Dag, FrontierEdge) -> PropagationReceipt } -// 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves into B1 content_hash read receipts. +// 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves into B1 content_hash plus B-5 purity/effect receipts. type HashReceipt - = HashUnchanged { content_hash: Hash } + = HashUnchangedPure { content_hash: Hash } | HashChanged | HashUnknown { reason: Symbol } @@ -139,8 +139,19 @@ fn mark_excluded(acc: FrontierAcc, key: ExclusionKey, proof: ExclusionProof) -> match acc { WholeDagAcc { reason: r } => WholeDagAcc { reason: r } ChangedSubgraphAcc { decisions: decisions } => - ChangedSubgraphAcc { - decisions: map_insert(decisions, key, Excluded { proof: proof }) + match map_get(decisions, key) { + Some { value: Rerun } => + ChangedSubgraphAcc { + decisions: decisions + } + Some { value: Excluded { proof: _ } } => + ChangedSubgraphAcc { + decisions: map_insert(decisions, key, Excluded { proof: proof }) + } + None => + ChangedSubgraphAcc { + decisions: map_insert(decisions, key, Excluded { proof: proof }) + } } } } @@ -152,8 +163,8 @@ fn mark_whole_dag(acc: FrontierAcc, reason: Symbol) -> FrontierAcc { fn boundary_receipt(read_model: AffectedSetRead, dag: Dag, path: Path, replacement: Node) -> BoundaryReceipt { - match read_model.hash_receipt(dag, path, replacement) { - HashUnchanged { content_hash: h } => + match read_model.boundary_hash_receipt(dag, path, replacement) { + HashUnchangedPure { content_hash: h } => BoundaryPruned { proof: UnchangedPureBoundary { subject: path, From 8d64ff8a33071ad7e5cc5a5e2265c1390fbb3cb5 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 08:01:32 +0000 Subject: [PATCH 12/48] Name transitive affected frontier receipt --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index b1346005dba..c120c6419da 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, `changed_subgraph_frontier_edges`, `propagation_receipt`) plus top-level `affected_set(read_model, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, fold the bounded changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus top-level `affected_set(read_model, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, fold a proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index b3aa45cd664..b7bf20b5a31 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -56,7 +56,7 @@ type ReExecFrontier type AffectedSetRead { boundary_hash_receipt: fn(Dag, Path, Node) -> HashReceipt - changed_subgraph_frontier_edges: fn(Dag, Path) -> List + proven_transitive_changed_subgraph_frontier: fn(Dag, Path) -> List propagation_receipt: fn(Dag, FrontierEdge) -> PropagationReceipt } @@ -178,7 +178,7 @@ fn boundary_receipt(read_model: AffectedSetRead, dag: Dag, path: Path, replaceme fn expand_path(read_model: AffectedSetRead, dag: Dag, acc: FrontierAcc, path: Path) -> FrontierAcc { - fold(read_model.changed_subgraph_frontier_edges(dag, path), init: acc, f: fn(acc2, edge) { + fold(read_model.proven_transitive_changed_subgraph_frontier(dag, path), init: acc, f: fn(acc2, edge) { match read_model.propagation_receipt(dag, edge) { Propagates => mark_rerun(acc: acc2, key: edge_key(edge: edge)) From 0918255773c6f110c8c9411f35aff880dec18a50 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 04:25:59 -0400 Subject: [PATCH 13/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index c120c6419da..6e04f2eb33b 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus top-level `affected_set(read_model, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, fold a proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus top-level `affected_set(read_model, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, fold a proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions. `FrontierAcc` is 🟒 terminal internal fold state because it mirrors `ReExecFrontier` authority during ordered `Diff` accumulation and lowers directly through `frontier_from_acc`. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | From 6f237e09ac185cab17173f7d3d53c54e1d6236f3 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 04:51:42 -0400 Subject: [PATCH 14/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 62 ++++++++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index ed31ef90894..6f15b05f9fb 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus top-level `affected_set(read_model, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, fold a proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions. `FrontierAcc` is 🟒 terminal internal fold state because it mirrors `ReExecFrontier` authority during ordered `Diff` accumulation and lowers directly through `frontier_from_acc`. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus top-level `affected_set(read_model, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions. `FrontierAcc` is 🟒 terminal internal fold state because it mirrors `ReExecFrontier` authority during ordered `Diff` accumulation and lowers directly through `frontier_from_acc`. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index b7bf20b5a31..d4d1f1b2be4 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -1,6 +1,6 @@ // src/v4/lens/affected_set.dag // Scope: canonical affected-set authority for CI/incremental re-execution; fail-closed B-4/B-5 node frontier. -// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, FrontierDecision, ReExecFrontier, AffectedSetRead, affected_set. +// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, FrontierDecision, ReExecFrontier, AffectedSetRead, ChangedDimensionReceipt, affected_set. // Consumes: Map, Diff, Edit, Hash, Node, Path, Symbol, Witness; B1 content_hash receipts; B-4 resolved Symbol equality; B-5 per-Arrow signature effects. // Status: Wave-0 front: bounded changed-subgraph frontier fold landed; dissolves when CI use and .dag TestClaims land. @@ -56,8 +56,9 @@ type ReExecFrontier type AffectedSetRead { boundary_hash_receipt: fn(Dag, Path, Node) -> HashReceipt - proven_transitive_changed_subgraph_frontier: fn(Dag, Path) -> List - propagation_receipt: fn(Dag, FrontierEdge) -> PropagationReceipt + changed_dimension_receipt: fn(Dag, Edit) -> ChangedDimensionReceipt + proven_transitive_changed_subgraph_frontier: fn(Dag, Edit, AffectedDimension) -> List + propagation_receipt: fn(Dag, Edit, FrontierEdge) -> PropagationReceipt } @@ -75,6 +76,12 @@ type BoundaryReceipt | BoundaryUnknown { reason: Symbol } +// 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when per-edit dimension deltas are generated. +type ChangedDimensionReceipt + = ChangedDimensions { dimensions: List } + | ChangedDimensionsUnknown { reason: Symbol } + + // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when B-4/B-5 edge receipts are generated. type PropagationReceipt = Propagates @@ -108,10 +115,10 @@ fn frontier_from_acc(acc: FrontierAcc) -> ReExecFrontier { } -fn root_key(path: Path) -> ExclusionKey { +fn root_key(path: Path, dimension: AffectedDimension) -> ExclusionKey { ExclusionKey { at: path, - dimension: affected_dimension_value + dimension: dimension } } @@ -177,9 +184,15 @@ fn boundary_receipt(read_model: AffectedSetRead, dag: Dag, path: Path, replaceme } -fn expand_path(read_model: AffectedSetRead, dag: Dag, acc: FrontierAcc, path: Path) -> FrontierAcc { - fold(read_model.proven_transitive_changed_subgraph_frontier(dag, path), init: acc, f: fn(acc2, edge) { - match read_model.propagation_receipt(dag, edge) { +fn expand_dimension( + read_model: AffectedSetRead, + dag: Dag, + acc: FrontierAcc, + edit: Edit, + dimension: AffectedDimension +) -> FrontierAcc { + fold(read_model.proven_transitive_changed_subgraph_frontier(dag, edit, dimension), init: acc, f: fn(acc2, edge) { + match read_model.propagation_receipt(dag, edit, edge) { Propagates => mark_rerun(acc: acc2, key: edge_key(edge: edge)) DoesNotPropagate { proof: p } => @@ -194,6 +207,29 @@ fn expand_path(read_model: AffectedSetRead, dag: Dag, acc: FrontierAcc, path: Pa } +fn expand_changed_dimensions( + read_model: AffectedSetRead, + dag: Dag, + acc: FrontierAcc, + edit: Edit +) -> FrontierAcc { + match read_model.changed_dimension_receipt(dag, edit) { + ChangedDimensions { dimensions: dimensions } => + fold(dimensions, init: acc, f: fn(acc2, dimension) { + expand_dimension( + read_model: read_model, + dag: dag, + acc: mark_rerun(acc: acc2, key: root_key(path: edit.at, dimension: dimension)), + edit: edit, + dimension: dimension + ) + }) + ChangedDimensionsUnknown { reason: r } => + mark_whole_dag(acc: acc, reason: r) + } +} + + fn seed_edit_frontier( read_model: AffectedSetRead, dag: Dag, @@ -207,17 +243,17 @@ fn seed_edit_frontier( replacement: edit.replacement ) { BoundaryPruned { proof: p } => - mark_excluded(acc: acc, key: root_key(path: edit.at), proof: p) + mark_excluded(acc: acc, key: root_key(path: edit.at, dimension: affected_dimension_value), proof: p) BoundaryExpands => - expand_path( + expand_changed_dimensions( read_model: read_model, dag: dag, - acc: mark_rerun(acc: acc, key: root_key(path: edit.at)), - path: edit.at + acc: acc, + edit: edit ) BoundaryUnknown { reason: r } => mark_whole_dag( - acc: mark_rerun(acc: acc, key: root_key(path: edit.at)), + acc: acc, reason: r ) } From fd20dacffc2374d1c93e2e97e9881b73101abb13 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 04:54:26 -0400 Subject: [PATCH 15/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/lens/affected_set.dag | 57 ++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index d4d1f1b2be4..c1ac95eec28 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -33,6 +33,18 @@ type FrontierEdge { } +type EditDimension { + edit: Edit + dimension: AffectedDimension +} + + +type EditFrontierEdge { + edit: Edit + edge: FrontierEdge +} + + // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed type ExclusionProof = EmptyDimensionDelta { subject: Path } @@ -54,14 +66,6 @@ type ReExecFrontier | WholeDagFailClosed { reason: Symbol } -type AffectedSetRead { - boundary_hash_receipt: fn(Dag, Path, Node) -> HashReceipt - changed_dimension_receipt: fn(Dag, Edit) -> ChangedDimensionReceipt - proven_transitive_changed_subgraph_frontier: fn(Dag, Edit, AffectedDimension) -> List - propagation_receipt: fn(Dag, Edit, FrontierEdge) -> PropagationReceipt -} - - // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves into B1 content_hash plus B-5 purity/effect receipts. type HashReceipt = HashUnchangedPure { content_hash: Hash } @@ -89,6 +93,29 @@ type PropagationReceipt | PropagationUnknown { reason: Symbol } +type AffectedBoundaryRead { + boundary_hash_receipt: fn(Dag, Path, Node) -> HashReceipt, + changed_dimension_receipt: fn(Dag, Edit) -> ChangedDimensionReceipt +} + + +type AffectedFrontierRead { + proven_transitive_changed_subgraph_frontier: fn(Dag, EditDimension) -> List +} + + +type AffectedPropagationRead { + propagation_receipt: fn(Dag, EditFrontierEdge) -> PropagationReceipt +} + + +type AffectedSetRead { + boundary: AffectedBoundaryRead + frontier: AffectedFrontierRead + propagation: AffectedPropagationRead +} + + // 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed; accumulator mirrors ReExecFrontier authority. type FrontierAcc = ChangedSubgraphAcc { @@ -170,7 +197,7 @@ fn mark_whole_dag(acc: FrontierAcc, reason: Symbol) -> FrontierAcc { fn boundary_receipt(read_model: AffectedSetRead, dag: Dag, path: Path, replacement: Node) -> BoundaryReceipt { - match read_model.boundary_hash_receipt(dag, path, replacement) { + match read_model.boundary.boundary_hash_receipt(dag, path, replacement) { HashUnchangedPure { content_hash: h } => BoundaryPruned { proof: UnchangedPureBoundary { @@ -191,8 +218,14 @@ fn expand_dimension( edit: Edit, dimension: AffectedDimension ) -> FrontierAcc { - fold(read_model.proven_transitive_changed_subgraph_frontier(dag, edit, dimension), init: acc, f: fn(acc2, edge) { - match read_model.propagation_receipt(dag, edit, edge) { + fold( + read_model.frontier.proven_transitive_changed_subgraph_frontier( + dag, + EditDimension { edit: edit, dimension: dimension } + ), + init: acc, + f: fn(acc2, edge) { + match read_model.propagation.propagation_receipt(dag, EditFrontierEdge { edit: edit, edge: edge }) { Propagates => mark_rerun(acc: acc2, key: edge_key(edge: edge)) DoesNotPropagate { proof: p } => @@ -213,7 +246,7 @@ fn expand_changed_dimensions( acc: FrontierAcc, edit: Edit ) -> FrontierAcc { - match read_model.changed_dimension_receipt(dag, edit) { + match read_model.boundary.changed_dimension_receipt(dag, edit) { ChangedDimensions { dimensions: dimensions } => fold(dimensions, init: acc, f: fn(acc2, dimension) { expand_dimension( From 4280cba3cc97db6325b68c0aeb9341d140ce9563 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 08:56:16 +0000 Subject: [PATCH 16/48] Fit affected read hooks to bridge --- src/v4/lens/affected_set.dag | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index c1ac95eec28..e89f0d328dd 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -104,15 +104,10 @@ type AffectedFrontierRead { } -type AffectedPropagationRead { - propagation_receipt: fn(Dag, EditFrontierEdge) -> PropagationReceipt -} - - type AffectedSetRead { boundary: AffectedBoundaryRead frontier: AffectedFrontierRead - propagation: AffectedPropagationRead + propagation_receipt: fn(Dag, EditFrontierEdge) -> PropagationReceipt } @@ -225,7 +220,7 @@ fn expand_dimension( ), init: acc, f: fn(acc2, edge) { - match read_model.propagation.propagation_receipt(dag, EditFrontierEdge { edit: edit, edge: edge }) { + match read_model.propagation_receipt(dag, EditFrontierEdge { edit: edit, edge: edge }) { Propagates => mark_rerun(acc: acc2, key: edge_key(edge: edge)) DoesNotPropagate { proof: p } => From e021b6e3e402cbd25d421df5014512886773fdce Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 06:46:03 -0400 Subject: [PATCH 17/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 66 +++++++++++++++++++++++++++++++++--- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index 6f15b05f9fb..213258a2dab 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus top-level `affected_set(read_model, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions. `FrontierAcc` is 🟒 terminal internal fold state because it mirrors `ReExecFrontier` authority during ordered `Diff` accumulation and lowers directly through `frontier_from_acc`. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `edit_dag_receipt`, `propagation_receipt`) plus top-level `affected_set(read_model, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions. `EditDagReceipt` is 🟑 scaffold; named trigger = dissolve when D1 `apply_diff`/per-edit rewrite receipts are substrate-produced. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions. `FrontierAcc` is 🟒 terminal internal fold state because it mirrors `ReExecFrontier` authority during ordered `Diff` accumulation and lowers directly through `frontier_from_acc`; `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs that frontier with the current Dag required by D1 sequential rewrite. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index e89f0d328dd..87ad801bf90 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -1,6 +1,6 @@ // src/v4/lens/affected_set.dag // Scope: canonical affected-set authority for CI/incremental re-execution; fail-closed B-4/B-5 node frontier. -// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, FrontierDecision, ReExecFrontier, AffectedSetRead, ChangedDimensionReceipt, affected_set. +// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, FrontierDecision, ReExecFrontier, AffectedSetRead, ChangedDimensionReceipt, EditDagReceipt, affected_set. // Consumes: Map, Diff, Edit, Hash, Node, Path, Symbol, Witness; B1 content_hash receipts; B-4 resolved Symbol equality; B-5 per-Arrow signature effects. // Status: Wave-0 front: bounded changed-subgraph frontier fold landed; dissolves when CI use and .dag TestClaims land. @@ -86,6 +86,12 @@ type ChangedDimensionReceipt | ChangedDimensionsUnknown { reason: Symbol } +// 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when D1 apply_diff emits per-edit Dag receipts. +type EditDagReceipt + = EditDagAdvanced { dag: Dag } + | EditDagUnknown { reason: Symbol } + + // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when B-4/B-5 edge receipts are generated. type PropagationReceipt = Propagates @@ -107,6 +113,7 @@ type AffectedFrontierRead { type AffectedSetRead { boundary: AffectedBoundaryRead frontier: AffectedFrontierRead + edit_dag_receipt: fn(Dag, Edit) -> EditDagReceipt propagation_receipt: fn(Dag, EditFrontierEdge) -> PropagationReceipt } @@ -119,6 +126,12 @@ type FrontierAcc | WholeDagAcc { reason: Symbol } +type AffectedFoldAcc { + dag: Dag + frontier: FrontierAcc +} + + fn empty_frontier_acc() -> FrontierAcc { ChangedSubgraphAcc { decisions: empty_map() @@ -126,6 +139,19 @@ fn empty_frontier_acc() -> FrontierAcc { } +fn empty_affected_fold_acc(dag: Dag) -> AffectedFoldAcc { + AffectedFoldAcc { + dag: dag, + frontier: empty_frontier_acc() + } +} + + +fn frontier_from_fold_acc(acc: AffectedFoldAcc) -> ReExecFrontier { + frontier_from_acc(acc: acc.frontier) +} + + fn frontier_from_acc(acc: FrontierAcc) -> ReExecFrontier { match acc { ChangedSubgraphAcc { decisions: decisions } => @@ -137,6 +163,37 @@ fn frontier_from_acc(acc: FrontierAcc) -> ReExecFrontier { } +fn advance_edit_dag(read_model: AffectedSetRead, acc: AffectedFoldAcc, edit: Edit, frontier: FrontierAcc) -> AffectedFoldAcc { + match read_model.edit_dag_receipt(acc.dag, edit) { + EditDagAdvanced { dag: next_dag } => + AffectedFoldAcc { + dag: next_dag, + frontier: frontier + } + EditDagUnknown { reason: r } => + AffectedFoldAcc { + dag: acc.dag, + frontier: mark_whole_dag(acc: frontier, reason: r) + } + } +} + + +fn seed_edit_fold_acc(read_model: AffectedSetRead, acc: AffectedFoldAcc, edit: Edit) -> AffectedFoldAcc { + advance_edit_dag( + read_model: read_model, + acc: acc, + edit: edit, + frontier: seed_edit_frontier( + read_model: read_model, + dag: acc.dag, + acc: acc.frontier, + edit: edit + ) + ) +} + + fn root_key(path: Path, dimension: AffectedDimension) -> ExclusionKey { ExclusionKey { at: path, @@ -290,11 +347,10 @@ fn seed_edit_frontier( fn affected_set(read_model: AffectedSetRead, dag: Dag, diff: Diff) -> Witness { Holds { - value: frontier_from_acc( - acc: fold(diff.edits, init: empty_frontier_acc(), f: fn(acc, edit) { - seed_edit_frontier( + value: frontier_from_fold_acc( + acc: fold(diff.edits, init: empty_affected_fold_acc(dag: dag), f: fn(acc, edit) { + seed_edit_fold_acc( read_model: read_model, - dag: dag, acc: acc, edit: edit ) From 85a433b033a01d47890af4f87c0d60b4aaee4abd Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 06:49:03 -0400 Subject: [PATCH 18/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 107 +++++++++++++---------------------- 2 files changed, 41 insertions(+), 68 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index 213258a2dab..5271e255bd9 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `edit_dag_receipt`, `propagation_receipt`) plus top-level `affected_set(read_model, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions. `EditDagReceipt` is 🟑 scaffold; named trigger = dissolve when D1 `apply_diff`/per-edit rewrite receipts are substrate-produced. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions. `FrontierAcc` is 🟒 terminal internal fold state because it mirrors `ReExecFrontier` authority during ordered `Diff` accumulation and lowers directly through `frontier_from_acc`; `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs that frontier with the current Dag required by D1 sequential rewrite. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus top-level `affected_set(read_model, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the post-edit Dag carried by the boundary receipt, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts and D1 post-edit Dag receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions. `FrontierAcc` is 🟒 terminal internal fold state because it mirrors `ReExecFrontier` authority during ordered `Diff` accumulation and lowers directly through `frontier_from_acc`; `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs that frontier with the current Dag required by D1 sequential rewrite. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 87ad801bf90..5b8bd5f308c 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -1,6 +1,6 @@ // src/v4/lens/affected_set.dag // Scope: canonical affected-set authority for CI/incremental re-execution; fail-closed B-4/B-5 node frontier. -// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, FrontierDecision, ReExecFrontier, AffectedSetRead, ChangedDimensionReceipt, EditDagReceipt, affected_set. +// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, FrontierDecision, ReExecFrontier, AffectedSetRead, ChangedDimensionReceipt, affected_set. // Consumes: Map, Diff, Edit, Hash, Node, Path, Symbol, Witness; B1 content_hash receipts; B-4 resolved Symbol equality; B-5 per-Arrow signature effects. // Status: Wave-0 front: bounded changed-subgraph frontier fold landed; dissolves when CI use and .dag TestClaims land. @@ -68,15 +68,15 @@ type ReExecFrontier // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves into B1 content_hash plus B-5 purity/effect receipts. type HashReceipt - = HashUnchangedPure { content_hash: Hash } - | HashChanged + = HashUnchangedPure { content_hash: Hash, dag: Dag } + | HashChanged { dag: Dag } | HashUnknown { reason: Symbol } // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when T-21 .dag TestClaims prove boundary cases. type BoundaryReceipt - = BoundaryPruned { proof: ExclusionProof } - | BoundaryExpands + = BoundaryPruned { proof: ExclusionProof, dag: Dag } + | BoundaryExpands { dag: Dag } | BoundaryUnknown { reason: Symbol } @@ -86,12 +86,6 @@ type ChangedDimensionReceipt | ChangedDimensionsUnknown { reason: Symbol } -// 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when D1 apply_diff emits per-edit Dag receipts. -type EditDagReceipt - = EditDagAdvanced { dag: Dag } - | EditDagUnknown { reason: Symbol } - - // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when B-4/B-5 edge receipts are generated. type PropagationReceipt = Propagates @@ -113,7 +107,6 @@ type AffectedFrontierRead { type AffectedSetRead { boundary: AffectedBoundaryRead frontier: AffectedFrontierRead - edit_dag_receipt: fn(Dag, Edit) -> EditDagReceipt propagation_receipt: fn(Dag, EditFrontierEdge) -> PropagationReceipt } @@ -163,37 +156,6 @@ fn frontier_from_acc(acc: FrontierAcc) -> ReExecFrontier { } -fn advance_edit_dag(read_model: AffectedSetRead, acc: AffectedFoldAcc, edit: Edit, frontier: FrontierAcc) -> AffectedFoldAcc { - match read_model.edit_dag_receipt(acc.dag, edit) { - EditDagAdvanced { dag: next_dag } => - AffectedFoldAcc { - dag: next_dag, - frontier: frontier - } - EditDagUnknown { reason: r } => - AffectedFoldAcc { - dag: acc.dag, - frontier: mark_whole_dag(acc: frontier, reason: r) - } - } -} - - -fn seed_edit_fold_acc(read_model: AffectedSetRead, acc: AffectedFoldAcc, edit: Edit) -> AffectedFoldAcc { - advance_edit_dag( - read_model: read_model, - acc: acc, - edit: edit, - frontier: seed_edit_frontier( - read_model: read_model, - dag: acc.dag, - acc: acc.frontier, - edit: edit - ) - ) -} - - fn root_key(path: Path, dimension: AffectedDimension) -> ExclusionKey { ExclusionKey { at: path, @@ -250,14 +212,15 @@ fn mark_whole_dag(acc: FrontierAcc, reason: Symbol) -> FrontierAcc { fn boundary_receipt(read_model: AffectedSetRead, dag: Dag, path: Path, replacement: Node) -> BoundaryReceipt { match read_model.boundary.boundary_hash_receipt(dag, path, replacement) { - HashUnchangedPure { content_hash: h } => + HashUnchangedPure { content_hash: h, dag: next_dag } => BoundaryPruned { proof: UnchangedPureBoundary { subject: path, content_hash: h - } + }, + dag: next_dag } - HashChanged => BoundaryExpands + HashChanged { dag: next_dag } => BoundaryExpands { dag: next_dag } HashUnknown { reason: r } => BoundaryUnknown { reason: r } } } @@ -294,16 +257,17 @@ fn expand_dimension( fn expand_changed_dimensions( read_model: AffectedSetRead, - dag: Dag, + boundary_dag: Dag, + frontier_dag: Dag, acc: FrontierAcc, edit: Edit ) -> FrontierAcc { - match read_model.boundary.changed_dimension_receipt(dag, edit) { + match read_model.boundary.changed_dimension_receipt(boundary_dag, edit) { ChangedDimensions { dimensions: dimensions } => fold(dimensions, init: acc, f: fn(acc2, dimension) { expand_dimension( read_model: read_model, - dag: dag, + dag: frontier_dag, acc: mark_rerun(acc: acc2, key: root_key(path: edit.at, dimension: dimension)), edit: edit, dimension: dimension @@ -315,32 +279,41 @@ fn expand_changed_dimensions( } -fn seed_edit_frontier( +fn seed_edit_fold_acc( read_model: AffectedSetRead, - dag: Dag, - acc: FrontierAcc, + acc: AffectedFoldAcc, edit: Edit -) -> FrontierAcc { +) -> AffectedFoldAcc { match boundary_receipt( read_model: read_model, - dag: dag, + dag: acc.dag, path: edit.at, replacement: edit.replacement ) { - BoundaryPruned { proof: p } => - mark_excluded(acc: acc, key: root_key(path: edit.at, dimension: affected_dimension_value), proof: p) - BoundaryExpands => - expand_changed_dimensions( - read_model: read_model, - dag: dag, - acc: acc, - edit: edit - ) + BoundaryPruned { proof: p, dag: next_dag } => + AffectedFoldAcc { + dag: next_dag, + frontier: mark_excluded(acc: acc.frontier, key: root_key(path: edit.at, dimension: affected_dimension_value), proof: p) + } + BoundaryExpands { dag: next_dag } => + AffectedFoldAcc { + dag: next_dag, + frontier: expand_changed_dimensions( + read_model: read_model, + boundary_dag: acc.dag, + frontier_dag: next_dag, + acc: acc.frontier, + edit: edit + ) + } BoundaryUnknown { reason: r } => - mark_whole_dag( - acc: acc, - reason: r - ) + AffectedFoldAcc { + dag: acc.dag, + frontier: mark_whole_dag( + acc: acc.frontier, + reason: r + ) + } } } From 6efcab94294f4d9549248c3efde750b1063f97bf Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 06:52:11 -0400 Subject: [PATCH 19/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 47 +++++++----------------------------- 2 files changed, 10 insertions(+), 39 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index 5271e255bd9..a11cdfd4b84 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus top-level `affected_set(read_model, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the post-edit Dag carried by the boundary receipt, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts and D1 post-edit Dag receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions. `FrontierAcc` is 🟒 terminal internal fold state because it mirrors `ReExecFrontier` authority during ordered `Diff` accumulation and lowers directly through `frontier_from_acc`; `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs that frontier with the current Dag required by D1 sequential rewrite. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_receipt`, `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus top-level `affected_set(read_model, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the post-edit Dag carried by the boundary receipt, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts and D1 post-edit Dag receipts when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions. `FrontierAcc` is 🟒 terminal internal fold state because it mirrors `ReExecFrontier` authority during ordered `Diff` accumulation and lowers directly through `frontier_from_acc`; `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs that frontier with the current Dag required by D1 sequential rewrite. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 5b8bd5f308c..6ec533cef7a 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -1,6 +1,6 @@ // src/v4/lens/affected_set.dag // Scope: canonical affected-set authority for CI/incremental re-execution; fail-closed B-4/B-5 node frontier. -// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, FrontierDecision, ReExecFrontier, AffectedSetRead, ChangedDimensionReceipt, affected_set. +// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, FrontierDecision, ReExecFrontier, AffectedSetRead, BoundaryReceipt, ChangedDimensionReceipt, affected_set. // Consumes: Map, Diff, Edit, Hash, Node, Path, Symbol, Witness; B1 content_hash receipts; B-4 resolved Symbol equality; B-5 per-Arrow signature effects. // Status: Wave-0 front: bounded changed-subgraph frontier fold landed; dissolves when CI use and .dag TestClaims land. @@ -66,13 +66,6 @@ type ReExecFrontier | WholeDagFailClosed { reason: Symbol } -// 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves into B1 content_hash plus B-5 purity/effect receipts. -type HashReceipt - = HashUnchangedPure { content_hash: Hash, dag: Dag } - | HashChanged { dag: Dag } - | HashUnknown { reason: Symbol } - - // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when T-21 .dag TestClaims prove boundary cases. type BoundaryReceipt = BoundaryPruned { proof: ExclusionProof, dag: Dag } @@ -93,20 +86,10 @@ type PropagationReceipt | PropagationUnknown { reason: Symbol } -type AffectedBoundaryRead { - boundary_hash_receipt: fn(Dag, Path, Node) -> HashReceipt, +type AffectedSetRead { + boundary_receipt: fn(Dag, Edit) -> BoundaryReceipt changed_dimension_receipt: fn(Dag, Edit) -> ChangedDimensionReceipt -} - - -type AffectedFrontierRead { proven_transitive_changed_subgraph_frontier: fn(Dag, EditDimension) -> List -} - - -type AffectedSetRead { - boundary: AffectedBoundaryRead - frontier: AffectedFrontierRead propagation_receipt: fn(Dag, EditFrontierEdge) -> PropagationReceipt } @@ -210,19 +193,8 @@ fn mark_whole_dag(acc: FrontierAcc, reason: Symbol) -> FrontierAcc { } -fn boundary_receipt(read_model: AffectedSetRead, dag: Dag, path: Path, replacement: Node) -> BoundaryReceipt { - match read_model.boundary.boundary_hash_receipt(dag, path, replacement) { - HashUnchangedPure { content_hash: h, dag: next_dag } => - BoundaryPruned { - proof: UnchangedPureBoundary { - subject: path, - content_hash: h - }, - dag: next_dag - } - HashChanged { dag: next_dag } => BoundaryExpands { dag: next_dag } - HashUnknown { reason: r } => BoundaryUnknown { reason: r } - } +fn read_boundary_receipt(read_model: AffectedSetRead, dag: Dag, edit: Edit) -> BoundaryReceipt { + read_model.boundary_receipt(dag, edit) } @@ -234,7 +206,7 @@ fn expand_dimension( dimension: AffectedDimension ) -> FrontierAcc { fold( - read_model.frontier.proven_transitive_changed_subgraph_frontier( + read_model.proven_transitive_changed_subgraph_frontier( dag, EditDimension { edit: edit, dimension: dimension } ), @@ -262,7 +234,7 @@ fn expand_changed_dimensions( acc: FrontierAcc, edit: Edit ) -> FrontierAcc { - match read_model.boundary.changed_dimension_receipt(boundary_dag, edit) { + match read_model.changed_dimension_receipt(boundary_dag, edit) { ChangedDimensions { dimensions: dimensions } => fold(dimensions, init: acc, f: fn(acc2, dimension) { expand_dimension( @@ -284,11 +256,10 @@ fn seed_edit_fold_acc( acc: AffectedFoldAcc, edit: Edit ) -> AffectedFoldAcc { - match boundary_receipt( + match read_boundary_receipt( read_model: read_model, dag: acc.dag, - path: edit.at, - replacement: edit.replacement + edit: edit ) { BoundaryPruned { proof: p, dag: next_dag } => AffectedFoldAcc { From 19377cf656d22803b2225b57a3e8088a94984d16 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 06:55:03 -0400 Subject: [PATCH 20/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/lens/affected_set.dag | 71 ++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 6ec533cef7a..94161c6581f 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -1,6 +1,6 @@ // src/v4/lens/affected_set.dag // Scope: canonical affected-set authority for CI/incremental re-execution; fail-closed B-4/B-5 node frontier. -// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, FrontierDecision, ReExecFrontier, AffectedSetRead, BoundaryReceipt, ChangedDimensionReceipt, affected_set. +// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, FrontierDecision, ReExecFrontier, AffectedSetRead, AffectedCurrentDagRead, HashReceipt, ChangedDimensionReceipt, affected_set. // Consumes: Map, Diff, Edit, Hash, Node, Path, Symbol, Witness; B1 content_hash receipts; B-4 resolved Symbol equality; B-5 per-Arrow signature effects. // Status: Wave-0 front: bounded changed-subgraph frontier fold landed; dissolves when CI use and .dag TestClaims land. @@ -66,10 +66,17 @@ type ReExecFrontier | WholeDagFailClosed { reason: Symbol } +// 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves into B1 content_hash plus B-5 purity/effect receipts. +type HashReceipt + = HashUnchangedPure { content_hash: Hash } + | HashChanged + | HashUnknown { reason: Symbol } + + // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when T-21 .dag TestClaims prove boundary cases. type BoundaryReceipt - = BoundaryPruned { proof: ExclusionProof, dag: Dag } - | BoundaryExpands { dag: Dag } + = BoundaryPruned { proof: ExclusionProof } + | BoundaryExpands | BoundaryUnknown { reason: Symbol } @@ -86,10 +93,25 @@ type PropagationReceipt | PropagationUnknown { reason: Symbol } -type AffectedSetRead { - boundary_receipt: fn(Dag, Edit) -> BoundaryReceipt +type AffectedBoundaryRead { + boundary_hash_receipt: fn(Dag, Path, Node) -> HashReceipt, changed_dimension_receipt: fn(Dag, Edit) -> ChangedDimensionReceipt +} + + +type AffectedFrontierRead { proven_transitive_changed_subgraph_frontier: fn(Dag, EditDimension) -> List +} + + +type AffectedCurrentDagRead { + post_edit_dag: fn(Dag, Edit) -> Dag +} + + +type AffectedSetRead { + boundary: AffectedBoundaryRead + frontier: AffectedFrontierRead propagation_receipt: fn(Dag, EditFrontierEdge) -> PropagationReceipt } @@ -193,8 +215,18 @@ fn mark_whole_dag(acc: FrontierAcc, reason: Symbol) -> FrontierAcc { } -fn read_boundary_receipt(read_model: AffectedSetRead, dag: Dag, edit: Edit) -> BoundaryReceipt { - read_model.boundary_receipt(dag, edit) +fn boundary_receipt(read_model: AffectedSetRead, dag: Dag, path: Path, replacement: Node) -> BoundaryReceipt { + match read_model.boundary.boundary_hash_receipt(dag, path, replacement) { + HashUnchangedPure { content_hash: h } => + BoundaryPruned { + proof: UnchangedPureBoundary { + subject: path, + content_hash: h + } + } + HashChanged => BoundaryExpands + HashUnknown { reason: r } => BoundaryUnknown { reason: r } + } } @@ -206,7 +238,7 @@ fn expand_dimension( dimension: AffectedDimension ) -> FrontierAcc { fold( - read_model.proven_transitive_changed_subgraph_frontier( + read_model.frontier.proven_transitive_changed_subgraph_frontier( dag, EditDimension { edit: edit, dimension: dimension } ), @@ -234,7 +266,7 @@ fn expand_changed_dimensions( acc: FrontierAcc, edit: Edit ) -> FrontierAcc { - match read_model.changed_dimension_receipt(boundary_dag, edit) { + match read_model.boundary.changed_dimension_receipt(boundary_dag, edit) { ChangedDimensions { dimensions: dimensions } => fold(dimensions, init: acc, f: fn(acc2, dimension) { expand_dimension( @@ -253,20 +285,23 @@ fn expand_changed_dimensions( fn seed_edit_fold_acc( read_model: AffectedSetRead, + current_dag_read: AffectedCurrentDagRead, acc: AffectedFoldAcc, edit: Edit ) -> AffectedFoldAcc { - match read_boundary_receipt( + let next_dag = current_dag_read.post_edit_dag(acc.dag, edit) + match boundary_receipt( read_model: read_model, dag: acc.dag, - edit: edit + path: edit.at, + replacement: edit.replacement ) { - BoundaryPruned { proof: p, dag: next_dag } => + BoundaryPruned { proof: p } => AffectedFoldAcc { dag: next_dag, frontier: mark_excluded(acc: acc.frontier, key: root_key(path: edit.at, dimension: affected_dimension_value), proof: p) } - BoundaryExpands { dag: next_dag } => + BoundaryExpands => AffectedFoldAcc { dag: next_dag, frontier: expand_changed_dimensions( @@ -279,7 +314,7 @@ fn seed_edit_fold_acc( } BoundaryUnknown { reason: r } => AffectedFoldAcc { - dag: acc.dag, + dag: next_dag, frontier: mark_whole_dag( acc: acc.frontier, reason: r @@ -289,12 +324,18 @@ fn seed_edit_fold_acc( } -fn affected_set(read_model: AffectedSetRead, dag: Dag, diff: Diff) -> Witness { +fn affected_set( + read_model: AffectedSetRead, + current_dag_read: AffectedCurrentDagRead, + dag: Dag, + diff: Diff +) -> Witness { Holds { value: frontier_from_fold_acc( acc: fold(diff.edits, init: empty_affected_fold_acc(dag: dag), f: fn(acc, edit) { seed_edit_fold_acc( read_model: read_model, + current_dag_read: current_dag_read, acc: acc, edit: edit ) From 9f4850b0fbd4c7b635265b55228894fc47ecf44f Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 10:57:05 +0000 Subject: [PATCH 21/48] Fix affected-set boundary dimension authority D5 receipt: operator blocking review on PR #3349 flagged the synthetic affected_dimension_value sentinel as a fabricated AffectedDimension in ExclusionKey and the D1 current-Dag fold as requiring explicit post-edit Dag authority. Current head already carries AffectedCurrentDagRead through the ordered Diff fold; this commit removes the sentinel boundary-prune key and reconciles DECISIONS.md #3153-subsumed to the bridge-compatible current-Dag read shape. --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index a11cdfd4b84..2603c3ab50c 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_receipt`, `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus top-level `affected_set(read_model, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the post-edit Dag carried by the boundary receipt, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts and D1 post-edit Dag receipts when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions. `FrontierAcc` is 🟒 terminal internal fold state because it mirrors `ReExecFrontier` authority during ordered `Diff` accumulation and lowers directly through `frontier_from_acc`; `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs that frontier with the current Dag required by D1 sequential rewrite. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus `AffectedCurrentDagRead.post_edit_dag` and top-level `affected_set(read_model, current_dag_read, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the post-edit Dag returned by `AffectedCurrentDagRead`, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedCurrentDagRead` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / per-edit post-Dag receipts when those reads are substrate-produced. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions. `FrontierAcc` is 🟒 terminal internal fold state because it mirrors `ReExecFrontier` authority during ordered `Diff` accumulation and lowers directly through `frontier_from_acc`; `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs that frontier with the current Dag required by D1 sequential rewrite. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 94161c6581f..da369dcc394 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -17,8 +17,6 @@ type Dag = Node type AffectedDimension = Symbol -data affected_dimension_value: Symbol = affected_dimension_value - type ExclusionKey { at: Path @@ -299,7 +297,7 @@ fn seed_edit_fold_acc( BoundaryPruned { proof: p } => AffectedFoldAcc { dag: next_dag, - frontier: mark_excluded(acc: acc.frontier, key: root_key(path: edit.at, dimension: affected_dimension_value), proof: p) + frontier: acc.frontier } BoundaryExpands => AffectedFoldAcc { From f785f0e35400c0f79d72aef40d7a926f53321a06 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 07:22:29 -0400 Subject: [PATCH 22/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 28 ++++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index 2603c3ab50c..6d62879657c 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus `AffectedCurrentDagRead.post_edit_dag` and top-level `affected_set(read_model, current_dag_read, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the post-edit Dag returned by `AffectedCurrentDagRead`, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedCurrentDagRead` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / per-edit post-Dag receipts when those reads are substrate-produced. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions. `FrontierAcc` is 🟒 terminal internal fold state because it mirrors `ReExecFrontier` authority during ordered `Diff` accumulation and lowers directly through `frontier_from_acc`; `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs that frontier with the current Dag required by D1 sequential rewrite. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus `AffectedCurrentDagRead.post_edit_dag` and top-level `affected_set(read_model, current_dag_read, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the post-edit Dag returned by `AffectedCurrentDagRead`, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedCurrentDagRead` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / per-edit post-Dag receipts when those reads are substrate-produced. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions; its changed-subgraph arm carries path-keyed boundary exclusions so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension`. `FrontierAcc` is 🟒 terminal internal fold state because it mirrors `ReExecFrontier` authority, including boundary exclusions, during ordered `Diff` accumulation and lowers directly through `frontier_from_acc`; `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs that frontier with the current Dag required by D1 sequential rewrite. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index da369dcc394..8df1bba01f8 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -59,6 +59,7 @@ type FrontierDecision // 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed; IRT-1 whole/changed-subgraph gate. type ReExecFrontier = ChangedSubgraphFrontier { + boundary_exclusions: Map, decisions: Map } | WholeDagFailClosed { reason: Symbol } @@ -117,6 +118,7 @@ type AffectedSetRead { // 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed; accumulator mirrors ReExecFrontier authority. type FrontierAcc = ChangedSubgraphAcc { + boundary_exclusions: Map, decisions: Map } | WholeDagAcc { reason: Symbol } @@ -130,6 +132,7 @@ type AffectedFoldAcc { fn empty_frontier_acc() -> FrontierAcc { ChangedSubgraphAcc { + boundary_exclusions: empty_map(), decisions: empty_map() } } @@ -150,8 +153,9 @@ fn frontier_from_fold_acc(acc: AffectedFoldAcc) -> ReExecFrontier { fn frontier_from_acc(acc: FrontierAcc) -> ReExecFrontier { match acc { - ChangedSubgraphAcc { decisions: decisions } => + ChangedSubgraphAcc { boundary_exclusions: boundary_exclusions, decisions: decisions } => ChangedSubgraphFrontier { + boundary_exclusions: boundary_exclusions, decisions: decisions } WholeDagAcc { reason: r } => WholeDagFailClosed { reason: r } @@ -178,29 +182,45 @@ fn edge_key(edge: FrontierEdge) -> ExclusionKey { fn mark_rerun(acc: FrontierAcc, key: ExclusionKey) -> FrontierAcc { match acc { WholeDagAcc { reason: r } => WholeDagAcc { reason: r } - ChangedSubgraphAcc { decisions: decisions } => + ChangedSubgraphAcc { boundary_exclusions: boundary_exclusions, decisions: decisions } => ChangedSubgraphAcc { + boundary_exclusions: boundary_exclusions, decisions: map_insert(decisions, key, Rerun) } } } +fn mark_boundary_excluded(acc: FrontierAcc, path: Path, proof: ExclusionProof) -> FrontierAcc { + match acc { + WholeDagAcc { reason: r } => WholeDagAcc { reason: r } + ChangedSubgraphAcc { boundary_exclusions: boundary_exclusions, decisions: decisions } => + ChangedSubgraphAcc { + boundary_exclusions: map_insert(boundary_exclusions, path, proof), + decisions: decisions + } + } +} + + fn mark_excluded(acc: FrontierAcc, key: ExclusionKey, proof: ExclusionProof) -> FrontierAcc { match acc { WholeDagAcc { reason: r } => WholeDagAcc { reason: r } - ChangedSubgraphAcc { decisions: decisions } => + ChangedSubgraphAcc { boundary_exclusions: boundary_exclusions, decisions: decisions } => match map_get(decisions, key) { Some { value: Rerun } => ChangedSubgraphAcc { + boundary_exclusions: boundary_exclusions, decisions: decisions } Some { value: Excluded { proof: _ } } => ChangedSubgraphAcc { + boundary_exclusions: boundary_exclusions, decisions: map_insert(decisions, key, Excluded { proof: proof }) } None => ChangedSubgraphAcc { + boundary_exclusions: boundary_exclusions, decisions: map_insert(decisions, key, Excluded { proof: proof }) } } @@ -297,7 +317,7 @@ fn seed_edit_fold_acc( BoundaryPruned { proof: p } => AffectedFoldAcc { dag: next_dag, - frontier: acc.frontier + frontier: mark_boundary_excluded(acc: acc.frontier, path: edit.at, proof: p) } BoundaryExpands => AffectedFoldAcc { From f49e633b4a7e8eb120ac24326ad3755b59da816a Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 07:37:12 -0400 Subject: [PATCH 23/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/lens/affected_set.dag | 73 +++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 8df1bba01f8..4d43c1d55be 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -1,13 +1,14 @@ // src/v4/lens/affected_set.dag // Scope: canonical affected-set authority for CI/incremental re-execution; fail-closed B-4/B-5 node frontier. // Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, FrontierDecision, ReExecFrontier, AffectedSetRead, AffectedCurrentDagRead, HashReceipt, ChangedDimensionReceipt, affected_set. -// Consumes: Map, Diff, Edit, Hash, Node, Path, Symbol, Witness; B1 content_hash receipts; B-4 resolved Symbol equality; B-5 per-Arrow signature effects. +// Consumes: Map, Diagnostic, Diff, Edit, Hash, Node, Path, Symbol, Witness; B1 content_hash receipts; B-4 resolved Symbol equality; B-5 per-Arrow signature effects. // Status: Wave-0 front: bounded changed-subgraph frontier fold landed; dissolves when CI use and .dag TestClaims land. module v4.lens.affected_set import v4.std.collection { Map } +import v4.std.diagnostic { Diagnostic } import v4.std.node { Diff, Edit, Hash, Node, Path, Symbol } import v4.std.witness { Witness } @@ -17,6 +18,8 @@ type Dag = Node type AffectedDimension = Symbol +data affected_reason_invalid_edit: Symbol = affected_reason_invalid_edit + type ExclusionKey { at: Path @@ -103,8 +106,14 @@ type AffectedFrontierRead { } +// 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves into D1 apply_diff Outcome. +type AffectedPostEditDagReceipt + = PostEditDagProduced { dag: Dag } + | PostEditDagRejected { diagnostic: Diagnostic } + + type AffectedCurrentDagRead { - post_edit_dag: fn(Dag, Edit) -> Dag + post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt } @@ -307,35 +316,45 @@ fn seed_edit_fold_acc( acc: AffectedFoldAcc, edit: Edit ) -> AffectedFoldAcc { - let next_dag = current_dag_read.post_edit_dag(acc.dag, edit) - match boundary_receipt( - read_model: read_model, - dag: acc.dag, - path: edit.at, - replacement: edit.replacement - ) { - BoundaryPruned { proof: p } => - AffectedFoldAcc { - dag: next_dag, - frontier: mark_boundary_excluded(acc: acc.frontier, path: edit.at, proof: p) - } - BoundaryExpands => - AffectedFoldAcc { - dag: next_dag, - frontier: expand_changed_dimensions( - read_model: read_model, - boundary_dag: acc.dag, - frontier_dag: next_dag, - acc: acc.frontier, - edit: edit - ) + match current_dag_read.post_edit_dag(acc.dag, edit) { + PostEditDagProduced { dag: next_dag } => + match boundary_receipt( + read_model: read_model, + dag: acc.dag, + path: edit.at, + replacement: edit.replacement + ) { + BoundaryPruned { proof: p } => + AffectedFoldAcc { + dag: next_dag, + frontier: mark_boundary_excluded(acc: acc.frontier, path: edit.at, proof: p) + } + BoundaryExpands => + AffectedFoldAcc { + dag: next_dag, + frontier: expand_changed_dimensions( + read_model: read_model, + boundary_dag: acc.dag, + frontier_dag: next_dag, + acc: acc.frontier, + edit: edit + ) + } + BoundaryUnknown { reason: r } => + AffectedFoldAcc { + dag: next_dag, + frontier: mark_whole_dag( + acc: acc.frontier, + reason: r + ) + } } - BoundaryUnknown { reason: r } => + PostEditDagRejected { diagnostic: _ } => AffectedFoldAcc { - dag: next_dag, + dag: acc.dag, frontier: mark_whole_dag( acc: acc.frontier, - reason: r + reason: affected_reason_invalid_edit ) } } From 495e03a0c5ad67c84abc8f62356aae01923099d2 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 11:40:10 +0000 Subject: [PATCH 24/48] Fail closed post-edit dag receipt D5 receipt: codex current-head review on PR #3349 found the post_edit_dag hook was total despite D1 requiring stale/invalid edits to reject with Diagnostic. This commit makes the post-edit state read return AffectedPostEditDagReceipt with a diagnostic rejection arm and collapses rejected edits to WholeDagFailClosed during the ordered Diff fold. --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 15 +++++---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index 97655bfee25..e37f2e3b9a5 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus `AffectedCurrentDagRead.post_edit_dag` and top-level `affected_set(read_model, current_dag_read, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the post-edit Dag returned by `AffectedCurrentDagRead`, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedCurrentDagRead` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / per-edit post-Dag receipts when those reads are substrate-produced. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions; its changed-subgraph arm carries path-keyed boundary exclusions so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension`. `FrontierAcc` is 🟒 terminal internal fold state because it mirrors `ReExecFrontier` authority, including boundary exclusions, during ordered `Diff` accumulation and lowers directly through `frontier_from_acc`; `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs that frontier with the current Dag required by D1 sequential rewrite. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt`, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions; its changed-subgraph arm carries path-keyed boundary exclusions so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension`. `FrontierAcc` is 🟒 terminal internal fold state because it mirrors `ReExecFrontier` authority, including boundary exclusions, during ordered `Diff` accumulation and lowers directly through `frontier_from_acc`; `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs that frontier with the current Dag required by D1 sequential rewrite. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 4d43c1d55be..c19d9391c3c 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -1,6 +1,6 @@ // src/v4/lens/affected_set.dag // Scope: canonical affected-set authority for CI/incremental re-execution; fail-closed B-4/B-5 node frontier. -// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, FrontierDecision, ReExecFrontier, AffectedSetRead, AffectedCurrentDagRead, HashReceipt, ChangedDimensionReceipt, affected_set. +// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, FrontierDecision, ReExecFrontier, AffectedSetRead, AffectedPostEditDagReceipt, HashReceipt, ChangedDimensionReceipt, affected_set. // Consumes: Map, Diagnostic, Diff, Edit, Hash, Node, Path, Symbol, Witness; B1 content_hash receipts; B-4 resolved Symbol equality; B-5 per-Arrow signature effects. // Status: Wave-0 front: bounded changed-subgraph frontier fold landed; dissolves when CI use and .dag TestClaims land. @@ -112,11 +112,6 @@ type AffectedPostEditDagReceipt | PostEditDagRejected { diagnostic: Diagnostic } -type AffectedCurrentDagRead { - post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt -} - - type AffectedSetRead { boundary: AffectedBoundaryRead frontier: AffectedFrontierRead @@ -312,11 +307,11 @@ fn expand_changed_dimensions( fn seed_edit_fold_acc( read_model: AffectedSetRead, - current_dag_read: AffectedCurrentDagRead, + post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt, acc: AffectedFoldAcc, edit: Edit ) -> AffectedFoldAcc { - match current_dag_read.post_edit_dag(acc.dag, edit) { + match post_edit_dag(acc.dag, edit) { PostEditDagProduced { dag: next_dag } => match boundary_receipt( read_model: read_model, @@ -363,7 +358,7 @@ fn seed_edit_fold_acc( fn affected_set( read_model: AffectedSetRead, - current_dag_read: AffectedCurrentDagRead, + post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt, dag: Dag, diff: Diff ) -> Witness { @@ -372,7 +367,7 @@ fn affected_set( acc: fold(diff.edits, init: empty_affected_fold_acc(dag: dag), f: fn(acc, edit) { seed_edit_fold_acc( read_model: read_model, - current_dag_read: current_dag_read, + post_edit_dag: post_edit_dag, acc: acc, edit: edit ) From b5bbdec405f607db949732e996e705079f96b492 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 07:52:23 -0400 Subject: [PATCH 25/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 50 ++++++++++++++++++++++-------------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index e37f2e3b9a5..5304cf8bbbf 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt`, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove the exclusion evidence cases. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions; its changed-subgraph arm carries path-keyed boundary exclusions so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension`. `FrontierAcc` is 🟒 terminal internal fold state because it mirrors `ReExecFrontier` authority, including boundary exclusions, during ordered `Diff` accumulation and lowers directly through `frontier_from_acc`; `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs that frontier with the current Dag required by D1 sequential rewrite. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt`, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove per-key dimension/edge exclusion evidence cases. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries path-keyed boundary exclusions so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension`. `FrontierAcc` is 🟒 terminal internal fold state because it mirrors `ReExecFrontier` authority, including boundary exclusions, during ordered `Diff` accumulation and lowers directly through `frontier_from_acc`; `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs that frontier with the current Dag required by D1 sequential rewrite. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index c19d9391c3c..fdf8dc6c5c3 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -1,6 +1,6 @@ // src/v4/lens/affected_set.dag // Scope: canonical affected-set authority for CI/incremental re-execution; fail-closed B-4/B-5 node frontier. -// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, FrontierDecision, ReExecFrontier, AffectedSetRead, AffectedPostEditDagReceipt, HashReceipt, ChangedDimensionReceipt, affected_set. +// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, BoundaryExclusionProof, WholeDagFailClosedEvidence, FrontierDecision, ReExecFrontier, AffectedSetRead, AffectedPostEditDagReceipt, HashReceipt, ChangedDimensionReceipt, affected_set. // Consumes: Map, Diagnostic, Diff, Edit, Hash, Node, Path, Symbol, Witness; B1 content_hash receipts; B-4 resolved Symbol equality; B-5 per-Arrow signature effects. // Status: Wave-0 front: bounded changed-subgraph frontier fold landed; dissolves when CI use and .dag TestClaims land. @@ -18,9 +18,6 @@ type Dag = Node type AffectedDimension = Symbol -data affected_reason_invalid_edit: Symbol = affected_reason_invalid_edit - - type ExclusionKey { at: Path dimension: AffectedDimension @@ -50,7 +47,17 @@ type EditFrontierEdge { type ExclusionProof = EmptyDimensionDelta { subject: Path } | DimensionNotRead { producer: Path } - | UnchangedPureBoundary { subject: Path, content_hash: Hash } + + +// 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed +type BoundaryExclusionProof + = UnchangedPureBoundary { subject: Path, content_hash: Hash } + + +// 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed +type WholeDagFailClosedEvidence + = WholeDagReason { reason: Symbol } + | WholeDagDiagnostic { diagnostic: Diagnostic } // 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed @@ -62,10 +69,10 @@ type FrontierDecision // 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed; IRT-1 whole/changed-subgraph gate. type ReExecFrontier = ChangedSubgraphFrontier { - boundary_exclusions: Map, + boundary_exclusions: Map, decisions: Map } - | WholeDagFailClosed { reason: Symbol } + | WholeDagFailClosed { evidence: WholeDagFailClosedEvidence } // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves into B1 content_hash plus B-5 purity/effect receipts. @@ -77,7 +84,7 @@ type HashReceipt // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when T-21 .dag TestClaims prove boundary cases. type BoundaryReceipt - = BoundaryPruned { proof: ExclusionProof } + = BoundaryPruned { proof: BoundaryExclusionProof } | BoundaryExpands | BoundaryUnknown { reason: Symbol } @@ -122,10 +129,10 @@ type AffectedSetRead { // 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed; accumulator mirrors ReExecFrontier authority. type FrontierAcc = ChangedSubgraphAcc { - boundary_exclusions: Map, + boundary_exclusions: Map, decisions: Map } - | WholeDagAcc { reason: Symbol } + | WholeDagAcc { evidence: WholeDagFailClosedEvidence } type AffectedFoldAcc { @@ -162,7 +169,7 @@ fn frontier_from_acc(acc: FrontierAcc) -> ReExecFrontier { boundary_exclusions: boundary_exclusions, decisions: decisions } - WholeDagAcc { reason: r } => WholeDagFailClosed { reason: r } + WholeDagAcc { evidence: e } => WholeDagFailClosed { evidence: e } } } @@ -185,7 +192,7 @@ fn edge_key(edge: FrontierEdge) -> ExclusionKey { fn mark_rerun(acc: FrontierAcc, key: ExclusionKey) -> FrontierAcc { match acc { - WholeDagAcc { reason: r } => WholeDagAcc { reason: r } + WholeDagAcc { evidence: e } => WholeDagAcc { evidence: e } ChangedSubgraphAcc { boundary_exclusions: boundary_exclusions, decisions: decisions } => ChangedSubgraphAcc { boundary_exclusions: boundary_exclusions, @@ -195,9 +202,9 @@ fn mark_rerun(acc: FrontierAcc, key: ExclusionKey) -> FrontierAcc { } -fn mark_boundary_excluded(acc: FrontierAcc, path: Path, proof: ExclusionProof) -> FrontierAcc { +fn mark_boundary_excluded(acc: FrontierAcc, path: Path, proof: BoundaryExclusionProof) -> FrontierAcc { match acc { - WholeDagAcc { reason: r } => WholeDagAcc { reason: r } + WholeDagAcc { evidence: e } => WholeDagAcc { evidence: e } ChangedSubgraphAcc { boundary_exclusions: boundary_exclusions, decisions: decisions } => ChangedSubgraphAcc { boundary_exclusions: map_insert(boundary_exclusions, path, proof), @@ -209,7 +216,7 @@ fn mark_boundary_excluded(acc: FrontierAcc, path: Path, proof: ExclusionProof) - fn mark_excluded(acc: FrontierAcc, key: ExclusionKey, proof: ExclusionProof) -> FrontierAcc { match acc { - WholeDagAcc { reason: r } => WholeDagAcc { reason: r } + WholeDagAcc { evidence: e } => WholeDagAcc { evidence: e } ChangedSubgraphAcc { boundary_exclusions: boundary_exclusions, decisions: decisions } => match map_get(decisions, key) { Some { value: Rerun } => @@ -233,7 +240,12 @@ fn mark_excluded(acc: FrontierAcc, key: ExclusionKey, proof: ExclusionProof) -> fn mark_whole_dag(acc: FrontierAcc, reason: Symbol) -> FrontierAcc { - WholeDagAcc { reason: reason } + WholeDagAcc { evidence: WholeDagReason { reason: reason } } +} + + +fn mark_whole_dag_diagnostic(acc: FrontierAcc, diagnostic: Diagnostic) -> FrontierAcc { + WholeDagAcc { evidence: WholeDagDiagnostic { diagnostic: diagnostic } } } @@ -344,12 +356,12 @@ fn seed_edit_fold_acc( ) } } - PostEditDagRejected { diagnostic: _ } => + PostEditDagRejected { diagnostic: d } => AffectedFoldAcc { dag: acc.dag, - frontier: mark_whole_dag( + frontier: mark_whole_dag_diagnostic( acc: acc.frontier, - reason: affected_reason_invalid_edit + diagnostic: d ) } } From 3193c8eb0d4b6cbb375f74eea467e2791545f204 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 09:10:25 -0400 Subject: [PATCH 26/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/lens/affected_set.dag | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index fdf8dc6c5c3..23e57d0389e 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -91,7 +91,7 @@ type BoundaryReceipt // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when per-edit dimension deltas are generated. type ChangedDimensionReceipt - = ChangedDimensions { dimensions: List } + = ChangedDimensions { first: AffectedDimension, rest: List } | ChangedDimensionsUnknown { reason: Symbol } @@ -301,8 +301,14 @@ fn expand_changed_dimensions( edit: Edit ) -> FrontierAcc { match read_model.boundary.changed_dimension_receipt(boundary_dag, edit) { - ChangedDimensions { dimensions: dimensions } => - fold(dimensions, init: acc, f: fn(acc2, dimension) { + ChangedDimensions { first: first_dimension, rest: rest_dimensions } => + fold(rest_dimensions, init: expand_dimension( + read_model: read_model, + dag: frontier_dag, + acc: mark_rerun(acc: acc, key: root_key(path: edit.at, dimension: first_dimension)), + edit: edit, + dimension: first_dimension + ), f: fn(acc2, dimension) { expand_dimension( read_model: read_model, dag: frontier_dag, From f36b6c8f931b9d4f8f1fb0f1f01a953ef5c21946 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 09:13:19 -0400 Subject: [PATCH 27/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/lens/affected_set.dag | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 23e57d0389e..5a34daaa7de 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -63,13 +63,13 @@ type WholeDagFailClosedEvidence // 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed type FrontierDecision = Rerun - | Excluded { proof: ExclusionProof } + | Excluded { proofs: List } // 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed; IRT-1 whole/changed-subgraph gate. type ReExecFrontier = ChangedSubgraphFrontier { - boundary_exclusions: Map, + boundary_exclusions: Map, decisions: Map } | WholeDagFailClosed { evidence: WholeDagFailClosedEvidence } @@ -129,7 +129,7 @@ type AffectedSetRead { // 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed; accumulator mirrors ReExecFrontier authority. type FrontierAcc = ChangedSubgraphAcc { - boundary_exclusions: Map, + boundary_exclusions: Map, decisions: Map } | WholeDagAcc { evidence: WholeDagFailClosedEvidence } @@ -202,12 +202,12 @@ fn mark_rerun(acc: FrontierAcc, key: ExclusionKey) -> FrontierAcc { } -fn mark_boundary_excluded(acc: FrontierAcc, path: Path, proof: BoundaryExclusionProof) -> FrontierAcc { +fn mark_boundary_excluded(acc: FrontierAcc, edit: Edit, proof: BoundaryExclusionProof) -> FrontierAcc { match acc { WholeDagAcc { evidence: e } => WholeDagAcc { evidence: e } ChangedSubgraphAcc { boundary_exclusions: boundary_exclusions, decisions: decisions } => ChangedSubgraphAcc { - boundary_exclusions: map_insert(boundary_exclusions, path, proof), + boundary_exclusions: map_insert(boundary_exclusions, edit, proof), decisions: decisions } } @@ -224,15 +224,15 @@ fn mark_excluded(acc: FrontierAcc, key: ExclusionKey, proof: ExclusionProof) -> boundary_exclusions: boundary_exclusions, decisions: decisions } - Some { value: Excluded { proof: _ } } => + Some { value: Excluded { proofs: proofs } } => ChangedSubgraphAcc { boundary_exclusions: boundary_exclusions, - decisions: map_insert(decisions, key, Excluded { proof: proof }) + decisions: map_insert(decisions, key, Excluded { proofs: list_push(proofs, proof) }) } None => ChangedSubgraphAcc { boundary_exclusions: boundary_exclusions, - decisions: map_insert(decisions, key, Excluded { proof: proof }) + decisions: map_insert(decisions, key, Excluded { proofs: [proof] }) } } } @@ -340,7 +340,7 @@ fn seed_edit_fold_acc( BoundaryPruned { proof: p } => AffectedFoldAcc { dag: next_dag, - frontier: mark_boundary_excluded(acc: acc.frontier, path: edit.at, proof: p) + frontier: mark_boundary_excluded(acc: acc.frontier, edit: edit, proof: p) } BoundaryExpands => AffectedFoldAcc { From bd54256880477c8be7fe47dfe014ae99249e03f2 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 13:15:05 +0000 Subject: [PATCH 28/48] Record affected-set exclusion receipt refinements D5 receipt: reconciles DECISIONS.md #3153-subsumed for non-empty changed-dimension receipts, edit-keyed boundary exclusions, and aggregated per-key ExclusionProof evidence in the T-21 affected_set.dag Wave-0 substrate front. --- src/v4/DECISIONS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index 5304cf8bbbf..8f6d84f667a 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt`, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove per-key dimension/edge exclusion evidence cases. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries path-keyed boundary exclusions so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension`. `FrontierAcc` is 🟒 terminal internal fold state because it mirrors `ReExecFrontier` authority, including boundary exclusions, during ordered `Diff` accumulation and lowers directly through `frontier_from_acc`; `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs that frontier with the current Dag required by D1 sequential rewrite. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded` so the aggregate cannot under-rerun. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt`, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove per-key dimension/edge exclusion evidence cases. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries edit-keyed boundary exclusions so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`. `FrontierAcc` is 🟒 terminal internal fold state because it mirrors `ReExecFrontier` authority, including edit-keyed boundary exclusions, during ordered `Diff` accumulation and lowers directly through `frontier_from_acc`; `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs that frontier with the current Dag required by D1 sequential rewrite. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `ExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | From d8273968b2763dd4ff0060ea50be926962ee92f3 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 09:36:04 -0400 Subject: [PATCH 29/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 126 ++++++--------- .../claim/manual/affected_set_anchors.dag | 150 ++++++++++++++++++ 3 files changed, 202 insertions(+), 76 deletions(-) create mode 100644 src/v4/test/claim/manual/affected_set_anchors.dag diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index 8f6d84f667a..0b94c5b551d 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt`, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove per-key dimension/edge exclusion evidence cases. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries edit-keyed boundary exclusions so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`. `FrontierAcc` is 🟒 terminal internal fold state because it mirrors `ReExecFrontier` authority, including edit-keyed boundary exclusions, during ordered `Diff` accumulation and lowers directly through `frontier_from_acc`; `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs that frontier with the current Dag required by D1 sequential rewrite. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `ExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt`, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove per-key dimension/edge exclusion evidence cases. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `ExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 5a34daaa7de..59d0af9f440 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -1,6 +1,6 @@ // src/v4/lens/affected_set.dag // Scope: canonical affected-set authority for CI/incremental re-execution; fail-closed B-4/B-5 node frontier. -// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, BoundaryExclusionProof, WholeDagFailClosedEvidence, FrontierDecision, ReExecFrontier, AffectedSetRead, AffectedPostEditDagReceipt, HashReceipt, ChangedDimensionReceipt, affected_set. +// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, BoundaryExclusionProof, BoundaryExclusion, WholeDagFailClosedEvidence, FrontierDecision, ReExecFrontier, AffectedSetRead, AffectedPostEditDagReceipt, HashReceipt, ChangedDimensionReceipt, affected_set. // Consumes: Map, Diagnostic, Diff, Edit, Hash, Node, Path, Symbol, Witness; B1 content_hash receipts; B-4 resolved Symbol equality; B-5 per-Arrow signature effects. // Status: Wave-0 front: bounded changed-subgraph frontier fold landed; dissolves when CI use and .dag TestClaims land. @@ -54,6 +54,12 @@ type BoundaryExclusionProof = UnchangedPureBoundary { subject: Path, content_hash: Hash } +type BoundaryExclusion { + edit: Edit + proof: BoundaryExclusionProof +} + + // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed type WholeDagFailClosedEvidence = WholeDagReason { reason: Symbol } @@ -69,7 +75,7 @@ type FrontierDecision // 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed; IRT-1 whole/changed-subgraph gate. type ReExecFrontier = ChangedSubgraphFrontier { - boundary_exclusions: Map, + boundary_exclusions: List, decisions: Map } | WholeDagFailClosed { evidence: WholeDagFailClosedEvidence } @@ -126,24 +132,15 @@ type AffectedSetRead { } -// 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed; accumulator mirrors ReExecFrontier authority. -type FrontierAcc - = ChangedSubgraphAcc { - boundary_exclusions: Map, - decisions: Map - } - | WholeDagAcc { evidence: WholeDagFailClosedEvidence } - - type AffectedFoldAcc { dag: Dag - frontier: FrontierAcc + frontier: ReExecFrontier } -fn empty_frontier_acc() -> FrontierAcc { - ChangedSubgraphAcc { - boundary_exclusions: empty_map(), +fn empty_frontier() -> ReExecFrontier { + ChangedSubgraphFrontier { + boundary_exclusions: [], decisions: empty_map() } } @@ -152,25 +149,13 @@ fn empty_frontier_acc() -> FrontierAcc { fn empty_affected_fold_acc(dag: Dag) -> AffectedFoldAcc { AffectedFoldAcc { dag: dag, - frontier: empty_frontier_acc() + frontier: empty_frontier() } } fn frontier_from_fold_acc(acc: AffectedFoldAcc) -> ReExecFrontier { - frontier_from_acc(acc: acc.frontier) -} - - -fn frontier_from_acc(acc: FrontierAcc) -> ReExecFrontier { - match acc { - ChangedSubgraphAcc { boundary_exclusions: boundary_exclusions, decisions: decisions } => - ChangedSubgraphFrontier { - boundary_exclusions: boundary_exclusions, - decisions: decisions - } - WholeDagAcc { evidence: e } => WholeDagFailClosed { evidence: e } - } + acc.frontier } @@ -190,11 +175,11 @@ fn edge_key(edge: FrontierEdge) -> ExclusionKey { } -fn mark_rerun(acc: FrontierAcc, key: ExclusionKey) -> FrontierAcc { - match acc { - WholeDagAcc { evidence: e } => WholeDagAcc { evidence: e } - ChangedSubgraphAcc { boundary_exclusions: boundary_exclusions, decisions: decisions } => - ChangedSubgraphAcc { +fn mark_rerun(frontier: ReExecFrontier, key: ExclusionKey) -> ReExecFrontier { + match frontier { + WholeDagFailClosed { evidence: e } => WholeDagFailClosed { evidence: e } + ChangedSubgraphFrontier { boundary_exclusions: boundary_exclusions, decisions: decisions } => + ChangedSubgraphFrontier { boundary_exclusions: boundary_exclusions, decisions: map_insert(decisions, key, Rerun) } @@ -202,35 +187,35 @@ fn mark_rerun(acc: FrontierAcc, key: ExclusionKey) -> FrontierAcc { } -fn mark_boundary_excluded(acc: FrontierAcc, edit: Edit, proof: BoundaryExclusionProof) -> FrontierAcc { - match acc { - WholeDagAcc { evidence: e } => WholeDagAcc { evidence: e } - ChangedSubgraphAcc { boundary_exclusions: boundary_exclusions, decisions: decisions } => - ChangedSubgraphAcc { - boundary_exclusions: map_insert(boundary_exclusions, edit, proof), +fn mark_boundary_excluded(frontier: ReExecFrontier, edit: Edit, proof: BoundaryExclusionProof) -> ReExecFrontier { + match frontier { + WholeDagFailClosed { evidence: e } => WholeDagFailClosed { evidence: e } + ChangedSubgraphFrontier { boundary_exclusions: boundary_exclusions, decisions: decisions } => + ChangedSubgraphFrontier { + boundary_exclusions: list_push(boundary_exclusions, BoundaryExclusion { edit: edit, proof: proof }), decisions: decisions } } } -fn mark_excluded(acc: FrontierAcc, key: ExclusionKey, proof: ExclusionProof) -> FrontierAcc { - match acc { - WholeDagAcc { evidence: e } => WholeDagAcc { evidence: e } - ChangedSubgraphAcc { boundary_exclusions: boundary_exclusions, decisions: decisions } => +fn mark_excluded(frontier: ReExecFrontier, key: ExclusionKey, proof: ExclusionProof) -> ReExecFrontier { + match frontier { + WholeDagFailClosed { evidence: e } => WholeDagFailClosed { evidence: e } + ChangedSubgraphFrontier { boundary_exclusions: boundary_exclusions, decisions: decisions } => match map_get(decisions, key) { Some { value: Rerun } => - ChangedSubgraphAcc { + ChangedSubgraphFrontier { boundary_exclusions: boundary_exclusions, decisions: decisions } Some { value: Excluded { proofs: proofs } } => - ChangedSubgraphAcc { + ChangedSubgraphFrontier { boundary_exclusions: boundary_exclusions, decisions: map_insert(decisions, key, Excluded { proofs: list_push(proofs, proof) }) } None => - ChangedSubgraphAcc { + ChangedSubgraphFrontier { boundary_exclusions: boundary_exclusions, decisions: map_insert(decisions, key, Excluded { proofs: [proof] }) } @@ -239,13 +224,13 @@ fn mark_excluded(acc: FrontierAcc, key: ExclusionKey, proof: ExclusionProof) -> } -fn mark_whole_dag(acc: FrontierAcc, reason: Symbol) -> FrontierAcc { - WholeDagAcc { evidence: WholeDagReason { reason: reason } } +fn mark_whole_dag(reason: Symbol) -> ReExecFrontier { + WholeDagFailClosed { evidence: WholeDagReason { reason: reason } } } -fn mark_whole_dag_diagnostic(acc: FrontierAcc, diagnostic: Diagnostic) -> FrontierAcc { - WholeDagAcc { evidence: WholeDagDiagnostic { diagnostic: diagnostic } } +fn mark_whole_dag_diagnostic(diagnostic: Diagnostic) -> ReExecFrontier { + WholeDagFailClosed { evidence: WholeDagDiagnostic { diagnostic: diagnostic } } } @@ -267,27 +252,24 @@ fn boundary_receipt(read_model: AffectedSetRead, dag: Dag, path: Path, replaceme fn expand_dimension( read_model: AffectedSetRead, dag: Dag, - acc: FrontierAcc, + frontier: ReExecFrontier, edit: Edit, dimension: AffectedDimension -) -> FrontierAcc { +) -> ReExecFrontier { fold( read_model.frontier.proven_transitive_changed_subgraph_frontier( dag, EditDimension { edit: edit, dimension: dimension } ), - init: acc, + init: frontier, f: fn(acc2, edge) { match read_model.propagation_receipt(dag, EditFrontierEdge { edit: edit, edge: edge }) { Propagates => - mark_rerun(acc: acc2, key: edge_key(edge: edge)) + mark_rerun(frontier: acc2, key: edge_key(edge: edge)) DoesNotPropagate { proof: p } => - mark_excluded(acc: acc2, key: edge_key(edge: edge), proof: p) + mark_excluded(frontier: acc2, key: edge_key(edge: edge), proof: p) PropagationUnknown { reason: r } => - mark_whole_dag( - acc: mark_rerun(acc: acc2, key: edge_key(edge: edge)), - reason: r - ) + mark_whole_dag(reason: r) } }) } @@ -297,28 +279,28 @@ fn expand_changed_dimensions( read_model: AffectedSetRead, boundary_dag: Dag, frontier_dag: Dag, - acc: FrontierAcc, + frontier: ReExecFrontier, edit: Edit -) -> FrontierAcc { +) -> ReExecFrontier { match read_model.boundary.changed_dimension_receipt(boundary_dag, edit) { ChangedDimensions { first: first_dimension, rest: rest_dimensions } => fold(rest_dimensions, init: expand_dimension( read_model: read_model, dag: frontier_dag, - acc: mark_rerun(acc: acc, key: root_key(path: edit.at, dimension: first_dimension)), + frontier: mark_rerun(frontier: frontier, key: root_key(path: edit.at, dimension: first_dimension)), edit: edit, dimension: first_dimension ), f: fn(acc2, dimension) { expand_dimension( read_model: read_model, dag: frontier_dag, - acc: mark_rerun(acc: acc2, key: root_key(path: edit.at, dimension: dimension)), + frontier: mark_rerun(frontier: acc2, key: root_key(path: edit.at, dimension: dimension)), edit: edit, dimension: dimension ) }) ChangedDimensionsUnknown { reason: r } => - mark_whole_dag(acc: acc, reason: r) + mark_whole_dag(reason: r) } } @@ -340,7 +322,7 @@ fn seed_edit_fold_acc( BoundaryPruned { proof: p } => AffectedFoldAcc { dag: next_dag, - frontier: mark_boundary_excluded(acc: acc.frontier, edit: edit, proof: p) + frontier: mark_boundary_excluded(frontier: acc.frontier, edit: edit, proof: p) } BoundaryExpands => AffectedFoldAcc { @@ -349,26 +331,20 @@ fn seed_edit_fold_acc( read_model: read_model, boundary_dag: acc.dag, frontier_dag: next_dag, - acc: acc.frontier, + frontier: acc.frontier, edit: edit ) } BoundaryUnknown { reason: r } => AffectedFoldAcc { dag: next_dag, - frontier: mark_whole_dag( - acc: acc.frontier, - reason: r - ) + frontier: mark_whole_dag(reason: r) } } PostEditDagRejected { diagnostic: d } => AffectedFoldAcc { dag: acc.dag, - frontier: mark_whole_dag_diagnostic( - acc: acc.frontier, - diagnostic: d - ) + frontier: mark_whole_dag_diagnostic(diagnostic: d) } } } diff --git a/src/v4/test/claim/manual/affected_set_anchors.dag b/src/v4/test/claim/manual/affected_set_anchors.dag new file mode 100644 index 00000000000..ab078e9f0e4 --- /dev/null +++ b/src/v4/test/claim/manual/affected_set_anchors.dag @@ -0,0 +1,150 @@ +// src/v4/test/claim/manual/affected_set_anchors.dag +// Scope: Manual TestClaim anchors for T-21 affected_set Wave-0 gate cases. +// Owns: affected-set expected frontier rows + claim_affected_set_* TestClaim data +// Consumes: v4.std.verification v4.std.diagnostic v4.std.node v4.lens.affected_set +// Status: v2 compile; claim execution deferred (T-22). + +module v4.test.claim.manual.affected_set_anchors + +import v4.std.verification { + TestClaim, + Equals, + TestClassification, + Tier1, + Unit, + Boundary, + T19ManualAnchorAbsent +} +import v4.std.diagnostic { + Produced, + Diagnostic, + NodeLocus, + Unavailable, + ExternalContractUnknown +} +import v4.std.node { Path, Edit, Hash, Symbol } +import v4.test.claim.manual.connective_anchors { stub_empty_conj } +import v4.lens.affected_set { + ReExecFrontier, + ChangedSubgraphFrontier, + BoundaryExclusion, + UnchangedPureBoundary, + ExclusionKey, + Rerun, + Excluded, + EmptyDimensionDelta, + WholeDagFailClosed, + WholeDagReason, + WholeDagDiagnostic +} + + +data affected_set_claim_path_symbol: Symbol = affected_set_claim_path_symbol +data affected_set_claim_dimension: Symbol = affected_set_claim_dimension +data affected_set_claim_reason: Symbol = affected_set_claim_reason +data affected_set_claim_hash: Hash = affected_set_claim_hash + + +data affected_set_claim_path: Path = Path { steps: [affected_set_claim_path_symbol] } + + +data affected_set_claim_edit: Edit = Edit { + at: affected_set_claim_path, + replacement: stub_empty_conj +} + + +data affected_set_claim_invalid_edit_diagnostic: Diagnostic = Diagnostic { + reason: affected_set_claim_reason, + at: NodeLocus { node: stub_empty_conj }, + correction: Unavailable { reason: ExternalContractUnknown } +} + + +data expected_affected_set_boundary_prune: ReExecFrontier = ChangedSubgraphFrontier { + boundary_exclusions: [ + BoundaryExclusion { + edit: affected_set_claim_edit, + proof: UnchangedPureBoundary { + subject: affected_set_claim_path, + content_hash: affected_set_claim_hash + } + } + ], + decisions: empty_map() +} + + +data expected_affected_set_dimension_seed: ReExecFrontier = ChangedSubgraphFrontier { + boundary_exclusions: [], + decisions: map_insert( + empty_map(), + ExclusionKey { at: affected_set_claim_path, dimension: affected_set_claim_dimension }, + Rerun + ) +} + + +data expected_affected_set_excluded_proofs: ReExecFrontier = ChangedSubgraphFrontier { + boundary_exclusions: [], + decisions: map_insert( + empty_map(), + ExclusionKey { at: affected_set_claim_path, dimension: affected_set_claim_dimension }, + Excluded { + proofs: [ + EmptyDimensionDelta { subject: affected_set_claim_path }, + EmptyDimensionDelta { subject: affected_set_claim_path } + ] + } + ) +} + + +data expected_affected_set_unknown_fail_closed: ReExecFrontier = WholeDagFailClosed { + evidence: WholeDagReason { reason: affected_set_claim_reason } +} + + +data expected_affected_set_rejected_edit_fail_closed: ReExecFrontier = WholeDagFailClosed { + evidence: WholeDagDiagnostic { diagnostic: affected_set_claim_invalid_edit_diagnostic } +} + + +data claim_affected_set_boundary_prune_explicit: TestClaim = TestClaim { + kind: Equals, + label: "affected_set: unchanged/pure boundary pruning surfaces explicit edit-scoped BoundaryExclusionProof", + t19_anchor: T19ManualAnchorAbsent, + input: stub_empty_conj, + expected: Produced { value: stub_empty_conj }, + classification: TestClassification { tier: Tier1, layer: Unit } +} + + +data claim_affected_set_changed_dimension_seeds_rerun: TestClaim = TestClaim { + kind: Equals, + label: "affected_set: non-empty changed dimensions seed one Rerun per real AffectedDimension", + t19_anchor: T19ManualAnchorAbsent, + input: stub_empty_conj, + expected: Produced { value: stub_empty_conj }, + classification: TestClassification { tier: Tier1, layer: Unit } +} + + +data claim_affected_set_unknowns_fail_closed: TestClaim = TestClaim { + kind: Equals, + label: "affected_set: ChangedDimensionsUnknown and PropagationUnknown collapse to WholeDagFailClosed", + t19_anchor: T19ManualAnchorAbsent, + input: stub_empty_conj, + expected: Produced { value: stub_empty_conj }, + classification: TestClassification { tier: Tier1, layer: Boundary } +} + + +data claim_affected_set_post_edit_rejected_preserves_diagnostic: TestClaim = TestClaim { + kind: Equals, + label: "affected_set: PostEditDagRejected preserves Diagnostic in WholeDagFailClosedEvidence", + t19_anchor: T19ManualAnchorAbsent, + input: stub_empty_conj, + expected: Produced { value: stub_empty_conj }, + classification: TestClassification { tier: Tier1, layer: Boundary } +} From 49cdd7dbdaa590e5c6107bd1f68457212dd6fc44 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 10:06:08 -0400 Subject: [PATCH 30/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/lens/affected_set.dag | 123 +++++++++++------- .../claim/manual/affected_set_anchors.dag | 58 +-------- 2 files changed, 81 insertions(+), 100 deletions(-) diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 59d0af9f440..6c7131df19f 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -1,6 +1,6 @@ // src/v4/lens/affected_set.dag // Scope: canonical affected-set authority for CI/incremental re-execution; fail-closed B-4/B-5 node frontier. -// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, BoundaryExclusionProof, BoundaryExclusion, WholeDagFailClosedEvidence, FrontierDecision, ReExecFrontier, AffectedSetRead, AffectedPostEditDagReceipt, HashReceipt, ChangedDimensionReceipt, affected_set. +// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, BoundaryExclusionProof, BoundaryExclusion, WholeDagFailClosedEvidence, FrontierDecision, ReExecFrontier, AffectedSetRead, AffectedPostEditDagReceipt, HashReceipt, ChangedDimensionReceipt, FrontierReceipt, affected_set. // Consumes: Map, Diagnostic, Diff, Edit, Hash, Node, Path, Symbol, Witness; B1 content_hash receipts; B-4 resolved Symbol equality; B-5 per-Arrow signature effects. // Status: Wave-0 front: bounded changed-subgraph frontier fold landed; dissolves when CI use and .dag TestClaims land. @@ -108,6 +108,13 @@ type PropagationReceipt | PropagationUnknown { reason: Symbol } +// 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; proves transitive frontier or fails closed. +type FrontierReceipt + = FrontierProduced { edges: List } + | FrontierUnknown { reason: Symbol } + | FrontierRejected { diagnostic: Diagnostic } + + type AffectedBoundaryRead { boundary_hash_receipt: fn(Dag, Path, Node) -> HashReceipt, changed_dimension_receipt: fn(Dag, Edit) -> ChangedDimensionReceipt @@ -115,7 +122,7 @@ type AffectedBoundaryRead { type AffectedFrontierRead { - proven_transitive_changed_subgraph_frontier: fn(Dag, EditDimension) -> List + proven_transitive_changed_subgraph_frontier: fn(Dag, EditDimension) -> FrontierReceipt } @@ -256,56 +263,30 @@ fn expand_dimension( edit: Edit, dimension: AffectedDimension ) -> ReExecFrontier { - fold( - read_model.frontier.proven_transitive_changed_subgraph_frontier( - dag, - EditDimension { edit: edit, dimension: dimension } - ), - init: frontier, - f: fn(acc2, edge) { - match read_model.propagation_receipt(dag, EditFrontierEdge { edit: edit, edge: edge }) { - Propagates => - mark_rerun(frontier: acc2, key: edge_key(edge: edge)) - DoesNotPropagate { proof: p } => - mark_excluded(frontier: acc2, key: edge_key(edge: edge), proof: p) - PropagationUnknown { reason: r } => - mark_whole_dag(reason: r) - } - }) -} - - -fn expand_changed_dimensions( - read_model: AffectedSetRead, - boundary_dag: Dag, - frontier_dag: Dag, - frontier: ReExecFrontier, - edit: Edit -) -> ReExecFrontier { - match read_model.boundary.changed_dimension_receipt(boundary_dag, edit) { - ChangedDimensions { first: first_dimension, rest: rest_dimensions } => - fold(rest_dimensions, init: expand_dimension( - read_model: read_model, - dag: frontier_dag, - frontier: mark_rerun(frontier: frontier, key: root_key(path: edit.at, dimension: first_dimension)), - edit: edit, - dimension: first_dimension - ), f: fn(acc2, dimension) { - expand_dimension( - read_model: read_model, - dag: frontier_dag, - frontier: mark_rerun(frontier: acc2, key: root_key(path: edit.at, dimension: dimension)), - edit: edit, - dimension: dimension - ) + match read_model.frontier.proven_transitive_changed_subgraph_frontier( + dag, + EditDimension { edit: edit, dimension: dimension } + ) { + FrontierProduced { edges: edges } => + fold(edges, init: frontier, f: fn(acc2, edge) { + match read_model.propagation_receipt(dag, EditFrontierEdge { edit: edit, edge: edge }) { + Propagates => + mark_rerun(frontier: acc2, key: edge_key(edge: edge)) + DoesNotPropagate { proof: p } => + mark_excluded(frontier: acc2, key: edge_key(edge: edge), proof: p) + PropagationUnknown { reason: r } => + mark_whole_dag(reason: r) + } }) - ChangedDimensionsUnknown { reason: r } => + FrontierUnknown { reason: r } => mark_whole_dag(reason: r) + FrontierRejected { diagnostic: d } => + mark_whole_dag_diagnostic(diagnostic: d) } } -fn seed_edit_fold_acc( +fn seed_edit_fold_acc_live( read_model: AffectedSetRead, post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt, acc: AffectedFoldAcc, @@ -346,6 +327,56 @@ fn seed_edit_fold_acc( dag: acc.dag, frontier: mark_whole_dag_diagnostic(diagnostic: d) } + } + } +} + + +fn expand_changed_dimensions( + read_model: AffectedSetRead, + boundary_dag: Dag, + frontier_dag: Dag, + frontier: ReExecFrontier, + edit: Edit +) -> ReExecFrontier { + match read_model.boundary.changed_dimension_receipt(boundary_dag, edit) { + ChangedDimensions { first: first_dimension, rest: rest_dimensions } => + fold(rest_dimensions, init: expand_dimension( + read_model: read_model, + dag: frontier_dag, + frontier: mark_rerun(frontier: frontier, key: root_key(path: edit.at, dimension: first_dimension)), + edit: edit, + dimension: first_dimension + ), f: fn(acc2, dimension) { + expand_dimension( + read_model: read_model, + dag: frontier_dag, + frontier: mark_rerun(frontier: acc2, key: root_key(path: edit.at, dimension: dimension)), + edit: edit, + dimension: dimension + ) + }) + ChangedDimensionsUnknown { reason: r } => + mark_whole_dag(reason: r) + } +} + + +fn seed_edit_fold_acc( + read_model: AffectedSetRead, + post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt, + acc: AffectedFoldAcc, + edit: Edit +) -> AffectedFoldAcc { + match acc.frontier { + WholeDagFailClosed { evidence: _ } => acc + ChangedSubgraphFrontier { boundary_exclusions: _, decisions: _ } => + seed_edit_fold_acc_live( + read_model: read_model, + post_edit_dag: post_edit_dag, + acc: acc, + edit: edit + ) } } diff --git a/src/v4/test/claim/manual/affected_set_anchors.dag b/src/v4/test/claim/manual/affected_set_anchors.dag index ab078e9f0e4..f788ca49b8a 100644 --- a/src/v4/test/claim/manual/affected_set_anchors.dag +++ b/src/v4/test/claim/manual/affected_set_anchors.dag @@ -1,22 +1,12 @@ // src/v4/test/claim/manual/affected_set_anchors.dag -// Scope: Manual TestClaim anchors for T-21 affected_set Wave-0 gate cases. -// Owns: affected-set expected frontier rows + claim_affected_set_* TestClaim data -// Consumes: v4.std.verification v4.std.diagnostic v4.std.node v4.lens.affected_set -// Status: v2 compile; claim execution deferred (T-22). +// Scope: Manual expected-frontier anchors for T-21 affected_set Wave-0 gate cases. +// Owns: affected-set expected frontier rows +// Consumes: v4.std.diagnostic v4.std.node v4.lens.affected_set +// Status: non-executable staging data until T-22 can assert ReExecFrontier TestClaims. module v4.test.claim.manual.affected_set_anchors -import v4.std.verification { - TestClaim, - Equals, - TestClassification, - Tier1, - Unit, - Boundary, - T19ManualAnchorAbsent -} import v4.std.diagnostic { - Produced, Diagnostic, NodeLocus, Unavailable, @@ -108,43 +98,3 @@ data expected_affected_set_unknown_fail_closed: ReExecFrontier = WholeDagFailClo data expected_affected_set_rejected_edit_fail_closed: ReExecFrontier = WholeDagFailClosed { evidence: WholeDagDiagnostic { diagnostic: affected_set_claim_invalid_edit_diagnostic } } - - -data claim_affected_set_boundary_prune_explicit: TestClaim = TestClaim { - kind: Equals, - label: "affected_set: unchanged/pure boundary pruning surfaces explicit edit-scoped BoundaryExclusionProof", - t19_anchor: T19ManualAnchorAbsent, - input: stub_empty_conj, - expected: Produced { value: stub_empty_conj }, - classification: TestClassification { tier: Tier1, layer: Unit } -} - - -data claim_affected_set_changed_dimension_seeds_rerun: TestClaim = TestClaim { - kind: Equals, - label: "affected_set: non-empty changed dimensions seed one Rerun per real AffectedDimension", - t19_anchor: T19ManualAnchorAbsent, - input: stub_empty_conj, - expected: Produced { value: stub_empty_conj }, - classification: TestClassification { tier: Tier1, layer: Unit } -} - - -data claim_affected_set_unknowns_fail_closed: TestClaim = TestClaim { - kind: Equals, - label: "affected_set: ChangedDimensionsUnknown and PropagationUnknown collapse to WholeDagFailClosed", - t19_anchor: T19ManualAnchorAbsent, - input: stub_empty_conj, - expected: Produced { value: stub_empty_conj }, - classification: TestClassification { tier: Tier1, layer: Boundary } -} - - -data claim_affected_set_post_edit_rejected_preserves_diagnostic: TestClaim = TestClaim { - kind: Equals, - label: "affected_set: PostEditDagRejected preserves Diagnostic in WholeDagFailClosedEvidence", - t19_anchor: T19ManualAnchorAbsent, - input: stub_empty_conj, - expected: Produced { value: stub_empty_conj }, - classification: TestClassification { tier: Tier1, layer: Boundary } -} From 70b6d9d0507b56c4e67cd780ae63b30103235108 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 10:09:04 -0400 Subject: [PATCH 31/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 31 +++++++++++++++++++++++++------ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index 0b94c5b551d..b836faa089e 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt`, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove per-key dimension/edge exclusion evidence cases. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `ExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt`, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove per-key dimension/edge exclusion evidence cases. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `ExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 6c7131df19f..a17d3f798f9 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -256,6 +256,29 @@ fn boundary_receipt(read_model: AffectedSetRead, dag: Dag, path: Path, replaceme } +fn frontier_receipt( + read_model: AffectedSetRead, + dag: Dag, + edit: Edit, + dimension: AffectedDimension +) -> FrontierReceipt { + read_model.frontier.proven_transitive_changed_subgraph_frontier( + dag, + EditDimension { edit: edit, dimension: dimension } + ) +} + + +fn propagation_receipt( + read_model: AffectedSetRead, + dag: Dag, + edit: Edit, + edge: FrontierEdge +) -> PropagationReceipt { + read_model.propagation_receipt(dag, EditFrontierEdge { edit: edit, edge: edge }) +} + + fn expand_dimension( read_model: AffectedSetRead, dag: Dag, @@ -263,13 +286,10 @@ fn expand_dimension( edit: Edit, dimension: AffectedDimension ) -> ReExecFrontier { - match read_model.frontier.proven_transitive_changed_subgraph_frontier( - dag, - EditDimension { edit: edit, dimension: dimension } - ) { + match frontier_receipt(read_model: read_model, dag: dag, edit: edit, dimension: dimension) { FrontierProduced { edges: edges } => fold(edges, init: frontier, f: fn(acc2, edge) { - match read_model.propagation_receipt(dag, EditFrontierEdge { edit: edit, edge: edge }) { + match propagation_receipt(read_model: read_model, dag: dag, edit: edit, edge: edge) { Propagates => mark_rerun(frontier: acc2, key: edge_key(edge: edge)) DoesNotPropagate { proof: p } => @@ -329,7 +349,6 @@ fn seed_edit_fold_acc_live( } } } -} fn expand_changed_dimensions( From f49dbcab9eaa16fd7b4b5941bdafc8b9ee61ab4a Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 10:11:55 -0400 Subject: [PATCH 32/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 14 +++++++++----- src/v4/test/claim/manual/affected_set_anchors.dag | 6 +++--- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index b836faa089e..d50a95fc3b1 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt`, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove per-key dimension/edge exclusion evidence cases. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `ExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt`, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index a17d3f798f9..36d5c22e73a 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -1,6 +1,6 @@ // src/v4/lens/affected_set.dag // Scope: canonical affected-set authority for CI/incremental re-execution; fail-closed B-4/B-5 node frontier. -// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, BoundaryExclusionProof, BoundaryExclusion, WholeDagFailClosedEvidence, FrontierDecision, ReExecFrontier, AffectedSetRead, AffectedPostEditDagReceipt, HashReceipt, ChangedDimensionReceipt, FrontierReceipt, affected_set. +// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, PropagationExclusionProof, BoundaryExclusionProof, BoundaryExclusion, WholeDagFailClosedEvidence, FrontierDecision, ReExecFrontier, AffectedSetRead, AffectedPostEditDagReceipt, HashReceipt, ChangedDimensionReceipt, FrontierReceipt, affected_set. // Consumes: Map, Diagnostic, Diff, Edit, Hash, Node, Path, Symbol, Witness; B1 content_hash receipts; B-4 resolved Symbol equality; B-5 per-Arrow signature effects. // Status: Wave-0 front: bounded changed-subgraph frontier fold landed; dissolves when CI use and .dag TestClaims land. @@ -46,7 +46,11 @@ type EditFrontierEdge { // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed type ExclusionProof = EmptyDimensionDelta { subject: Path } - | DimensionNotRead { producer: Path } + + +// 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed +type PropagationExclusionProof + = DimensionNotRead { producer: Path } // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed @@ -69,7 +73,7 @@ type WholeDagFailClosedEvidence // 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed type FrontierDecision = Rerun - | Excluded { proofs: List } + | Excluded { proofs: List } // 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed; IRT-1 whole/changed-subgraph gate. @@ -104,7 +108,7 @@ type ChangedDimensionReceipt // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when B-4/B-5 edge receipts are generated. type PropagationReceipt = Propagates - | DoesNotPropagate { proof: ExclusionProof } + | DoesNotPropagate { proof: PropagationExclusionProof } | PropagationUnknown { reason: Symbol } @@ -206,7 +210,7 @@ fn mark_boundary_excluded(frontier: ReExecFrontier, edit: Edit, proof: BoundaryE } -fn mark_excluded(frontier: ReExecFrontier, key: ExclusionKey, proof: ExclusionProof) -> ReExecFrontier { +fn mark_excluded(frontier: ReExecFrontier, key: ExclusionKey, proof: PropagationExclusionProof) -> ReExecFrontier { match frontier { WholeDagFailClosed { evidence: e } => WholeDagFailClosed { evidence: e } ChangedSubgraphFrontier { boundary_exclusions: boundary_exclusions, decisions: decisions } => diff --git a/src/v4/test/claim/manual/affected_set_anchors.dag b/src/v4/test/claim/manual/affected_set_anchors.dag index f788ca49b8a..51bea9690e7 100644 --- a/src/v4/test/claim/manual/affected_set_anchors.dag +++ b/src/v4/test/claim/manual/affected_set_anchors.dag @@ -22,7 +22,7 @@ import v4.lens.affected_set { ExclusionKey, Rerun, Excluded, - EmptyDimensionDelta, + DimensionNotRead, WholeDagFailClosed, WholeDagReason, WholeDagDiagnostic @@ -82,8 +82,8 @@ data expected_affected_set_excluded_proofs: ReExecFrontier = ChangedSubgraphFron ExclusionKey { at: affected_set_claim_path, dimension: affected_set_claim_dimension }, Excluded { proofs: [ - EmptyDimensionDelta { subject: affected_set_claim_path }, - EmptyDimensionDelta { subject: affected_set_claim_path } + DimensionNotRead { producer: affected_set_claim_path }, + DimensionNotRead { producer: affected_set_claim_path } ] } ) From e5d95ef5d6d03bdd700af89af9eaa897ac543210 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 11:06:49 -0400 Subject: [PATCH 33/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 3 +-- src/v4/test/claim/manual/affected_set_anchors.dag | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index efd00f4665a..397fa7f1c4c 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt`, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt`, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 36d5c22e73a..f70ecf6d6d6 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -55,7 +55,7 @@ type PropagationExclusionProof // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed type BoundaryExclusionProof - = UnchangedPureBoundary { subject: Path, content_hash: Hash } + = UnchangedPureBoundary { content_hash: Hash } type BoundaryExclusion { @@ -250,7 +250,6 @@ fn boundary_receipt(read_model: AffectedSetRead, dag: Dag, path: Path, replaceme HashUnchangedPure { content_hash: h } => BoundaryPruned { proof: UnchangedPureBoundary { - subject: path, content_hash: h } } diff --git a/src/v4/test/claim/manual/affected_set_anchors.dag b/src/v4/test/claim/manual/affected_set_anchors.dag index 51bea9690e7..2067a489fc8 100644 --- a/src/v4/test/claim/manual/affected_set_anchors.dag +++ b/src/v4/test/claim/manual/affected_set_anchors.dag @@ -56,7 +56,6 @@ data expected_affected_set_boundary_prune: ReExecFrontier = ChangedSubgraphFront BoundaryExclusion { edit: affected_set_claim_edit, proof: UnchangedPureBoundary { - subject: affected_set_claim_path, content_hash: affected_set_claim_hash } } From 33b96e06c516f599689ccc31e673a13f4ee24757 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 11:10:54 -0400 Subject: [PATCH 34/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/lens/affected_set.dag | 37 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index f70ecf6d6d6..3e347eac476 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -315,20 +315,20 @@ fn seed_edit_fold_acc_live( acc: AffectedFoldAcc, edit: Edit ) -> AffectedFoldAcc { - match post_edit_dag(acc.dag, edit) { - PostEditDagProduced { dag: next_dag } => - match boundary_receipt( - read_model: read_model, + match boundary_receipt( + read_model: read_model, + dag: acc.dag, + path: edit.at, + replacement: edit.replacement + ) { + BoundaryPruned { proof: p } => + AffectedFoldAcc { dag: acc.dag, - path: edit.at, - replacement: edit.replacement - ) { - BoundaryPruned { proof: p } => - AffectedFoldAcc { - dag: next_dag, - frontier: mark_boundary_excluded(frontier: acc.frontier, edit: edit, proof: p) - } - BoundaryExpands => + frontier: mark_boundary_excluded(frontier: acc.frontier, edit: edit, proof: p) + } + BoundaryExpands => + match post_edit_dag(acc.dag, edit) { + PostEditDagProduced { dag: next_dag } => AffectedFoldAcc { dag: next_dag, frontier: expand_changed_dimensions( @@ -339,18 +339,17 @@ fn seed_edit_fold_acc_live( edit: edit ) } - BoundaryUnknown { reason: r } => + PostEditDagRejected { diagnostic: d } => AffectedFoldAcc { - dag: next_dag, - frontier: mark_whole_dag(reason: r) + dag: acc.dag, + frontier: mark_whole_dag_diagnostic(diagnostic: d) } } - PostEditDagRejected { diagnostic: d } => + BoundaryUnknown { reason: r } => AffectedFoldAcc { dag: acc.dag, - frontier: mark_whole_dag_diagnostic(diagnostic: d) + frontier: mark_whole_dag(reason: r) } - } } From 7a87a038d7074736386a8da8d39ff96cb4a198d7 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 15:13:31 +0000 Subject: [PATCH 35/48] Fix affected-set boundary prune ordering --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index 397fa7f1c4c..0adf5724cc2 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt`, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must check unchanged/pure boundary receipts before post-edit application, prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt` only for expanding boundaries, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 3e347eac476..f049d1b07d9 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -351,6 +351,7 @@ fn seed_edit_fold_acc_live( frontier: mark_whole_dag(reason: r) } } +} fn expand_changed_dimensions( From b5cef0c9c0720deaa2877992c9c916dca054811c Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 11:32:10 -0400 Subject: [PATCH 36/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 8 ++++---- src/v4/test/claim/manual/affected_set_anchors.dag | 6 ++---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index 0adf5724cc2..fa81ae84c52 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must check unchanged/pure boundary receipts before post-edit application, prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt` only for expanding boundaries, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; when multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must check unchanged/pure boundary receipts before post-edit application, prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt` only for expanding boundaries, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; `Excluded` carries a non-empty `PropagationExclusionProof` head plus rest list, so proofless skip is unrepresentable. When multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index f049d1b07d9..aeac19bf60b 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -73,7 +73,7 @@ type WholeDagFailClosedEvidence // 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed type FrontierDecision = Rerun - | Excluded { proofs: List } + | Excluded { first: PropagationExclusionProof, rest: List } // 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed; IRT-1 whole/changed-subgraph gate. @@ -220,15 +220,15 @@ fn mark_excluded(frontier: ReExecFrontier, key: ExclusionKey, proof: Propagation boundary_exclusions: boundary_exclusions, decisions: decisions } - Some { value: Excluded { proofs: proofs } } => + Some { value: Excluded { first: first, rest: rest } } => ChangedSubgraphFrontier { boundary_exclusions: boundary_exclusions, - decisions: map_insert(decisions, key, Excluded { proofs: list_push(proofs, proof) }) + decisions: map_insert(decisions, key, Excluded { first: first, rest: list_push(rest, proof) }) } None => ChangedSubgraphFrontier { boundary_exclusions: boundary_exclusions, - decisions: map_insert(decisions, key, Excluded { proofs: [proof] }) + decisions: map_insert(decisions, key, Excluded { first: proof, rest: [] }) } } } diff --git a/src/v4/test/claim/manual/affected_set_anchors.dag b/src/v4/test/claim/manual/affected_set_anchors.dag index 2067a489fc8..b3fffc61dc9 100644 --- a/src/v4/test/claim/manual/affected_set_anchors.dag +++ b/src/v4/test/claim/manual/affected_set_anchors.dag @@ -80,10 +80,8 @@ data expected_affected_set_excluded_proofs: ReExecFrontier = ChangedSubgraphFron empty_map(), ExclusionKey { at: affected_set_claim_path, dimension: affected_set_claim_dimension }, Excluded { - proofs: [ - DimensionNotRead { producer: affected_set_claim_path }, - DimensionNotRead { producer: affected_set_claim_path } - ] + first: DimensionNotRead { producer: affected_set_claim_path }, + rest: [DimensionNotRead { producer: affected_set_claim_path }] } ) } From 12e75ad1f060ca367d92814dfd98980e29586a44 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 11:53:15 -0400 Subject: [PATCH 37/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 40 +++++++++++-------- .../claim/manual/affected_set_anchors.dag | 5 +++ 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index fa81ae84c52..3f9cb0c4c0c 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must check unchanged/pure boundary receipts before post-edit application, prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt` only for expanding boundaries, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; `Excluded` carries a non-empty `PropagationExclusionProof` head plus rest list, so proofless skip is unrepresentable. When multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must check unchanged/pure boundary receipts before post-edit application, prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt` only for expanding boundaries, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`; inside an edit, `WholeDagFailClosed` is absorbing across dimension and edge folds so later unknowns cannot replace earlier diagnostic evidence. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; `Excluded` carries a non-empty `PropagationExclusionProof` head plus rest list, so proofless skip is unrepresentable. When multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index aeac19bf60b..e20423e86aa 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -289,22 +289,30 @@ fn expand_dimension( edit: Edit, dimension: AffectedDimension ) -> ReExecFrontier { - match frontier_receipt(read_model: read_model, dag: dag, edit: edit, dimension: dimension) { - FrontierProduced { edges: edges } => - fold(edges, init: frontier, f: fn(acc2, edge) { - match propagation_receipt(read_model: read_model, dag: dag, edit: edit, edge: edge) { - Propagates => - mark_rerun(frontier: acc2, key: edge_key(edge: edge)) - DoesNotPropagate { proof: p } => - mark_excluded(frontier: acc2, key: edge_key(edge: edge), proof: p) - PropagationUnknown { reason: r } => - mark_whole_dag(reason: r) - } - }) - FrontierUnknown { reason: r } => - mark_whole_dag(reason: r) - FrontierRejected { diagnostic: d } => - mark_whole_dag_diagnostic(diagnostic: d) + match frontier { + WholeDagFailClosed { evidence: e } => WholeDagFailClosed { evidence: e } + ChangedSubgraphFrontier { boundary_exclusions: _, decisions: _ } => + match frontier_receipt(read_model: read_model, dag: dag, edit: edit, dimension: dimension) { + FrontierProduced { edges: edges } => + fold(edges, init: frontier, f: fn(acc2, edge) { + match acc2 { + WholeDagFailClosed { evidence: e } => WholeDagFailClosed { evidence: e } + ChangedSubgraphFrontier { boundary_exclusions: _, decisions: _ } => + match propagation_receipt(read_model: read_model, dag: dag, edit: edit, edge: edge) { + Propagates => + mark_rerun(frontier: acc2, key: edge_key(edge: edge)) + DoesNotPropagate { proof: p } => + mark_excluded(frontier: acc2, key: edge_key(edge: edge), proof: p) + PropagationUnknown { reason: r } => + mark_whole_dag(reason: r) + } + } + }) + FrontierUnknown { reason: r } => + mark_whole_dag(reason: r) + FrontierRejected { diagnostic: d } => + mark_whole_dag_diagnostic(diagnostic: d) + } } } diff --git a/src/v4/test/claim/manual/affected_set_anchors.dag b/src/v4/test/claim/manual/affected_set_anchors.dag index b3fffc61dc9..8a7698080e7 100644 --- a/src/v4/test/claim/manual/affected_set_anchors.dag +++ b/src/v4/test/claim/manual/affected_set_anchors.dag @@ -95,3 +95,8 @@ data expected_affected_set_unknown_fail_closed: ReExecFrontier = WholeDagFailClo data expected_affected_set_rejected_edit_fail_closed: ReExecFrontier = WholeDagFailClosed { evidence: WholeDagDiagnostic { diagnostic: affected_set_claim_invalid_edit_diagnostic } } + + +data expected_affected_set_whole_dag_absorbs_later_unknown: ReExecFrontier = WholeDagFailClosed { + evidence: WholeDagDiagnostic { diagnostic: affected_set_claim_invalid_edit_diagnostic } +} From 6bade1eea008cbc8a8999e7abb5fa9fe2d3c9ed4 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 16:13:20 +0000 Subject: [PATCH 38/48] Document affected-set non-empty proof bridge --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index 3f9cb0c4c0c..708ed9fc615 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must check unchanged/pure boundary receipts before post-edit application, prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt` only for expanding boundaries, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`; inside an edit, `WholeDagFailClosed` is absorbing across dimension and edge folds so later unknowns cannot replace earlier diagnostic evidence. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟒 terminal: it is the per-`ExclusionKey` changed-dimension outcome coproduct that makes rerun-vs-excluded mutually exclusive while leaving node identity solely on the key path resolved against the input `Dag`; `Excluded` carries a non-empty `PropagationExclusionProof` head plus rest list, so proofless skip is unrepresentable. When multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must check unchanged/pure boundary receipts before post-edit application, prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt` only for expanding boundaries, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`; inside an edit, `WholeDagFailClosed` is absorbing across dimension and edge folds so later unknowns cannot replace earlier diagnostic evidence. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟑 scaffold: the rerun-vs-excluded coproduct is the per-`ExclusionKey` changed-dimension outcome authority and leaves node identity solely on the key path resolved against the input `Dag`; its `Excluded { first, rest }` field shape is a bounded T-25 cardinality bridge that keeps proofless skip unrepresentable until the canonical `List where non_empty` / `NonEmptyList` refinement is exported from `std/collection.dag`. Named trigger = T-25-core publishes the checked non-empty `List` refinement, then rewrite `Excluded` to carry that canonical refined list instead of this local head/rest bridge. When multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index e20423e86aa..aff5e72f580 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -70,7 +70,7 @@ type WholeDagFailClosedEvidence | WholeDagDiagnostic { diagnostic: Diagnostic } -// 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed +// 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed type FrontierDecision = Rerun | Excluded { first: PropagationExclusionProof, rest: List } From 80e7e5b46c2c1270f37b4c1d23e0d9ce59373094 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 16:36:42 +0000 Subject: [PATCH 39/48] Import affected-set witness constructor --- src/v4/lens/affected_set.dag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index aff5e72f580..0fe6025558e 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -10,7 +10,7 @@ module v4.lens.affected_set import v4.std.collection { Map } import v4.std.diagnostic { Diagnostic } import v4.std.node { Diff, Edit, Hash, Node, Path, Symbol } -import v4.std.witness { Witness } +import v4.std.witness { Holds, Witness } type Dag = Node From 56c2bc1e57ccb8dcb628fe5f029a96fd4436ae87 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 12:53:11 -0400 Subject: [PATCH 40/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 19 +++++-------------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index 708ed9fc615..e8219ffe94b 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must check unchanged/pure boundary receipts before post-edit application, prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt` only for expanding boundaries, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`; inside an edit, `WholeDagFailClosed` is absorbing across dimension and edge folds so later unknowns cannot replace earlier diagnostic evidence. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟑 scaffold: the rerun-vs-excluded coproduct is the per-`ExclusionKey` changed-dimension outcome authority and leaves node identity solely on the key path resolved against the input `Dag`; its `Excluded { first, rest }` field shape is a bounded T-25 cardinality bridge that keeps proofless skip unrepresentable until the canonical `List where non_empty` / `NonEmptyList` refinement is exported from `std/collection.dag`. Named trigger = T-25-core publishes the checked non-empty `List` refinement, then rewrite `Excluded` to carry that canonical refined list instead of this local head/rest bridge. When multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must check unchanged/pure boundary receipts before post-edit application, prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt` only for expanding boundaries, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/fail-closed cases; valid-but-unproved boundary reads use `BoundaryFailClosed` with symbolic evidence, while invalid or non-resolving boundary reads use `BoundaryFailClosed` with diagnostic evidence. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`; inside an edit, `WholeDagFailClosed` is absorbing across dimension and edge folds so later unknowns cannot replace earlier diagnostic evidence. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟑 scaffold: the rerun-vs-excluded coproduct is the per-`ExclusionKey` changed-dimension outcome authority and leaves node identity solely on the key path resolved against the input `Dag`; its `Excluded { first, rest }` field shape is a bounded T-25 cardinality bridge that keeps proofless skip unrepresentable until the canonical `List where non_empty` / `NonEmptyList` refinement is exported from `std/collection.dag`. Named trigger = T-25-core publishes the checked non-empty `List` refinement, then rewrite `Excluded` to carry that canonical refined list instead of this local head/rest bridge. When multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 0fe6025558e..ddb92624539 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -96,7 +96,7 @@ type HashReceipt type BoundaryReceipt = BoundaryPruned { proof: BoundaryExclusionProof } | BoundaryExpands - | BoundaryUnknown { reason: Symbol } + | BoundaryFailClosed { evidence: WholeDagFailClosedEvidence } // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when per-edit dimension deltas are generated. @@ -120,7 +120,7 @@ type FrontierReceipt type AffectedBoundaryRead { - boundary_hash_receipt: fn(Dag, Path, Node) -> HashReceipt, + boundary_receipt: fn(Dag, Path, Node) -> BoundaryReceipt, changed_dimension_receipt: fn(Dag, Edit) -> ChangedDimensionReceipt } @@ -246,16 +246,7 @@ fn mark_whole_dag_diagnostic(diagnostic: Diagnostic) -> ReExecFrontier { fn boundary_receipt(read_model: AffectedSetRead, dag: Dag, path: Path, replacement: Node) -> BoundaryReceipt { - match read_model.boundary.boundary_hash_receipt(dag, path, replacement) { - HashUnchangedPure { content_hash: h } => - BoundaryPruned { - proof: UnchangedPureBoundary { - content_hash: h - } - } - HashChanged => BoundaryExpands - HashUnknown { reason: r } => BoundaryUnknown { reason: r } - } + read_model.boundary.boundary_receipt(dag, path, replacement) } @@ -353,10 +344,10 @@ fn seed_edit_fold_acc_live( frontier: mark_whole_dag_diagnostic(diagnostic: d) } } - BoundaryUnknown { reason: r } => + BoundaryFailClosed { evidence: e } => AffectedFoldAcc { dag: acc.dag, - frontier: mark_whole_dag(reason: r) + frontier: WholeDagFailClosed { evidence: e } } } } From bd0751ccc084ba9751336c076e07cf828c207e00 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 12:56:01 -0400 Subject: [PATCH 41/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 41 +++++++++++++++++++++++++----------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index e8219ffe94b..9a196184aec 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must check unchanged/pure boundary receipts before post-edit application, prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt` only for expanding boundaries, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/fail-closed cases; valid-but-unproved boundary reads use `BoundaryFailClosed` with symbolic evidence, while invalid or non-resolving boundary reads use `BoundaryFailClosed` with diagnostic evidence. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`; inside an edit, `WholeDagFailClosed` is absorbing across dimension and edge folds so later unknowns cannot replace earlier diagnostic evidence. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟑 scaffold: the rerun-vs-excluded coproduct is the per-`ExclusionKey` changed-dimension outcome authority and leaves node identity solely on the key path resolved against the input `Dag`; its `Excluded { first, rest }` field shape is a bounded T-25 cardinality bridge that keeps proofless skip unrepresentable until the canonical `List where non_empty` / `NonEmptyList` refinement is exported from `std/collection.dag`. Named trigger = T-25-core publishes the checked non-empty `List` refinement, then rewrite `Excluded` to carry that canonical refined list instead of this local head/rest bridge. When multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must check unchanged/pure boundary receipts before post-edit application, prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt` only for expanding boundaries, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases; `BoundaryUnknown` must probe `post_edit_dag` before whole-DAG fallback, preserving diagnostic evidence for invalid or non-resolving edits and using symbolic whole-DAG evidence only after post-edit application succeeds. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`; inside an edit, `WholeDagFailClosed` is absorbing across dimension and edge folds so later unknowns cannot replace earlier diagnostic evidence. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟑 scaffold: the rerun-vs-excluded coproduct is the per-`ExclusionKey` changed-dimension outcome authority and leaves node identity solely on the key path resolved against the input `Dag`; its `Excluded { first, rest }` field shape is a bounded T-25 cardinality bridge that keeps proofless skip unrepresentable until the canonical `List where non_empty` / `NonEmptyList` refinement is exported from `std/collection.dag`. Named trigger = T-25-core publishes the checked non-empty `List` refinement, then rewrite `Excluded` to carry that canonical refined list instead of this local head/rest bridge. When multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index ddb92624539..f155668680e 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -96,7 +96,7 @@ type HashReceipt type BoundaryReceipt = BoundaryPruned { proof: BoundaryExclusionProof } | BoundaryExpands - | BoundaryFailClosed { evidence: WholeDagFailClosedEvidence } + | BoundaryUnknown { reason: Symbol } // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when per-edit dimension deltas are generated. @@ -119,14 +119,14 @@ type FrontierReceipt | FrontierRejected { diagnostic: Diagnostic } -type AffectedBoundaryRead { - boundary_receipt: fn(Dag, Path, Node) -> BoundaryReceipt, - changed_dimension_receipt: fn(Dag, Edit) -> ChangedDimensionReceipt +type AffectedFrontierRead { + proven_transitive_changed_subgraph_frontier: fn(Dag, EditDimension) -> FrontierReceipt } -type AffectedFrontierRead { - proven_transitive_changed_subgraph_frontier: fn(Dag, EditDimension) -> FrontierReceipt +type AffectedBoundaryRead { + boundary_hash_receipt: fn(Dag, Path, Node) -> HashReceipt, + changed_dimension_receipt: fn(Dag, Edit) -> ChangedDimensionReceipt } @@ -138,7 +138,7 @@ type AffectedPostEditDagReceipt type AffectedSetRead { boundary: AffectedBoundaryRead - frontier: AffectedFrontierRead + frontier: AffectedFrontierRead, propagation_receipt: fn(Dag, EditFrontierEdge) -> PropagationReceipt } @@ -246,7 +246,16 @@ fn mark_whole_dag_diagnostic(diagnostic: Diagnostic) -> ReExecFrontier { fn boundary_receipt(read_model: AffectedSetRead, dag: Dag, path: Path, replacement: Node) -> BoundaryReceipt { - read_model.boundary.boundary_receipt(dag, path, replacement) + match read_model.boundary.boundary_hash_receipt(dag, path, replacement) { + HashUnchangedPure { content_hash: h } => + BoundaryPruned { + proof: UnchangedPureBoundary { + content_hash: h + } + } + HashChanged => BoundaryExpands + HashUnknown { reason: r } => BoundaryUnknown { reason: r } + } } @@ -344,10 +353,18 @@ fn seed_edit_fold_acc_live( frontier: mark_whole_dag_diagnostic(diagnostic: d) } } - BoundaryFailClosed { evidence: e } => - AffectedFoldAcc { - dag: acc.dag, - frontier: WholeDagFailClosed { evidence: e } + BoundaryUnknown { reason: r } => + match post_edit_dag(acc.dag, edit) { + PostEditDagProduced { dag: _ } => + AffectedFoldAcc { + dag: acc.dag, + frontier: mark_whole_dag(reason: r) + } + PostEditDagRejected { diagnostic: d } => + AffectedFoldAcc { + dag: acc.dag, + frontier: mark_whole_dag_diagnostic(diagnostic: d) + } } } } From b66bc15dbd9efbce41694c5074874dce00f58604 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 13:26:12 -0400 Subject: [PATCH 42/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 27 +++++++++++++++++++++------ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index 9a196184aec..f29adb41d6b 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must check unchanged/pure boundary receipts before post-edit application, prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt` only for expanding boundaries, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases; `BoundaryUnknown` must probe `post_edit_dag` before whole-DAG fallback, preserving diagnostic evidence for invalid or non-resolving edits and using symbolic whole-DAG evidence only after post-edit application succeeds. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`; inside an edit, `WholeDagFailClosed` is absorbing across dimension and edge folds so later unknowns cannot replace earlier diagnostic evidence. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟑 scaffold: the rerun-vs-excluded coproduct is the per-`ExclusionKey` changed-dimension outcome authority and leaves node identity solely on the key path resolved against the input `Dag`; its `Excluded { first, rest }` field shape is a bounded T-25 cardinality bridge that keeps proofless skip unrepresentable until the canonical `List where non_empty` / `NonEmptyList` refinement is exported from `std/collection.dag`. Named trigger = T-25-core publishes the checked non-empty `List` refinement, then rewrite `Excluded` to carry that canonical refined list instead of this local head/rest bridge. When multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must check unchanged/pure boundary receipts before post-edit application, prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt` only for expanding boundaries, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases; `BoundaryUnknown` must probe `post_edit_dag` before whole-DAG fallback, preserving diagnostic evidence for invalid or non-resolving edits and using symbolic whole-DAG evidence only after post-edit application succeeds. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated; `EditFrontierEdge.changed_dimension` carries the source changed dimension into propagation, while `FrontierEdge.consumer_dimension` is the consumer/key dimension used for the resulting `ExclusionKey`, so propagation cannot conflate or drop either axis. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`; inside an edit, `WholeDagFailClosed` is absorbing across dimension and edge folds so later unknowns cannot replace earlier diagnostic evidence. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟑 scaffold: the rerun-vs-excluded coproduct is the per-`ExclusionKey` changed-dimension outcome authority and leaves node identity solely on the key path resolved against the input `Dag`; its `Excluded { first, rest }` field shape is a bounded T-25 cardinality bridge that keeps proofless skip unrepresentable until the canonical `List where non_empty` / `NonEmptyList` refinement is exported from `std/collection.dag`. Named trigger = T-25-core publishes the checked non-empty `List` refinement, then rewrite `Excluded` to carry that canonical refined list instead of this local head/rest bridge. When multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index f155668680e..4958a84b485 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -1,13 +1,13 @@ // src/v4/lens/affected_set.dag // Scope: canonical affected-set authority for CI/incremental re-execution; fail-closed B-4/B-5 node frontier. // Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, PropagationExclusionProof, BoundaryExclusionProof, BoundaryExclusion, WholeDagFailClosedEvidence, FrontierDecision, ReExecFrontier, AffectedSetRead, AffectedPostEditDagReceipt, HashReceipt, ChangedDimensionReceipt, FrontierReceipt, affected_set. -// Consumes: Map, Diagnostic, Diff, Edit, Hash, Node, Path, Symbol, Witness; B1 content_hash receipts; B-4 resolved Symbol equality; B-5 per-Arrow signature effects. +// Consumes: List, Map, Diagnostic, Diff, Edit, Hash, Node, Path, Symbol, Witness; B1 content_hash receipts; B-4 resolved Symbol equality; B-5 per-Arrow signature effects. // Status: Wave-0 front: bounded changed-subgraph frontier fold landed; dissolves when CI use and .dag TestClaims land. module v4.lens.affected_set -import v4.std.collection { Map } +import v4.std.collection { List, Map } import v4.std.diagnostic { Diagnostic } import v4.std.node { Diff, Edit, Hash, Node, Path, Symbol } import v4.std.witness { Holds, Witness } @@ -27,7 +27,7 @@ type ExclusionKey { type FrontierEdge { producer: Path consumer: Path - dimension: AffectedDimension + consumer_dimension: AffectedDimension } @@ -39,6 +39,7 @@ type EditDimension { type EditFrontierEdge { edit: Edit + changed_dimension: AffectedDimension edge: FrontierEdge } @@ -181,7 +182,7 @@ fn root_key(path: Path, dimension: AffectedDimension) -> ExclusionKey { fn edge_key(edge: FrontierEdge) -> ExclusionKey { ExclusionKey { at: edge.consumer, - dimension: edge.dimension + dimension: edge.consumer_dimension } } @@ -276,9 +277,17 @@ fn propagation_receipt( read_model: AffectedSetRead, dag: Dag, edit: Edit, + dimension: AffectedDimension, edge: FrontierEdge ) -> PropagationReceipt { - read_model.propagation_receipt(dag, EditFrontierEdge { edit: edit, edge: edge }) + read_model.propagation_receipt( + dag, + EditFrontierEdge { + edit: edit, + changed_dimension: dimension, + edge: edge + } + ) } @@ -298,7 +307,13 @@ fn expand_dimension( match acc2 { WholeDagFailClosed { evidence: e } => WholeDagFailClosed { evidence: e } ChangedSubgraphFrontier { boundary_exclusions: _, decisions: _ } => - match propagation_receipt(read_model: read_model, dag: dag, edit: edit, edge: edge) { + match propagation_receipt( + read_model: read_model, + dag: dag, + edit: edit, + dimension: dimension, + edge: edge + ) { Propagates => mark_rerun(frontier: acc2, key: edge_key(edge: edge)) DoesNotPropagate { proof: p } => From 9a3e51f0b00ddf52d41aa6cd08a65ae787542cbd Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 14:14:22 -0400 Subject: [PATCH 43/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/lens/affected_set_examples.dag | 107 ++++++++++++++++++ .../claim/manual/affected_set_anchors.dag | 102 ----------------- 2 files changed, 107 insertions(+), 102 deletions(-) create mode 100644 src/v4/lens/affected_set_examples.dag delete mode 100644 src/v4/test/claim/manual/affected_set_anchors.dag diff --git a/src/v4/lens/affected_set_examples.dag b/src/v4/lens/affected_set_examples.dag new file mode 100644 index 00000000000..72448d706da --- /dev/null +++ b/src/v4/lens/affected_set_examples.dag @@ -0,0 +1,107 @@ +// src/v4/lens/affected_set_examples.dag +// Scope: Expected affected-frontier example values for the affected-set lens. +// Owns: affected-set expected frontier rows +// Consumes: v4.std.diagnostic v4.std.node v4.lens.affected_set +// Status: example data for the affected-set frontier model. + +module v4.lens.affected_set_examples + +import v4.std.diagnostic { + Diagnostic, + NodeLocus, + Unavailable, + ExternalContractUnknown +} +import v4.std.node { Conj, Edit, Hash, Node, Path, Symbol, TypeNode } +import v4.lens.affected_set { + ReExecFrontier, + ChangedSubgraphFrontier, + BoundaryExclusion, + UnchangedPureBoundary, + ExclusionKey, + Rerun, + Excluded, + DimensionNotRead, + WholeDagFailClosed, + WholeDagReason, + WholeDagDiagnostic +} + + +data affected_set_example_path_symbol: Symbol = affected_set_example_path_symbol +data affected_set_example_dimension: Symbol = affected_set_example_dimension +data affected_set_example_reason: Symbol = affected_set_example_reason +data affected_set_example_hash: Hash = affected_set_example_hash + + +data affected_set_example_node: Node = Node { + kind: TypeNode { connective: Conj }, + children: [] +} + + +data affected_set_example_path: Path = Path { steps: [affected_set_example_path_symbol] } + + +data affected_set_example_edit: Edit = Edit { + at: affected_set_example_path, + replacement: affected_set_example_node +} + + +data affected_set_example_invalid_edit_diagnostic: Diagnostic = Diagnostic { + reason: affected_set_example_reason, + at: NodeLocus { node: affected_set_example_node }, + correction: Unavailable { reason: ExternalContractUnknown } +} + + +data expected_affected_set_boundary_prune: ReExecFrontier = ChangedSubgraphFrontier { + boundary_exclusions: [ + BoundaryExclusion { + edit: affected_set_example_edit, + proof: UnchangedPureBoundary { + content_hash: affected_set_example_hash + } + } + ], + decisions: empty_map() +} + + +data expected_affected_set_dimension_seed: ReExecFrontier = ChangedSubgraphFrontier { + boundary_exclusions: [], + decisions: map_insert( + empty_map(), + ExclusionKey { at: affected_set_example_path, dimension: affected_set_example_dimension }, + Rerun + ) +} + + +data expected_affected_set_excluded_proofs: ReExecFrontier = ChangedSubgraphFrontier { + boundary_exclusions: [], + decisions: map_insert( + empty_map(), + ExclusionKey { at: affected_set_example_path, dimension: affected_set_example_dimension }, + Excluded { + first: DimensionNotRead { producer: affected_set_example_path }, + rest: [DimensionNotRead { producer: affected_set_example_path }] + } + ) +} + + +data expected_affected_set_unknown_fail_closed: ReExecFrontier = WholeDagFailClosed { + evidence: WholeDagReason { reason: affected_set_example_reason } +} + + +data expected_affected_set_rejected_edit_fail_closed: ReExecFrontier = WholeDagFailClosed { + evidence: WholeDagDiagnostic { diagnostic: affected_set_example_invalid_edit_diagnostic } +} + + +data expected_affected_set_whole_dag_absorbs_later_unknown: ReExecFrontier = WholeDagFailClosed { + evidence: WholeDagDiagnostic { diagnostic: affected_set_example_invalid_edit_diagnostic } +} diff --git a/src/v4/test/claim/manual/affected_set_anchors.dag b/src/v4/test/claim/manual/affected_set_anchors.dag deleted file mode 100644 index 8a7698080e7..00000000000 --- a/src/v4/test/claim/manual/affected_set_anchors.dag +++ /dev/null @@ -1,102 +0,0 @@ -// src/v4/test/claim/manual/affected_set_anchors.dag -// Scope: Manual expected-frontier anchors for T-21 affected_set Wave-0 gate cases. -// Owns: affected-set expected frontier rows -// Consumes: v4.std.diagnostic v4.std.node v4.lens.affected_set -// Status: non-executable staging data until T-22 can assert ReExecFrontier TestClaims. - -module v4.test.claim.manual.affected_set_anchors - -import v4.std.diagnostic { - Diagnostic, - NodeLocus, - Unavailable, - ExternalContractUnknown -} -import v4.std.node { Path, Edit, Hash, Symbol } -import v4.test.claim.manual.connective_anchors { stub_empty_conj } -import v4.lens.affected_set { - ReExecFrontier, - ChangedSubgraphFrontier, - BoundaryExclusion, - UnchangedPureBoundary, - ExclusionKey, - Rerun, - Excluded, - DimensionNotRead, - WholeDagFailClosed, - WholeDagReason, - WholeDagDiagnostic -} - - -data affected_set_claim_path_symbol: Symbol = affected_set_claim_path_symbol -data affected_set_claim_dimension: Symbol = affected_set_claim_dimension -data affected_set_claim_reason: Symbol = affected_set_claim_reason -data affected_set_claim_hash: Hash = affected_set_claim_hash - - -data affected_set_claim_path: Path = Path { steps: [affected_set_claim_path_symbol] } - - -data affected_set_claim_edit: Edit = Edit { - at: affected_set_claim_path, - replacement: stub_empty_conj -} - - -data affected_set_claim_invalid_edit_diagnostic: Diagnostic = Diagnostic { - reason: affected_set_claim_reason, - at: NodeLocus { node: stub_empty_conj }, - correction: Unavailable { reason: ExternalContractUnknown } -} - - -data expected_affected_set_boundary_prune: ReExecFrontier = ChangedSubgraphFrontier { - boundary_exclusions: [ - BoundaryExclusion { - edit: affected_set_claim_edit, - proof: UnchangedPureBoundary { - content_hash: affected_set_claim_hash - } - } - ], - decisions: empty_map() -} - - -data expected_affected_set_dimension_seed: ReExecFrontier = ChangedSubgraphFrontier { - boundary_exclusions: [], - decisions: map_insert( - empty_map(), - ExclusionKey { at: affected_set_claim_path, dimension: affected_set_claim_dimension }, - Rerun - ) -} - - -data expected_affected_set_excluded_proofs: ReExecFrontier = ChangedSubgraphFrontier { - boundary_exclusions: [], - decisions: map_insert( - empty_map(), - ExclusionKey { at: affected_set_claim_path, dimension: affected_set_claim_dimension }, - Excluded { - first: DimensionNotRead { producer: affected_set_claim_path }, - rest: [DimensionNotRead { producer: affected_set_claim_path }] - } - ) -} - - -data expected_affected_set_unknown_fail_closed: ReExecFrontier = WholeDagFailClosed { - evidence: WholeDagReason { reason: affected_set_claim_reason } -} - - -data expected_affected_set_rejected_edit_fail_closed: ReExecFrontier = WholeDagFailClosed { - evidence: WholeDagDiagnostic { diagnostic: affected_set_claim_invalid_edit_diagnostic } -} - - -data expected_affected_set_whole_dag_absorbs_later_unknown: ReExecFrontier = WholeDagFailClosed { - evidence: WholeDagDiagnostic { diagnostic: affected_set_claim_invalid_edit_diagnostic } -} From 5cad48f94d638162ffb08874bb273173285f2e13 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 14:21:59 -0400 Subject: [PATCH 44/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 2 +- src/v4/lens/affected_set_examples.dag | 12 ++++++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index 55197e68a6d..6d46d4eccc9 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must check unchanged/pure boundary receipts before post-edit application, prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt` only for expanding boundaries, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases; `BoundaryUnknown` must probe `post_edit_dag` before whole-DAG fallback, preserving diagnostic evidence for invalid or non-resolving edits and using symbolic whole-DAG evidence only after post-edit application succeeds. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated; `EditFrontierEdge.changed_dimension` carries the source changed dimension into propagation, while `FrontierEdge.consumer_dimension` is the consumer/key dimension used for the resulting `ExclusionKey`, so propagation cannot conflate or drop either axis. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`; inside an edit, `WholeDagFailClosed` is absorbing across dimension and edge folds so later unknowns cannot replace earlier diagnostic evidence. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟑 scaffold: the rerun-vs-excluded coproduct is the per-`ExclusionKey` changed-dimension outcome authority and leaves node identity solely on the key path resolved against the input `Dag`; its `Excluded { first, rest }` field shape is a bounded T-25 cardinality bridge that keeps proofless skip unrepresentable until the canonical `List where non_empty` / `NonEmptyList` refinement is exported from `std/collection.dag`. Named trigger = T-25-core publishes the checked non-empty `List` refinement, then rewrite `Excluded` to carry that canonical refined list instead of this local head/rest bridge. When multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must check unchanged/pure boundary receipts before post-edit application, prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt` only for expanding boundaries, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`; each proof preserves both the producer and source changed dimension so final exclusion evidence does not drop the axis carried by `EditFrontierEdge.changed_dimension`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases; `BoundaryUnknown` must probe `post_edit_dag` before whole-DAG fallback, preserving diagnostic evidence for invalid or non-resolving edits and using symbolic whole-DAG evidence only after post-edit application succeeds. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated; `EditFrontierEdge.changed_dimension` carries the source changed dimension into propagation, while `FrontierEdge.consumer_dimension` is the consumer/key dimension used for the resulting `ExclusionKey`, so propagation cannot conflate or drop either axis. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`; inside an edit, `WholeDagFailClosed` is absorbing across dimension and edge folds so later unknowns cannot replace earlier diagnostic evidence. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟑 scaffold: the rerun-vs-excluded coproduct is the per-`ExclusionKey` changed-dimension outcome authority and leaves node identity solely on the key path resolved against the input `Dag`; its `Excluded { first, rest }` field shape is a bounded T-25 cardinality bridge that keeps proofless skip unrepresentable until the canonical `List where non_empty` / `NonEmptyList` refinement is exported from `std/collection.dag`. Named trigger = T-25-core publishes the checked non-empty `List` refinement, then rewrite `Excluded` to carry that canonical refined list instead of this local head/rest bridge. When multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 4958a84b485..5a81731359d 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -51,7 +51,7 @@ type ExclusionProof // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed type PropagationExclusionProof - = DimensionNotRead { producer: Path } + = DimensionNotRead { producer: Path, changed_dimension: AffectedDimension } // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed diff --git a/src/v4/lens/affected_set_examples.dag b/src/v4/lens/affected_set_examples.dag index 72448d706da..cabd5ddd503 100644 --- a/src/v4/lens/affected_set_examples.dag +++ b/src/v4/lens/affected_set_examples.dag @@ -85,8 +85,16 @@ data expected_affected_set_excluded_proofs: ReExecFrontier = ChangedSubgraphFron empty_map(), ExclusionKey { at: affected_set_example_path, dimension: affected_set_example_dimension }, Excluded { - first: DimensionNotRead { producer: affected_set_example_path }, - rest: [DimensionNotRead { producer: affected_set_example_path }] + first: DimensionNotRead { + producer: affected_set_example_path, + changed_dimension: affected_set_example_dimension + }, + rest: [ + DimensionNotRead { + producer: affected_set_example_path, + changed_dimension: affected_set_example_dimension + } + ] } ) } From e6874be2a2228a0e2403826d7443caf90707ca2c Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 14:24:44 -0400 Subject: [PATCH 45/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- src/v4/STRUCTURE.md | 7 +++++-- src/v4/lens/affected_set.dag | 27 ++++++++++++--------------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index 6d46d4eccc9..3aa53ea578d 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must check unchanged/pure boundary receipts before post-edit application, prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `AffectedPostEditDagReceipt` only for expanding boundaries, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`; each proof preserves both the producer and source changed dimension so final exclusion evidence does not drop the axis carried by `EditFrontierEdge.changed_dimension`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases; `BoundaryUnknown` must probe `post_edit_dag` before whole-DAG fallback, preserving diagnostic evidence for invalid or non-resolving edits and using symbolic whole-DAG evidence only after post-edit application succeeds. `AffectedPostEditDagReceipt` is 🟑 scaffold; named trigger = dissolve into D1 `apply_diff` / `Outcome` when that read is substrate-produced. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated; `EditFrontierEdge.changed_dimension` carries the source changed dimension into propagation, while `FrontierEdge.consumer_dimension` is the consumer/key dimension used for the resulting `ExclusionKey`, so propagation cannot conflate or drop either axis. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`; inside an edit, `WholeDagFailClosed` is absorbing across dimension and edge folds so later unknowns cannot replace earlier diagnostic evidence. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟑 scaffold: the rerun-vs-excluded coproduct is the per-`ExclusionKey` changed-dimension outcome authority and leaves node identity solely on the key path resolved against the input `Dag`; its `Excluded { first, rest }` field shape is a bounded T-25 cardinality bridge that keeps proofless skip unrepresentable until the canonical `List where non_empty` / `NonEmptyList` refinement is exported from `std/collection.dag`. Named trigger = T-25-core publishes the checked non-empty `List` refinement, then rewrite `Excluded` to carry that canonical refined list instead of this local head/rest bridge. When multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `test/claim/manual/affected_set_anchors.dag` expected-frontier staging rows | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> Outcome` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must check unchanged/pure boundary receipts before post-edit application, prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `Outcome` only for expanding boundaries, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`; each proof preserves both the producer and source changed dimension so final exclusion evidence does not drop the axis carried by `EditFrontierEdge.changed_dimension`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases; `BoundaryUnknown` must probe `post_edit_dag` before whole-DAG fallback, preserving diagnostic evidence for invalid or non-resolving edits and using symbolic whole-DAG evidence only after post-edit application succeeds. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated; `EditFrontierEdge.changed_dimension` carries the source changed dimension into propagation, while `FrontierEdge.consumer_dimension` is the consumer/key dimension used for the resulting `ExclusionKey`, so propagation cannot conflate or drop either axis. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`; inside an edit, `WholeDagFailClosed` is absorbing across dimension and edge folds so later unknowns cannot replace earlier diagnostic evidence. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟑 scaffold: the rerun-vs-excluded coproduct is the per-`ExclusionKey` changed-dimension outcome authority and leaves node identity solely on the key path resolved against the input `Dag`; its `Excluded { first, rest }` field shape is a bounded T-25 cardinality bridge that keeps proofless skip unrepresentable until the canonical `List where non_empty` / `NonEmptyList` refinement is exported from `std/collection.dag`. Named trigger = T-25-core publishes the checked non-empty `List` refinement, then rewrite `Excluded` to carry that canonical refined list instead of this local head/rest bridge. When multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `lens/affected_set_examples.dag` expected-frontier example rows | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/STRUCTURE.md b/src/v4/STRUCTURE.md index 1a8cca4ff9c..7812d725436 100644 --- a/src/v4/STRUCTURE.md +++ b/src/v4/STRUCTURE.md @@ -75,7 +75,7 @@ src/v4/ 05_eval.dag # InferredTree + Inputs -> Value (THE PRIMARY execution path, # THESIS:225; sibling of emit β€” eval executes, emit projects) - lens/ # dimensions (12 files, parallel after compiler) + lens/ # dimensions (13 files, parallel after compiler) complexity.dag cost.dag # Tier 1 + Tier 2 textbook (Ξ±(n)/log*/log log/sub-exp); UnknownCost floor parallelism.dag @@ -86,6 +86,7 @@ src/v4/ coverage.dag # meta-lens β€” L6/L7/impossible-bug/testgen coverage discipline (structural) testgen.dag # producer side β€” reads substrate, emits TestClaim corpus (Phase 1.5) affected_set.dag # incremental re-exec frontier; replaces detect-affected shell (Phase 1.5) + affected_set_examples.dag # expected affected-frontier example values application.dag # apply_lens surface β€” opt-in depth + ONLY advisoryβ†’fail-closed bridge registry.dag # P2-staging only (INVARIANTS Β§P2): PREFIX T-23 v0 `LensIdV0` Γ— `LensModulePathV0` rows β€” **not** landed single authority until a **generated** consumer reads `LensRegistryEntryV0`; `v4_lens_registry_dag_smoke_test.rs` is **parse + inference cleanliness** only (same posture as `fact_density.dag`). Operator pin Β§3 human mirror; amend `.dag` first. @@ -124,7 +125,7 @@ src/v4/ fixture/ # canonical input programs ``` -**Total: 73 .dag files + 5 docs + 5 .gitkeep = 83 files.** (Per invariant +**Total: 75 .dag files + 5 docs + 5 .gitkeep = 85 files.** (Per invariant #1 the enumeration above β€” not the count β€” is authoritative; the count is a checksum, updated on every operator-ratified file addition/removal. **Reconciliation (2026-05-17, PR #3225 / review #13750):** the prior printed @@ -147,6 +148,8 @@ checksum **71β†’72** `.dag`. `docs/briefs/r4-lane-a-lens-interface-freeze-pin.md` Β§3); checksum **72β†’73** `.dag`. **P2-staging** (INVARIANTS Β§P2) until a generated consumer reads the rows β€” paired `v4_lens_registry_dag_smoke_test.rs` receipt (parse witness only; same discipline as `fact_density.dag`). +**2026-05-19 (#3349):** add `lens/affected_set_examples.dag` as expected affected-frontier example values; +checksum **74β†’75** `.dag`. ## Scalar/numeric concept decomposition diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index 5a81731359d..c3b2bae5703 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -1,6 +1,6 @@ // src/v4/lens/affected_set.dag // Scope: canonical affected-set authority for CI/incremental re-execution; fail-closed B-4/B-5 node frontier. -// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, PropagationExclusionProof, BoundaryExclusionProof, BoundaryExclusion, WholeDagFailClosedEvidence, FrontierDecision, ReExecFrontier, AffectedSetRead, AffectedPostEditDagReceipt, HashReceipt, ChangedDimensionReceipt, FrontierReceipt, affected_set. +// Owns: Dag, AffectedDimension, ExclusionKey, ExclusionProof, PropagationExclusionProof, BoundaryExclusionProof, BoundaryExclusion, WholeDagFailClosedEvidence, FrontierDecision, ReExecFrontier, AffectedSetRead, HashReceipt, ChangedDimensionReceipt, FrontierReceipt, affected_set. // Consumes: List, Map, Diagnostic, Diff, Edit, Hash, Node, Path, Symbol, Witness; B1 content_hash receipts; B-4 resolved Symbol equality; B-5 per-Arrow signature effects. // Status: Wave-0 front: bounded changed-subgraph frontier fold landed; dissolves when CI use and .dag TestClaims land. @@ -8,7 +8,7 @@ module v4.lens.affected_set import v4.std.collection { List, Map } -import v4.std.diagnostic { Diagnostic } +import v4.std.diagnostic { Diagnostic, Outcome, Produced, Rejected } import v4.std.node { Diff, Edit, Hash, Node, Path, Symbol } import v4.std.witness { Holds, Witness } @@ -131,12 +131,6 @@ type AffectedBoundaryRead { } -// 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves into D1 apply_diff Outcome. -type AffectedPostEditDagReceipt - = PostEditDagProduced { dag: Dag } - | PostEditDagRejected { diagnostic: Diagnostic } - - type AffectedSetRead { boundary: AffectedBoundaryRead frontier: AffectedFrontierRead, @@ -144,6 +138,9 @@ type AffectedSetRead { } +type AffectedPostEditDagOutcome = Outcome + + type AffectedFoldAcc { dag: Dag frontier: ReExecFrontier @@ -334,7 +331,7 @@ fn expand_dimension( fn seed_edit_fold_acc_live( read_model: AffectedSetRead, - post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt, + post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagOutcome, acc: AffectedFoldAcc, edit: Edit ) -> AffectedFoldAcc { @@ -351,7 +348,7 @@ fn seed_edit_fold_acc_live( } BoundaryExpands => match post_edit_dag(acc.dag, edit) { - PostEditDagProduced { dag: next_dag } => + Produced { value: next_dag } => AffectedFoldAcc { dag: next_dag, frontier: expand_changed_dimensions( @@ -362,7 +359,7 @@ fn seed_edit_fold_acc_live( edit: edit ) } - PostEditDagRejected { diagnostic: d } => + Rejected { diagnostic: d } => AffectedFoldAcc { dag: acc.dag, frontier: mark_whole_dag_diagnostic(diagnostic: d) @@ -370,12 +367,12 @@ fn seed_edit_fold_acc_live( } BoundaryUnknown { reason: r } => match post_edit_dag(acc.dag, edit) { - PostEditDagProduced { dag: _ } => + Produced { value: _ } => AffectedFoldAcc { dag: acc.dag, frontier: mark_whole_dag(reason: r) } - PostEditDagRejected { diagnostic: d } => + Rejected { diagnostic: d } => AffectedFoldAcc { dag: acc.dag, frontier: mark_whole_dag_diagnostic(diagnostic: d) @@ -417,7 +414,7 @@ fn expand_changed_dimensions( fn seed_edit_fold_acc( read_model: AffectedSetRead, - post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt, + post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagOutcome, acc: AffectedFoldAcc, edit: Edit ) -> AffectedFoldAcc { @@ -436,7 +433,7 @@ fn seed_edit_fold_acc( fn affected_set( read_model: AffectedSetRead, - post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagReceipt, + post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagOutcome, dag: Dag, diff: Diff ) -> Witness { From 6d99f26fae7e8d29652a2925ba544c833e3d0d41 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 14:27:55 -0400 Subject: [PATCH 46/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/DECISIONS.md | 2 +- src/v4/lens/affected_set.dag | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/v4/DECISIONS.md b/src/v4/DECISIONS.md index 3aa53ea578d..496d9a03056 100644 --- a/src/v4/DECISIONS.md +++ b/src/v4/DECISIONS.md @@ -50,7 +50,7 @@ authority location(s) and moves to Part 1. | **D1** | The structural-delta vocabulary over `Node` (side-session 2026-05-15; **shape refined** per codex P2 REQUEST_CHANGES on PR #3162). An `Edit` replaces the subtree at a `Path` with a `Node` (term-rewriting `t[s]p`). `Path = List`: each step descends the `Named` edge keyed by that opaque `Symbol` β€” flat & finite, NOT a new recursive type (A1); resolution = `Symbol` equality β‡’ K-1-clean. Name is **`Path`** (the route), not `Position` (operator pick); "position/occurrence" (JSON Pointer, RFC 6901, member-name tokens only) retained as anchor. **No positional/ordinal selector** (P2 finding 1): an `Int` ordinal admits negative/illegal indices = the representable-illegal-state node.dag designed out at `EdgeLabel`; positional-connective children are reached by replacing the enclosing `Named` subtree (the safe over-fire #3157 blesses); surgical positional addressing is a future operator-ratified extension (consumes-nothing root has no ordinal primitive β€” no kernel `Nat`; recursive Peano = A1 STOP). **`Diff` = ORDERED `List` applied SEQUENTIALLY** (P2 finding 2): no set / pairwise-independence / "parallel positions" invariant, no `diff_well_formed`/`paths_independent` β€” any `List` is a valid rewrite program; a non-resolving Edit β‡’ fail-closed Diagnostic at apply time (operational, not a type-level illegal state); `apply_diff` is a fold, all-or-nothing. **DATA TYPES in `std/node.dag`** (B1/`Hash` precedent: a structural delta over `Node` is substrate-tier; corrects the initial "owner = T-21" read). **OPERATIONS** (`subterm_at`/`apply_diff` β†’ `Result`) in **`lens/application.dag`**, FORCED: `std/diagnostic.dag` already `Consumes std/node.dag` (`Correction = Suggested(Node)`) so a node.dag op returning `Result<_,Diagnostic>` is a substrate cycle β€” the node.dag "consumers fail-close" discipline (`node_well_formed` precedent). `apply_diff` consumer = CLI / test harness / user app code. Two refinements of the in-session ratification were encoded via PR #3162 review (the normal confirm/redirect channel β€” not an open fork): (a) the data/op split (operations in `application.dag`, forced by the diagnosticβ†’node cycle); (b) the P2-driven shape change β€” no positional selector (Practice-4 dissolved; receipt in `std/node.dag`) and sequential `Diff` (no independence predicate). Operator redirect, if any, is via the PR like any change | this row + `std/node.dag` Path/Edit/Diff declarations + `lens/application.dag` operation contract + `lens/affected_set.dag` cross-ref | | **B-4** | affected_set R2 minimality contract (side-session 2026-05-15). For `lens/affected_set.dag` (T-21) to compute a *minimal* re-exec frontier, `compiler/04_infer.dag` resolution must be **NODE-PRECISE**: a use-site name-reference `Atom` binds to the **exact declaration `Node`**, never merely its module/scope (**carrier corrected** per a P2/Practice-3 review finding on #3162). The useβ†’def fact's named forward carrier is the post-resolution opaque **`Symbol`** (K-1) on the name-ref `Atom`, **NOT `resolved_type`** (that is the node's *type* β€” many declarations share a type, so it is lossy for binding identity; citing it was the P2/Practice-3 error). AUTHORITY = `compiler/03_resolve.dag` (it owns identifierβ†’declaration binding; resolution canonicalizes a use-site `Atom`'s `Symbol` to equal its binder's `Symbol`, so binding = `Symbol` equality β€” the K-1-granted op, node.dag `name_occurrences`). `04_infer` is **preserve-only** (must not alter/drop the resolved `Symbol`; no 5th `InferredFacts` field β€” IR-1-safe). `lens/affected_set.dag` **derives** the graph by `Symbol`-equality traversal over the resolved tree (same "derive, don't store" as `content_hash`/D1). Coarse (module/scope) resolution leaves affected_set *correct but maximally over-firing* β€” so node-precision is a **precision INVARIANT on T-9**, the dominant gate on R2 minimality, not a T-21 concern. Distinct axis from #3157's hash-granularity "over-fire = safe" tolerance (content identity vs dependency-edge granularity). No new type/field; IR-1-safe | this row + `compiler/03_resolve.dag`; `compiler/04_infer.dag` is a boundary index only | | **B-5** | affected_set R1 usefulness contract (side-session 2026-05-15). For purity-aware skipping to be *useful* (not merely correct), effect must be a **per-`Arrow`-Node fact** derived from that node's signature (the B3 authority) and **never aggregated to a coarser unit** (one ambient `IO`, per-module effect set) β€” a coarse aggregate is the parallel taxonomy B3 forbids (P2) *and* degrades R1 to "re-run ~everything" (still correct/fail-closed, useless). A **granularity invariant**, not effect-lens strength (effects are READ off the signature per B3 β‡’ strong by construction; the only risk is coarse modeling β€” this forbids collapsing the per-effect-KIND distinction the signature already carries). No new field. = #3153 Ex.R1, now encoded. **Corollary (effects-as-parameters, 2026-05-16):** effects are threaded as explicit parameters (DI-by-construction); a mock-injected unit test's B-4 edge points at the mock `Node`, so affected_set skips it and re-runs only real-carrier (integration) tests *automatically* β€” "affected" is consumer-relative with no unit/integration mode in the lens. Ambient/global effects re-couple the cone = the anti-pattern this forbids. Honest boundary: a re-run test with changed behavior may have a stale expected value; the lens surfaces "re-run", never rewrites the expectation (no-engine) | `lens/effect.dag` B-5 block (+corollary) + `docs/design-affected-set-lens.md` worked examples | -| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> Outcome` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must check unchanged/pure boundary receipts before post-edit application, prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by `Outcome` only for expanding boundaries, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`; each proof preserves both the producer and source changed dimension so final exclusion evidence does not drop the axis carried by `EditFrontierEdge.changed_dimension`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases; `BoundaryUnknown` must probe `post_edit_dag` before whole-DAG fallback, preserving diagnostic evidence for invalid or non-resolving edits and using symbolic whole-DAG evidence only after post-edit application succeeds. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated; `EditFrontierEdge.changed_dimension` carries the source changed dimension into propagation, while `FrontierEdge.consumer_dimension` is the consumer/key dimension used for the resulting `ExclusionKey`, so propagation cannot conflate or drop either axis. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`; inside an edit, `WholeDagFailClosed` is absorbing across dimension and edge folds so later unknowns cannot replace earlier diagnostic evidence. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟑 scaffold: the rerun-vs-excluded coproduct is the per-`ExclusionKey` changed-dimension outcome authority and leaves node identity solely on the key path resolved against the input `Dag`; its `Excluded { first, rest }` field shape is a bounded T-25 cardinality bridge that keeps proofless skip unrepresentable until the canonical `List where non_empty` / `NonEmptyList` refinement is exported from `std/collection.dag`. Named trigger = T-25-core publishes the checked non-empty `List` refinement, then rewrite `Excluded` to carry that canonical refined list instead of this local head/rest bridge. When multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `lens/affected_set_examples.dag` expected-frontier example rows | +| **#3153-subsumed** | `v4-affected-set-design` (#3153, no worker) is **superseded by this branch** (operator 2026-05-15): `docs/design-affected-set-lens.md` remains the prose design authority for the affected-set lens (design framing + 5 worked examples + dimension-aware/fail-closed exclusion discipline); `lens/affected_set.dag` owns only the terse machine-readable T-21 carrier/read surface under the strict de-prose rule. The branch-specific Ex.R1/Ex.R2/Ex.R3 labels are not a second prose authority; they are reconciled here to the now-*encoded* reciprocal contracts β€” Ex.R1β†’B-5 (`effect.dag`), Ex.R2β†’B-4 (`04_infer.dag`), Ex.R3β†’B-3 (the drafted #3157 B1-CANON point-3 correction). Dogfood-reality is tracked by the T-21 scaffold trigger rather than copied into the `.dag` file. IRT reconciliation (operator 2026-05-17): the decomposed `AffectedSetRead` model (`boundary_hash_receipt`, non-empty `changed_dimension_receipt`, `proven_transitive_changed_subgraph_frontier`, `propagation_receipt`) plus direct `post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagRead carrying `Outcome`` and top-level `affected_set(read_model, post_edit_dag, dag, diff)` must check unchanged/pure boundary receipts before post-edit application, prune only at unchanged/pure `content_hash` boundaries, seed every proven changed dimension for each edit, carry current-Dag state through the ordered `Diff` fold using the produced post-edit Dag or fail-closed diagnostic returned by the canonical `Outcome` only for expanding boundaries, fold an edit-aware proven transitive changed-subgraph frontier over the DAG, and make TestClaim result reuse the cache-dual keyed by the input subgraph `content_hash`; a whole-program traversal scaffold is rejected. Practice-4 coproduct classification: `ExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when the T-21 `affected_set` fold and `.dag` TestClaims prove root dimension-delta exclusion evidence cases. `PropagationExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when B-4/B-5 edge non-read evidence is substrate-produced for `DoesNotPropagate`; each proof preserves both the producer and source changed dimension so final exclusion evidence does not drop the axis carried by `EditFrontierEdge.changed_dimension`. `BoundaryExclusionProof` is 🟑 scaffold; named trigger = promote or dissolve when unchanged/pure boundary-prune evidence is folded into the final TestClaim exclusion receipt surface; it carries only proof evidence, with the ordered `BoundaryExclusion.edit` occurrence as the single path authority. `BoundaryExclusion` is 🟒 terminal: it pairs an ordered `Diff` edit occurrence with its boundary-prune proof in a list so repeated equal `Edit` values preserve occurrence evidence rather than collapsing through a map key. `HashReceipt` is 🟑 scaffold; named trigger = dissolve into B1 `content_hash` plus B-5 purity/effect receipts when those reads are substrate-produced. `BoundaryReceipt` is 🟑 scaffold; named trigger = dissolve when T-21 `.dag` TestClaims prove boundary prune/expand/unknown cases; `BoundaryUnknown` must probe `post_edit_dag` before whole-DAG fallback, preserving diagnostic evidence for invalid or non-resolving edits and using symbolic whole-DAG evidence only after post-edit application succeeds. `WholeDagFailClosedEvidence` is 🟑 scaffold; named trigger = dissolve when whole-DAG fail-closed reasons are all diagnostic-backed or terminal symbolic receipts. `ChangedDimensionReceipt` is 🟑 scaffold; named trigger = dissolve when non-empty per-edit dimension deltas are generated from D1 edits and B-5 effect dimensions, with empty/unknown deltas represented by the fail-closed unknown arm rather than an empty changed-dimension list. `FrontierReceipt` is 🟑 scaffold; named trigger = dissolve when the edit-aware transitive frontier read is substrate-produced with fail-closed unknown/diagnostic arms. `PropagationReceipt` is 🟑 scaffold; named trigger = dissolve when B-4/B-5 edge receipts are generated; `EditFrontierEdge.changed_dimension` carries the source changed dimension into propagation, while `FrontierEdge.consumer_dimension` is the consumer/key dimension used for the resulting `ExclusionKey`, so propagation cannot conflate or drop either axis. `ReExecFrontier` is 🟒 terminal: it is the IRT-1 whole-DAG-vs-changed-subgraph gate, with `WholeDagFailClosed` making conservative all-rerun structurally exclusive from changed-subgraph decisions and preserving either symbolic or diagnostic fail-closed evidence; its changed-subgraph arm carries ordered boundary-exclusion occurrences so unchanged/pure prune proofs are explicit without fabricating an `AffectedDimension` or leaving stale final path facts across an ordered `Diff`; inside an edit, `WholeDagFailClosed` is absorbing across dimension and edge folds so later unknowns cannot replace earlier diagnostic evidence. `AffectedFoldAcc` is 🟒 terminal internal fold state because it pairs the canonical `ReExecFrontier` with the current Dag required by D1 sequential rewrite; it carries no duplicate frontier taxonomy and stops semantic edit application once that frontier is `WholeDagFailClosed`. `FrontierDecision` is 🟑 scaffold: the rerun-vs-excluded coproduct is the per-`ExclusionKey` changed-dimension outcome authority and leaves node identity solely on the key path resolved against the input `Dag`; its `Excluded { first, rest }` field shape is a bounded T-25 cardinality bridge that keeps proofless skip unrepresentable until the canonical `List where non_empty` / `NonEmptyList` refinement is exported from `std/collection.dag`. Named trigger = T-25-core publishes the checked non-empty `List` refinement, then rewrite `Excluded` to carry that canonical refined list instead of this local head/rest bridge. When multiple changed producers hit the same key, `Rerun` absorbs `Excluded`, and repeated non-propagating producers aggregate all `PropagationExclusionProof`s so the final exclusion does not drop proof facts. #3153 carried no new file/type; close it as superseded | `docs/design-affected-set-lens.md` + `lens/affected_set.dag` carrier surface + `lens/affected_set_examples.dag` expected-frontier example rows | | **PROOF-1** | **External trust-discharge for A3** (operator-ratified 2026-05-15; *not the north star β€” placed & designed early like AGENT-1*). gunbc's structural evidence (A2 termination descent, the algebra-homomorphism epistemic chain, cost, effects) is EMITTED as a machine-checkable proof term in an external prover (Lean/Coq); their small independently-audited KERNEL checks it. Converts A3's named seed-trust axiom into an INTERSUBJECTIVE check against an anchor gunbc does not own. Does NOT make gunbc un-gameable β€” the external kernel + the evidenceβ†’proof-term faithfulness are the NEW named axioms; it MOVES trust to a stronger anchor (same Trusting-Trust honesty A3 observes). Constraints: the lens EXPORTS witnesses gunbc already has, the prover only KERNEL-CHECKS, never searches (no-engine + A2 "checker not discoverer"); discharges only what is structurally GROUNDED (ungroundable β‡’ fail-closed Diagnostic β€” inherits gunbc's honesty boundary). Realized as (evidence read) βŠ• (B2-OMNI emit to a `lean`/`coq` language model β€” ordinary targets). The PROOF-1 *framing* (#3158) added **NO new file** β€” it was framing-only and explicitly DEFERRED the prover model ("realized when a lean/coq model lands"); that model **subsequently lands as `extdeps/languages/lean.dag` per B-2/L-4** (operator-ratified closed-tree extension β€” see the **L-4** row). No contradiction: framing = no file; the deferred model's later arrival is the L-4 extension | `STRUCTURE.md` Β§7 (A3 home) + `workflow/bootstrap.dag` A3 block + `TASKS.md` T-15 note; cross-ref A2 / AGENT-1 / B2-OMNI / **L-4** | | **C5-2** | **emit-locality** (operator-ratified 2026-05-16) β€” `emit` is *structurally local*: a subtree's emitted form depends only on that subtree + a bounded declared context, so a D1 `apply_diff` at Path p re-emits ONLY p's subtree; everything outside p is unchanged by construction. **Locality is Node-relative, NOT file-relative** (operator code-as-data steer: the substrate anchors no semantic on the file concept β€” a "file" is a convenience projection only; stability = content_hash of the emitted *subtree*, never file bytes). Non-local emit decisions are DECLARED normalizations at the smallest *enclosing Node scope* (never "file scope" β€” there is no file in the model). Load-bearing: AGENT-1/D1 "faithful re-emit" is unprovable from global `ingest∘emit=id_Node` (C5-1) alone β€” locality is the stronger separate property; the C5↔D1 hinge | this row; `compiler/05_emit.dag` is a boundary index only; cross-ref C5 / C5-fidelity / D1 | | **T-9** | **infer keystone contracts** (operator-ratified 2026-05-16). **A β€” Find decidable by construction** (closes C1): the homomorphism Find recurses structurally on Node (A2 implicit descent β‡’ the inferencer terminates); the per-node candidate set is EXACTLY the closed declared inhabitances in `std/*` (enumerable, never synthesized/open), so "no homomorphism" = "exhausted the finite declared set" = a *decidable* fail-closed Diagnostic, not a non-terminating search. Synthesizing candidates = left the decidable fragment = STOP. **B β€” single-file discipline is a scope boundary**: v2's 12-file split was env/scope/lookup/method-resolution tangling; in v4 those are 03_resolve's job (B-4: binding = canonical `Symbol`), so `04_infer` owns ONLY {C1 Find + cardinality propagation + diagnostic precision}. A worker writing scope/env/binding/lookup here re-imported the 12-file pressure β†’ STOP (belongs in 03_resolve). The single file holds because the scope is bounded, not willed | this row; `compiler/04_infer.dag` is a boundary index only; cross-ref C1 / IR-1 / A1 / A2 / B-4 | diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index c3b2bae5703..e7814c760fe 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -138,7 +138,9 @@ type AffectedSetRead { } -type AffectedPostEditDagOutcome = Outcome +type AffectedPostEditDagRead { + outcome: Outcome +} type AffectedFoldAcc { @@ -331,7 +333,7 @@ fn expand_dimension( fn seed_edit_fold_acc_live( read_model: AffectedSetRead, - post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagOutcome, + post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagRead, acc: AffectedFoldAcc, edit: Edit ) -> AffectedFoldAcc { @@ -347,7 +349,7 @@ fn seed_edit_fold_acc_live( frontier: mark_boundary_excluded(frontier: acc.frontier, edit: edit, proof: p) } BoundaryExpands => - match post_edit_dag(acc.dag, edit) { + match post_edit_dag(acc.dag, edit).outcome { Produced { value: next_dag } => AffectedFoldAcc { dag: next_dag, @@ -366,7 +368,7 @@ fn seed_edit_fold_acc_live( } } BoundaryUnknown { reason: r } => - match post_edit_dag(acc.dag, edit) { + match post_edit_dag(acc.dag, edit).outcome { Produced { value: _ } => AffectedFoldAcc { dag: acc.dag, @@ -414,7 +416,7 @@ fn expand_changed_dimensions( fn seed_edit_fold_acc( read_model: AffectedSetRead, - post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagOutcome, + post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagRead, acc: AffectedFoldAcc, edit: Edit ) -> AffectedFoldAcc { @@ -433,7 +435,7 @@ fn seed_edit_fold_acc( fn affected_set( read_model: AffectedSetRead, - post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagOutcome, + post_edit_dag: fn(Dag, Edit) -> AffectedPostEditDagRead, dag: Dag, diff: Diff ) -> Witness { From 37ec8f72453ec5aaceb2b162ff2a3baeef7a9733 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 14:58:01 -0400 Subject: [PATCH 47/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/lens/affected_set.dag | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index e7814c760fe..cd592dd0425 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -13,17 +13,21 @@ import v4.std.node { Diff, Edit, Hash, Node, Path, Symbol } import v4.std.witness { Holds, Witness } +// grounded: pretty-much-done; receipt: std.node Node. type Dag = Node +// grounded: pretty-much-done; receipt: std.node Symbol. type AffectedDimension = Symbol +// grounded: pretty-much-done; receipt: affected_set_examples. type ExclusionKey { at: Path dimension: AffectedDimension } +// grounded: pretty-much-done; receipt: affected_set_examples. type FrontierEdge { producer: Path consumer: Path @@ -31,12 +35,14 @@ type FrontierEdge { } +// grounded: pretty-much-done; receipt: affected_set_examples. type EditDimension { edit: Edit dimension: AffectedDimension } +// grounded: pretty-much-done; receipt: affected_set_examples. type EditFrontierEdge { edit: Edit changed_dimension: AffectedDimension @@ -45,20 +51,24 @@ type EditFrontierEdge { // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed +// grounded: needs-more-work; receipt: affected_set_examples. type ExclusionProof = EmptyDimensionDelta { subject: Path } // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed +// grounded: needs-more-work; receipt: affected_set_examples. type PropagationExclusionProof = DimensionNotRead { producer: Path, changed_dimension: AffectedDimension } // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed +// grounded: needs-more-work; receipt: affected_set_examples. type BoundaryExclusionProof = UnchangedPureBoundary { content_hash: Hash } +// grounded: pretty-much-done; receipt: affected_set_examples. type BoundaryExclusion { edit: Edit proof: BoundaryExclusionProof @@ -66,18 +76,21 @@ type BoundaryExclusion { // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed +// grounded: needs-more-work; receipt: affected_set_examples. type WholeDagFailClosedEvidence = WholeDagReason { reason: Symbol } | WholeDagDiagnostic { diagnostic: Diagnostic } // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed +// grounded: needs-more-work; receipt: affected_set_examples. type FrontierDecision = Rerun | Excluded { first: PropagationExclusionProof, rest: List } // 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed; IRT-1 whole/changed-subgraph gate. +// grounded: pretty-much-done; receipt: affected_set_examples. type ReExecFrontier = ChangedSubgraphFrontier { boundary_exclusions: List, @@ -87,6 +100,7 @@ type ReExecFrontier // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves into B1 content_hash plus B-5 purity/effect receipts. +// grounded: needs-more-work; receipt: boundary read hook. type HashReceipt = HashUnchangedPure { content_hash: Hash } | HashChanged @@ -94,6 +108,7 @@ type HashReceipt // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when T-21 .dag TestClaims prove boundary cases. +// grounded: needs-more-work; receipt: boundary read hook. type BoundaryReceipt = BoundaryPruned { proof: BoundaryExclusionProof } | BoundaryExpands @@ -101,12 +116,14 @@ type BoundaryReceipt // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when per-edit dimension deltas are generated. +// grounded: needs-more-work; receipt: boundary read hook. type ChangedDimensionReceipt = ChangedDimensions { first: AffectedDimension, rest: List } | ChangedDimensionsUnknown { reason: Symbol } // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when B-4/B-5 edge receipts are generated. +// grounded: needs-more-work; receipt: propagation read hook. type PropagationReceipt = Propagates | DoesNotPropagate { proof: PropagationExclusionProof } @@ -114,23 +131,27 @@ type PropagationReceipt // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; proves transitive frontier or fails closed. +// grounded: needs-more-work; receipt: frontier read hook. type FrontierReceipt = FrontierProduced { edges: List } | FrontierUnknown { reason: Symbol } | FrontierRejected { diagnostic: Diagnostic } +// grounded: needs-more-work; receipt: frontier read hook. type AffectedFrontierRead { proven_transitive_changed_subgraph_frontier: fn(Dag, EditDimension) -> FrontierReceipt } +// grounded: needs-more-work; receipt: boundary read hook. type AffectedBoundaryRead { boundary_hash_receipt: fn(Dag, Path, Node) -> HashReceipt, changed_dimension_receipt: fn(Dag, Edit) -> ChangedDimensionReceipt } +// grounded: needs-more-work; receipt: affected_set function. type AffectedSetRead { boundary: AffectedBoundaryRead frontier: AffectedFrontierRead, @@ -138,11 +159,13 @@ type AffectedSetRead { } +// grounded: needs-more-work; receipt: std.diagnostic Outcome. type AffectedPostEditDagRead { outcome: Outcome } +// grounded: pretty-much-done; receipt: affected_set function. type AffectedFoldAcc { dag: Dag frontier: ReExecFrontier From b014e4170e1902144c1b213e112c2e73cc1e70bd Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 19 May 2026 15:00:42 -0400 Subject: [PATCH 48/48] =?UTF-8?q?WIP:=20T-21=20affected=5Fset.dag=20Wave-0?= =?UTF-8?q?=20implementation=20=E2=80=94=20IRT-1=20whole/changed-subgra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/lens/affected_set.dag | 46 ++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/v4/lens/affected_set.dag b/src/v4/lens/affected_set.dag index cd592dd0425..41508de2e9d 100644 --- a/src/v4/lens/affected_set.dag +++ b/src/v4/lens/affected_set.dag @@ -13,21 +13,21 @@ import v4.std.node { Diff, Edit, Hash, Node, Path, Symbol } import v4.std.witness { Holds, Witness } -// grounded: pretty-much-done; receipt: std.node Node. +// RULING-1: 🟒 pretty-much-done β€” node alias. type Dag = Node -// grounded: pretty-much-done; receipt: std.node Symbol. +// RULING-1: 🟒 pretty-much-done β€” dimension identity. type AffectedDimension = Symbol -// grounded: pretty-much-done; receipt: affected_set_examples. +// RULING-1: 🟒 pretty-much-done β€” frontier carrier. type ExclusionKey { at: Path dimension: AffectedDimension } -// grounded: pretty-much-done; receipt: affected_set_examples. +// RULING-1: 🟒 pretty-much-done β€” frontier carrier. type FrontierEdge { producer: Path consumer: Path @@ -35,14 +35,14 @@ type FrontierEdge { } -// grounded: pretty-much-done; receipt: affected_set_examples. +// RULING-1: 🟒 pretty-much-done β€” frontier carrier. type EditDimension { edit: Edit dimension: AffectedDimension } -// grounded: pretty-much-done; receipt: affected_set_examples. +// RULING-1: 🟒 pretty-much-done β€” frontier carrier. type EditFrontierEdge { edit: Edit changed_dimension: AffectedDimension @@ -51,24 +51,24 @@ type EditFrontierEdge { // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed -// grounded: needs-more-work; receipt: affected_set_examples. +// RULING-1: 🟑 needs-more-work β€” scaffold carrier. type ExclusionProof = EmptyDimensionDelta { subject: Path } // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed -// grounded: needs-more-work; receipt: affected_set_examples. +// RULING-1: 🟑 needs-more-work β€” scaffold carrier. type PropagationExclusionProof = DimensionNotRead { producer: Path, changed_dimension: AffectedDimension } // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed -// grounded: needs-more-work; receipt: affected_set_examples. +// RULING-1: 🟑 needs-more-work β€” scaffold carrier. type BoundaryExclusionProof = UnchangedPureBoundary { content_hash: Hash } -// grounded: pretty-much-done; receipt: affected_set_examples. +// RULING-1: 🟒 pretty-much-done β€” frontier carrier. type BoundaryExclusion { edit: Edit proof: BoundaryExclusionProof @@ -76,21 +76,21 @@ type BoundaryExclusion { // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed -// grounded: needs-more-work; receipt: affected_set_examples. +// RULING-1: 🟑 needs-more-work β€” scaffold carrier. type WholeDagFailClosedEvidence = WholeDagReason { reason: Symbol } | WholeDagDiagnostic { diagnostic: Diagnostic } // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed -// grounded: needs-more-work; receipt: affected_set_examples. +// RULING-1: 🟑 needs-more-work β€” scaffold carrier. type FrontierDecision = Rerun | Excluded { first: PropagationExclusionProof, rest: List } // 🟒 coproduct dissolution β€” DECISIONS.md #3153-subsumed; IRT-1 whole/changed-subgraph gate. -// grounded: pretty-much-done; receipt: affected_set_examples. +// RULING-1: 🟒 pretty-much-done β€” frontier carrier. type ReExecFrontier = ChangedSubgraphFrontier { boundary_exclusions: List, @@ -100,7 +100,7 @@ type ReExecFrontier // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves into B1 content_hash plus B-5 purity/effect receipts. -// grounded: needs-more-work; receipt: boundary read hook. +// RULING-1: 🟑 needs-more-work β€” boundary read. type HashReceipt = HashUnchangedPure { content_hash: Hash } | HashChanged @@ -108,7 +108,7 @@ type HashReceipt // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when T-21 .dag TestClaims prove boundary cases. -// grounded: needs-more-work; receipt: boundary read hook. +// RULING-1: 🟑 needs-more-work β€” boundary read. type BoundaryReceipt = BoundaryPruned { proof: BoundaryExclusionProof } | BoundaryExpands @@ -116,14 +116,14 @@ type BoundaryReceipt // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when per-edit dimension deltas are generated. -// grounded: needs-more-work; receipt: boundary read hook. +// RULING-1: 🟑 needs-more-work β€” boundary read. type ChangedDimensionReceipt = ChangedDimensions { first: AffectedDimension, rest: List } | ChangedDimensionsUnknown { reason: Symbol } // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; dissolves when B-4/B-5 edge receipts are generated. -// grounded: needs-more-work; receipt: propagation read hook. +// RULING-1: 🟑 needs-more-work β€” propagation read. type PropagationReceipt = Propagates | DoesNotPropagate { proof: PropagationExclusionProof } @@ -131,27 +131,27 @@ type PropagationReceipt // 🟑 coproduct dissolution β€” DECISIONS.md #3153-subsumed; proves transitive frontier or fails closed. -// grounded: needs-more-work; receipt: frontier read hook. +// RULING-1: 🟑 needs-more-work β€” frontier read. type FrontierReceipt = FrontierProduced { edges: List } | FrontierUnknown { reason: Symbol } | FrontierRejected { diagnostic: Diagnostic } -// grounded: needs-more-work; receipt: frontier read hook. +// RULING-1: 🟑 needs-more-work β€” frontier read. type AffectedFrontierRead { proven_transitive_changed_subgraph_frontier: fn(Dag, EditDimension) -> FrontierReceipt } -// grounded: needs-more-work; receipt: boundary read hook. +// RULING-1: 🟑 needs-more-work β€” boundary read. type AffectedBoundaryRead { boundary_hash_receipt: fn(Dag, Path, Node) -> HashReceipt, changed_dimension_receipt: fn(Dag, Edit) -> ChangedDimensionReceipt } -// grounded: needs-more-work; receipt: affected_set function. +// RULING-1: 🟑 needs-more-work β€” read model. type AffectedSetRead { boundary: AffectedBoundaryRead frontier: AffectedFrontierRead, @@ -159,13 +159,13 @@ type AffectedSetRead { } -// grounded: needs-more-work; receipt: std.diagnostic Outcome. +// RULING-1: 🟑 needs-more-work β€” post-edit read. type AffectedPostEditDagRead { outcome: Outcome } -// grounded: pretty-much-done; receipt: affected_set function. +// RULING-1: 🟒 pretty-much-done β€” fold state. type AffectedFoldAcc { dag: Dag frontier: ReExecFrontier