From 2aa2868df271f39285349717e13958909120f062 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 20 May 2026 03:40:11 -0400 Subject: [PATCH 01/11] =?UTF-8?q?WIP:=20Wave-1=20Worker=20C=20=E2=80=94=20?= =?UTF-8?q?infer=20lens-import=20cleanup=20+=20std/outcome.dag=20traverse?= =?UTF-8?q?=20+?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/std/diagnostic.dag | 18 +++++-- src/v4/std/node.dag | 57 ++++++++++++++++++-- src/v4/std/outcome.dag | 110 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+), 8 deletions(-) create mode 100644 src/v4/std/outcome.dag diff --git a/src/v4/std/diagnostic.dag b/src/v4/std/diagnostic.dag index 53466629308..36def63cfbd 100644 --- a/src/v4/std/diagnostic.dag +++ b/src/v4/std/diagnostic.dag @@ -1,14 +1,15 @@ // src/v4/std/diagnostic.dag // Scope: fail-closed diagnostic schema. // Anchor: THESIS.md "show the correct code" + INVARIANTS P3. -// Owns: Diagnostic, Outcome, Locus, Extent, Correction, NoCorrectionReason. -// Consumes: std node. -// Status: T-3 modeled. +// Owns: Diagnostic, Outcome, NonEmptyDiagnostics, Locus, Extent, Correction, NoCorrectionReason. +// Consumes: std node, std collection. +// Status: T-3 modeled; Outcome Accepted/Rejected reconciled with design-v4-compiler-homomorphism Q4. module v4.std.diagnostic +import v4.std.collection { List } import v4.std.node { Node } // 🟑 coproduct dissolution β€” CP-3229-GREEN-TERMINAL. type Extent @@ -33,7 +34,14 @@ type Diagnostic { at: Locus correction: Correction } +type NonEmptyDiagnostics + = Singleton { diagnostic: Diagnostic } + | Cons { head: Diagnostic, tail: List } // 🟒 coproduct dissolution β€” CP-3229-GREEN-TERMINAL. +// Reconciliation: Accepted{value, diagnostics} is canonical; Produced{value} ≑ Accepted with empty diagnostics. +// Rejected{diagnostics: NonEmptyDiagnostics} is canonical; RejectedSingular{diagnostic} is legacy single-diagnostic terminal. type Outcome - = Produced { value: T } - | Rejected { diagnostic: Diagnostic } + = Accepted { value: T, diagnostics: List } + | Rejected { diagnostics: NonEmptyDiagnostics } + | Produced { value: T } + | RejectedSingular { diagnostic: Diagnostic } diff --git a/src/v4/std/node.dag b/src/v4/std/node.dag index 375eb2543f1..1759a5db688 100644 --- a/src/v4/std/node.dag +++ b/src/v4/std/node.dag @@ -1,12 +1,25 @@ // src/v4/std/node.dag // Scope: v4 substrate root for type and computation nodes. // Anchor: THESIS.md "Substrate shape" + MODELING.md. -// Owns: Symbol, Hash, Connective, Behavior, NodeKind, EdgeLabel, Edge, Node, NodeFold, fold_node, EdgeDiscipline, connective_edge_discipline, edge_is_named, edge_is_positional, all_edges_named, all_edges_positional, name_occurrences, all_names_distinct, edges_conform, node_locally_well_formed, node_well_formed, is_empty_conj_root, Path, Edit, Diff. -// Consumes: none. -// Status: T-1 modeled; substrate-extension rule in src/v4/STRUCTURE.md. +// Owns: Symbol, Hash, Connective, Behavior, NodeKind, EdgeLabel, Edge, Node, NodeFold, fold_node, NodeOutcomeFold, traverse_node, sequence_node, map_node_outcome, EdgeDiscipline, connective_edge_discipline, edge_is_named, edge_is_positional, all_edges_named, all_edges_positional, name_occurrences, all_names_distinct, edges_conform, node_locally_well_formed, node_well_formed, is_empty_conj_root, Path, Edit, Diff. +// Consumes: std diagnostic, std outcome. +// Status: T-1 modeled; traverse_node substrate per design-v4-compiler-homomorphism Ratified Q4. module v4.std.node + + +import v4.std.diagnostic { + Diagnostic, + ExternalContractUnknown, + NonEmptyDiagnostics, + Outcome, + PortLocus, + Rejected, + Singleton, + Unavailable +} +import v4.std.outcome { StageDiagnosticPolicy } type Symbol type Hash // 🟒 coproduct dissolution β€” CP-3229-GREEN-TERMINAL. @@ -49,6 +62,44 @@ fn fold_node(n: Node, algebra: NodeFold) -> R { algebra.step(acc, e, fold_node(n: e.target, algebra: algebra)) }) } +type NodeOutcomeFold { + policy: StageDiagnosticPolicy + init: fn(Node) -> Outcome + step: fn(Outcome, Edge, Node) -> Outcome +} +data traverse_node_not_realized: Symbol = traverse_node_not_realized +fn traverse_node_not_realized_diagnostic() -> Diagnostic { + Diagnostic { + reason: traverse_node_not_realized, + at: PortLocus { port: traverse_node_not_realized }, + correction: Unavailable { reason: ExternalContractUnknown } + } +} +fn traverse_node(n: Node, algebra: NodeOutcomeFold) -> Outcome { + Rejected { + diagnostics: Singleton { diagnostic: traverse_node_not_realized_diagnostic() } + } +} +fn sequence_node(nodes: List, algebra: NodeOutcomeFold) -> Outcome> { + Rejected { + diagnostics: Singleton { diagnostic: traverse_node_not_realized_diagnostic() } + } +} +fn map_node_outcome(o: Outcome, f: fn(T) -> Outcome) -> Outcome { + match o { + Rejected { diagnostics: d } => Rejected { diagnostics: d } + RejectedSingular { diagnostic: d } => + Rejected { diagnostics: Singleton { diagnostic: d } } + Produced { value: v } => f(v) + Accepted { value: v, diagnostics: ds } => match f(v) { + Rejected { diagnostics: d } => Rejected { diagnostics: d } + RejectedSingular { diagnostic: d } => + Rejected { diagnostics: Singleton { diagnostic: d } } + Produced { value: u } => Accepted { value: u, diagnostics: ds } + Accepted { value: u, diagnostics: ds2 } => Accepted { value: u, diagnostics: ds } + } + } +} // 🟒 coproduct dissolution β€” CP-3229-GREEN-TERMINAL. type EdgeDiscipline = NoEdges diff --git a/src/v4/std/outcome.dag b/src/v4/std/outcome.dag new file mode 100644 index 00000000000..bf34764effc --- /dev/null +++ b/src/v4/std/outcome.dag @@ -0,0 +1,110 @@ +// src/v4/std/outcome.dag +// Scope: Outcome traverse combinators + diagnostic policy carriers + traverse law witnesses. +// Owns: DiagnosticAccumulationPolicy, StageDiagnosticPolicy, traverse_outcome, map_outcome, bind_outcome, TraverseIdentityLaw, TraverseCompositionLaw, TraverseFailureMonotonicityLaw. +// Consumes: std diagnostic, std collection, std algebra. +// Status: W-TraverseOutcome substrate (design-v4-compiler-homomorphism Ratified Q4 companion). + + +module v4.std.outcome + + +import v4.std.algebra { Empty, Cons } +import v4.std.collection { List } +import v4.std.diagnostic { + Accepted, + Diagnostic, + NonEmptyDiagnostics, + Outcome, + Produced, + Rejected, + RejectedSingular, + Singleton +} + + +type DiagnosticAccumulationPolicy + = Accumulate + | ShortCircuit + + +type StageDiagnosticPolicy { + accumulation: DiagnosticAccumulationPolicy +} + + +data traverse_identity_law: TraverseIdentityLaw = traverse_identity_law + + +data traverse_composition_law: TraverseCompositionLaw = traverse_composition_law + + +data traverse_failure_monotonicity_law: TraverseFailureMonotonicityLaw = traverse_failure_monotonicity_law + + +type TraverseIdentityLaw {} + + +type TraverseCompositionLaw {} + + +type TraverseFailureMonotonicityLaw {} + + +fn traverse_outcome(xs: List, f: fn(T) -> Outcome) -> Outcome> { + match xs { + Empty => Accepted { value: Empty, diagnostics: Empty } + Cons { head: h, tail: t } => match f(h) { + Rejected { diagnostics: d } => Rejected { diagnostics: d } + RejectedSingular { diagnostic: d } => + Rejected { diagnostics: Singleton { diagnostic: d } } + Produced { value: u } => + match traverse_outcome(xs: t, f: f) { + Rejected { diagnostics: d } => Rejected { diagnostics: d } + RejectedSingular { diagnostic: d } => + Rejected { diagnostics: Singleton { diagnostic: d } } + Produced { value: rest } => + Accepted { value: Cons { head: u, tail: rest }, diagnostics: Empty } + Accepted { value: rest, diagnostics: ds } => + Accepted { value: Cons { head: u, tail: rest }, diagnostics: ds } + } + Accepted { value: u, diagnostics: ds } => + match traverse_outcome(xs: t, f: f) { + Rejected { diagnostics: d } => Rejected { diagnostics: d } + RejectedSingular { diagnostic: d } => + Rejected { diagnostics: Singleton { diagnostic: d } } + Produced { value: rest } => + Accepted { value: Cons { head: u, tail: rest }, diagnostics: ds } + Accepted { value: rest, diagnostics: ds2 } => + Accepted { value: Cons { head: u, tail: rest }, diagnostics: ds } + } + } + } +} + + +fn map_outcome(o: Outcome, f: fn(T) -> U) -> Outcome { + match o { + Rejected { diagnostics: d } => Rejected { diagnostics: d } + RejectedSingular { diagnostic: d } => + Rejected { diagnostics: Singleton { diagnostic: d } } + Produced { value: v } => Accepted { value: f(v), diagnostics: Empty } + Accepted { value: v, diagnostics: ds } => Accepted { value: f(v), diagnostics: ds } + } +} + + +fn bind_outcome(o: Outcome, f: fn(T) -> Outcome) -> Outcome { + match o { + Rejected { diagnostics: d } => Rejected { diagnostics: d } + RejectedSingular { diagnostic: d } => + Rejected { diagnostics: Singleton { diagnostic: d } } + Produced { value: v } => f(v) + Accepted { value: v, diagnostics: ds } => match f(v) { + Rejected { diagnostics: d } => Rejected { diagnostics: d } + RejectedSingular { diagnostic: d } => + Rejected { diagnostics: Singleton { diagnostic: d } } + Produced { value: u } => Accepted { value: u, diagnostics: ds } + Accepted { value: u, diagnostics: ds2 } => Accepted { value: u, diagnostics: ds } + } + } +} From d44629ba9db124260325788eadbefaf37fe6e696 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 20 May 2026 07:42:12 +0000 Subject: [PATCH 02/11] v4 Wave-1 Worker C: infer lens cleanup, outcome traverse, file effects Remove compiler-core lens.cost import and InferredFacts.cost field (P3). Add std/outcome.dag traverse combinators, law witnesses, and diagnostic policies; extend Outcome with Accepted/Rejected while keeping Produced/ RejectedSingular for reconciliation. Declare traverse_node substrate stubs in std/node.dag. Model file_system peripheral read/write effects with explicit FileResource parameters; defer ResourceDependsOn until A1 lands. Co-authored-by: Cursor --- src/v4/compiler/04_infer.dag | 18 ++++--- src/v4/extdeps/file_system.dag | 91 ++++++++++++++++++++++++++++++++-- src/v4/std/node.dag | 4 +- 3 files changed, 99 insertions(+), 14 deletions(-) diff --git a/src/v4/compiler/04_infer.dag b/src/v4/compiler/04_infer.dag index eca1e27703c..962cba9f5b1 100644 --- a/src/v4/compiler/04_infer.dag +++ b/src/v4/compiler/04_infer.dag @@ -1,8 +1,8 @@ // src/v4/compiler/04_infer.dag // Scope: Stage 4 β€” ResolvedTree -> InferredTree; bounded Find phase (DECISIONS C1 / IR-1). // Owns: AlgebraRef, InferredFacts, InferredTree, infer_stage_locus_port, infer_coercion_fold_not_realized, infer_coercion_fold_not_realized_diagnostic, infer. -// Consumes: v4.compiler.resolve, v4.lens.cost, v4.std.cardinality, v4.std.collection, v4.std.diagnostic, v4.std.node. -// Status: scaffold β€” TASKS T-9; Wave-1 fail-closed boundary. +// Consumes: v4.compiler.resolve, v4.std.cardinality, v4.std.collection, v4.std.diagnostic, v4.std.node. +// Status: scaffold β€” TASKS T-9; Wave-1 fail-closed boundary; no lens imports (P3 commitment 4). // Anchor: https://en.wikipedia.org/wiki/Type_inference @@ -10,7 +10,6 @@ module v4.compiler.infer import v4.compiler.resolve { ResolvedTree } -import v4.lens.cost { SymbolicCost } import v4.std.cardinality { TerminationProof } import v4.std.collection { Map } import v4.std.diagnostic { @@ -19,6 +18,8 @@ import v4.std.diagnostic { Locus, Outcome, PortLocus, + Rejected, + Singleton, Unavailable } import v4.std.node { Node, Symbol } @@ -31,11 +32,10 @@ type AlgebraRef { } -// RULING-1: needs-more-work β€” cost/inhabitance coordinates remain T-9/T-12 bridged. +// RULING-1: needs-more-work β€” compiler-core semantic facts only; dimensional cost is lens-side (P3). type InferredFacts { resolved_type: Node inhabits: AlgebraRef - cost: SymbolicCost descent: TerminationProof } @@ -64,8 +64,10 @@ fn infer_coercion_fold_not_realized_diagnostic(at: Locus) -> Diagnostic { fn infer(tree: ResolvedTree) -> Outcome { Rejected { - diagnostic: infer_coercion_fold_not_realized_diagnostic( - at: PortLocus { port: infer_stage_locus_port } - ) + diagnostics: Singleton { + diagnostic: infer_coercion_fold_not_realized_diagnostic( + at: PortLocus { port: infer_stage_locus_port } + ) + } } } diff --git a/src/v4/extdeps/file_system.dag b/src/v4/extdeps/file_system.dag index de7b0ecdaac..50dbb0e7bd7 100644 --- a/src/v4/extdeps/file_system.dag +++ b/src/v4/extdeps/file_system.dag @@ -1,15 +1,25 @@ // src/v4/extdeps/file_system.dag -// Scope: file system substrate β€” paths, file kinds, read/write/list/kind operations. -// Owns: PosixByteString, NamedPathComponent, PathComponent, AbsolutePath, RelativePathHead, RelativePath, Path, FileKind, FileKindResolutionPolicy, Filesystem, FileBody, file-system request/result/operation carriers. -// Consumes: Diagnostic, Outcome, Byte, FreeMonoid, List. -// Status: T-4.5 modeled 2026-05-16. +// Scope: file system substrate β€” paths, file kinds, modeled peripheral I/O effects. +// Owns: PosixByteString, NamedPathComponent, PathComponent, AbsolutePath, RelativePathHead, RelativePath, Path, FileKind, FileKindResolutionPolicy, Filesystem, FileBody, FileResource, FilePath, FileContent, FileRead, FileWrite, NodeFileBinding, FileEffectWitness, FileWriteWitness, file-system request/result/operation carriers, file_read, file_write. +// Consumes: std diagnostic, std outcome, std node, std algebra, std machine. +// Status: T-4.5 modeled; peripheral effect surface per design-v4-compiler-homomorphism (not compile-core). + module v4.extdeps.file_system import v4.std.algebra { FreeMonoid } -import v4.std.diagnostic { Outcome } +import v4.std.diagnostic { + Diagnostic, + ExternalContractUnknown, + Outcome, + PortLocus, + Rejected, + Singleton, + Unavailable +} import v4.std.machine { Byte } +import v4.std.node { Node, Symbol } // Anchor: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap03.html#tag_03_241 @@ -71,6 +81,40 @@ type Filesystem // coincides: finite POSIX byte sequence. type FileBody = FreeMonoid +// Modeled resource β€” effects are explicit parameters against this carrier (B-5 DI). +type FileResource { + filesystem: Filesystem +} + +// Peripheral effect path coordinate (absolute path for MVP shims). +type FilePath { + absolute: AbsolutePath +} + +// Effect payload for read results. +type FileContent = FileBody + +// Effect markers β€” distinguish read vs write for affected_set granularity (B-5). +type FileRead {} + +type FileWrite {} + +type FileWriteWitness { + resource: FileResource + path: FilePath +} + +type NodeFileBinding { + node: Node + path: FilePath +} + +type FileEffectWitness + = ReadWitness { effect: FileRead, path: FilePath, content: FileContent } + | WriteWitness { effect: FileWrite, path: FilePath, witness: FileWriteWitness } + +data file_effect_dependency_wiring_deferred: Symbol = file_effect_dependency_wiring_deferred + type ReadFileRequest { filesystem: Filesystem path: AbsolutePath @@ -107,3 +151,40 @@ type FileSystemOperations { list_dir: fn(ListDirRequest) -> ListDirResult file_kind: fn(FileKindRequest) -> FileKindResult } + +data file_read_effect_not_wired: Symbol = file_read_effect_not_wired + +data file_write_effect_not_wired: Symbol = file_write_effect_not_wired + +fn file_effect_not_wired_diagnostic(reason: Symbol) -> Diagnostic { + Diagnostic { + reason: reason, + at: PortLocus { port: reason }, + correction: Unavailable { reason: ExternalContractUnknown } + } +} + +fn file_read( + resource: FileResource, + path: FilePath, + ops: FileSystemOperations +) -> Outcome { + Rejected { + diagnostics: Singleton { + diagnostic: file_effect_not_wired_diagnostic(reason: file_read_effect_not_wired) + } + } +} + +fn file_write( + resource: FileResource, + path: FilePath, + content: FileContent, + ops: FileSystemOperations +) -> Outcome { + Rejected { + diagnostics: Singleton { + diagnostic: file_effect_not_wired_diagnostic(reason: file_write_effect_not_wired) + } + } +} diff --git a/src/v4/std/node.dag b/src/v4/std/node.dag index 1759a5db688..828faa89d12 100644 --- a/src/v4/std/node.dag +++ b/src/v4/std/node.dag @@ -10,12 +10,14 @@ module v4.std.node import v4.std.diagnostic { + Accepted, Diagnostic, ExternalContractUnknown, - NonEmptyDiagnostics, Outcome, PortLocus, + Produced, Rejected, + RejectedSingular, Singleton, Unavailable } From 43b6fdae2f0c0143e55f70a64bdd09f6ec797e44 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 20 May 2026 07:48:35 +0000 Subject: [PATCH 03/11] fix(v4): merge Accepted diagnostics in outcome combinators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit append_diagnostics + merge_accepted_diagnostics preserve continuation diagnostics in traverse_outcome, bind_outcome, and map_node_outcome. Move traverse_node primitives to outcome.dag to break diagnosticβ†’node import cycle that failed v4 bootstrap compile. Co-authored-by: Cursor --- src/v4/std/node.dag | 59 ++------------------------- src/v4/std/outcome.dag | 92 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 90 insertions(+), 61 deletions(-) diff --git a/src/v4/std/node.dag b/src/v4/std/node.dag index 828faa89d12..375eb2543f1 100644 --- a/src/v4/std/node.dag +++ b/src/v4/std/node.dag @@ -1,27 +1,12 @@ // src/v4/std/node.dag // Scope: v4 substrate root for type and computation nodes. // Anchor: THESIS.md "Substrate shape" + MODELING.md. -// Owns: Symbol, Hash, Connective, Behavior, NodeKind, EdgeLabel, Edge, Node, NodeFold, fold_node, NodeOutcomeFold, traverse_node, sequence_node, map_node_outcome, EdgeDiscipline, connective_edge_discipline, edge_is_named, edge_is_positional, all_edges_named, all_edges_positional, name_occurrences, all_names_distinct, edges_conform, node_locally_well_formed, node_well_formed, is_empty_conj_root, Path, Edit, Diff. -// Consumes: std diagnostic, std outcome. -// Status: T-1 modeled; traverse_node substrate per design-v4-compiler-homomorphism Ratified Q4. +// Owns: Symbol, Hash, Connective, Behavior, NodeKind, EdgeLabel, Edge, Node, NodeFold, fold_node, EdgeDiscipline, connective_edge_discipline, edge_is_named, edge_is_positional, all_edges_named, all_edges_positional, name_occurrences, all_names_distinct, edges_conform, node_locally_well_formed, node_well_formed, is_empty_conj_root, Path, Edit, Diff. +// Consumes: none. +// Status: T-1 modeled; substrate-extension rule in src/v4/STRUCTURE.md. module v4.std.node - - -import v4.std.diagnostic { - Accepted, - Diagnostic, - ExternalContractUnknown, - Outcome, - PortLocus, - Produced, - Rejected, - RejectedSingular, - Singleton, - Unavailable -} -import v4.std.outcome { StageDiagnosticPolicy } type Symbol type Hash // 🟒 coproduct dissolution β€” CP-3229-GREEN-TERMINAL. @@ -64,44 +49,6 @@ fn fold_node(n: Node, algebra: NodeFold) -> R { algebra.step(acc, e, fold_node(n: e.target, algebra: algebra)) }) } -type NodeOutcomeFold { - policy: StageDiagnosticPolicy - init: fn(Node) -> Outcome - step: fn(Outcome, Edge, Node) -> Outcome -} -data traverse_node_not_realized: Symbol = traverse_node_not_realized -fn traverse_node_not_realized_diagnostic() -> Diagnostic { - Diagnostic { - reason: traverse_node_not_realized, - at: PortLocus { port: traverse_node_not_realized }, - correction: Unavailable { reason: ExternalContractUnknown } - } -} -fn traverse_node(n: Node, algebra: NodeOutcomeFold) -> Outcome { - Rejected { - diagnostics: Singleton { diagnostic: traverse_node_not_realized_diagnostic() } - } -} -fn sequence_node(nodes: List, algebra: NodeOutcomeFold) -> Outcome> { - Rejected { - diagnostics: Singleton { diagnostic: traverse_node_not_realized_diagnostic() } - } -} -fn map_node_outcome(o: Outcome, f: fn(T) -> Outcome) -> Outcome { - match o { - Rejected { diagnostics: d } => Rejected { diagnostics: d } - RejectedSingular { diagnostic: d } => - Rejected { diagnostics: Singleton { diagnostic: d } } - Produced { value: v } => f(v) - Accepted { value: v, diagnostics: ds } => match f(v) { - Rejected { diagnostics: d } => Rejected { diagnostics: d } - RejectedSingular { diagnostic: d } => - Rejected { diagnostics: Singleton { diagnostic: d } } - Produced { value: u } => Accepted { value: u, diagnostics: ds } - Accepted { value: u, diagnostics: ds2 } => Accepted { value: u, diagnostics: ds } - } - } -} // 🟒 coproduct dissolution β€” CP-3229-GREEN-TERMINAL. type EdgeDiscipline = NoEdges diff --git a/src/v4/std/outcome.dag b/src/v4/std/outcome.dag index bf34764effc..d629b9cba4d 100644 --- a/src/v4/std/outcome.dag +++ b/src/v4/std/outcome.dag @@ -1,7 +1,7 @@ // src/v4/std/outcome.dag // Scope: Outcome traverse combinators + diagnostic policy carriers + traverse law witnesses. -// Owns: DiagnosticAccumulationPolicy, StageDiagnosticPolicy, traverse_outcome, map_outcome, bind_outcome, TraverseIdentityLaw, TraverseCompositionLaw, TraverseFailureMonotonicityLaw. -// Consumes: std diagnostic, std collection, std algebra. +// Owns: DiagnosticAccumulationPolicy, StageDiagnosticPolicy, NodeOutcomeFold, traverse_node, sequence_node, map_node_outcome, traverse_outcome, map_outcome, bind_outcome, TraverseIdentityLaw, TraverseCompositionLaw, TraverseFailureMonotonicityLaw. +// Consumes: std diagnostic, std node, std collection, std algebra. // Status: W-TraverseOutcome substrate (design-v4-compiler-homomorphism Ratified Q4 companion). @@ -13,13 +13,17 @@ import v4.std.collection { List } import v4.std.diagnostic { Accepted, Diagnostic, + ExternalContractUnknown, NonEmptyDiagnostics, Outcome, + PortLocus, Produced, Rejected, RejectedSingular, - Singleton + Singleton, + Unavailable } +import v4.std.node { Edge, Node } type DiagnosticAccumulationPolicy @@ -50,6 +54,77 @@ type TraverseCompositionLaw {} type TraverseFailureMonotonicityLaw {} +fn append_diagnostics(front: List, back: List) -> List { + match front { + Empty => back + Cons { head: h, tail: t } => + Cons { head: h, tail: append_diagnostics(front: t, back: back) } + } +} + + +fn merge_accepted_diagnostics( + prior: List, + next: List +) -> List { + append_diagnostics(front: prior, back: next) +} + + +type NodeOutcomeFold { + policy: StageDiagnosticPolicy + init: fn(Node) -> Outcome + step: fn(Outcome, Edge, Node) -> Outcome +} + + +data traverse_node_not_realized: Symbol = traverse_node_not_realized + + +fn traverse_node_not_realized_diagnostic() -> Diagnostic { + Diagnostic { + reason: traverse_node_not_realized, + at: PortLocus { port: traverse_node_not_realized }, + correction: Unavailable { reason: ExternalContractUnknown } + } +} + + +fn traverse_node(n: Node, algebra: NodeOutcomeFold) -> Outcome { + Rejected { + diagnostics: Singleton { diagnostic: traverse_node_not_realized_diagnostic() } + } +} + + +fn sequence_node(nodes: List, algebra: NodeOutcomeFold) -> Outcome> { + Rejected { + diagnostics: Singleton { diagnostic: traverse_node_not_realized_diagnostic() } + } +} + + +fn map_node_outcome(o: Outcome, f: fn(T) -> Outcome) -> Outcome { + match o { + Rejected { diagnostics: d } => Rejected { diagnostics: d } + RejectedSingular { diagnostic: d } => + Rejected { diagnostics: Singleton { diagnostic: d } } + Produced { value: v } => f(v) + Accepted { value: v, diagnostics: ds } => match f(v) { + Rejected { diagnostics: d } => Rejected { diagnostics: d } + RejectedSingular { diagnostic: d } => + Rejected { diagnostics: Singleton { diagnostic: d } } + Produced { value: u } => Accepted { value: u, diagnostics: ds } + Accepted { value: u, diagnostics: ds2 } => + Accepted { + value: u, + diagnostics: merge_accepted_diagnostics(prior: ds, next: ds2) + } + } + } +} + + fn traverse_outcome(xs: List, f: fn(T) -> Outcome) -> Outcome> { match xs { Empty => Accepted { value: Empty, diagnostics: Empty } @@ -75,7 +150,10 @@ fn traverse_outcome(xs: List, f: fn(T) -> Outcome) -> Outcome Accepted { value: Cons { head: u, tail: rest }, diagnostics: ds } Accepted { value: rest, diagnostics: ds2 } => - Accepted { value: Cons { head: u, tail: rest }, diagnostics: ds } + Accepted { + value: Cons { head: u, tail: rest }, + diagnostics: merge_accepted_diagnostics(prior: ds, next: ds2) + } } } } @@ -104,7 +182,11 @@ fn bind_outcome(o: Outcome, f: fn(T) -> Outcome) -> Outcome { RejectedSingular { diagnostic: d } => Rejected { diagnostics: Singleton { diagnostic: d } } Produced { value: u } => Accepted { value: u, diagnostics: ds } - Accepted { value: u, diagnostics: ds2 } => Accepted { value: u, diagnostics: ds } + Accepted { value: u, diagnostics: ds2 } => + Accepted { + value: u, + diagnostics: merge_accepted_diagnostics(prior: ds, next: ds2) + } } } } From 4db730ccbf74f097481e2c8aa54b3f6d6b850f40 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 20 May 2026 03:53:39 -0400 Subject: [PATCH 04/11] =?UTF-8?q?WIP:=20Wave-1=20Worker=20C=20=E2=80=94=20?= =?UTF-8?q?infer=20lens-import=20cleanup=20+=20std/outcome.dag=20traverse?= =?UTF-8?q?=20+?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/std/diagnostic.dag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/std/diagnostic.dag b/src/v4/std/diagnostic.dag index 36def63cfbd..e48c776ab52 100644 --- a/src/v4/std/diagnostic.dag +++ b/src/v4/std/diagnostic.dag @@ -1,7 +1,7 @@ // src/v4/std/diagnostic.dag // Scope: fail-closed diagnostic schema. // Anchor: THESIS.md "show the correct code" + INVARIANTS P3. -// Owns: Diagnostic, Outcome, NonEmptyDiagnostics, Locus, Extent, Correction, NoCorrectionReason. +// Owns: Diagnostic, Outcome, NonEmptyDiagnostics, RejectedAccumulating, Locus, Extent, Correction, NoCorrectionReason. // Consumes: std node, std collection. // Status: T-3 modeled; Outcome Accepted/Rejected reconciled with design-v4-compiler-homomorphism Q4. From e7e1df10c751223475f7d4b18766f8b762938baf Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 20 May 2026 07:54:07 +0000 Subject: [PATCH 05/11] fix(v4): keep legacy Rejected; add RejectedAccumulating additive Softens Outcome delta for cross-PR compatibility with Worker B (#3440): legacy Rejected { diagnostic } and Produced unchanged; accumulating rejections use new RejectedAccumulating. Updates Worker C callsites and outcome combinators accordingly. Co-authored-by: Cursor --- src/v4/compiler/04_infer.dag | 9 ++---- src/v4/extdeps/file_system.dag | 9 ++---- src/v4/std/diagnostic.dag | 11 ++++--- src/v4/std/outcome.dag | 52 +++++++++++++--------------------- 4 files changed, 29 insertions(+), 52 deletions(-) diff --git a/src/v4/compiler/04_infer.dag b/src/v4/compiler/04_infer.dag index 962cba9f5b1..45e62b2fca6 100644 --- a/src/v4/compiler/04_infer.dag +++ b/src/v4/compiler/04_infer.dag @@ -19,7 +19,6 @@ import v4.std.diagnostic { Outcome, PortLocus, Rejected, - Singleton, Unavailable } import v4.std.node { Node, Symbol } @@ -64,10 +63,8 @@ fn infer_coercion_fold_not_realized_diagnostic(at: Locus) -> Diagnostic { fn infer(tree: ResolvedTree) -> Outcome { Rejected { - diagnostics: Singleton { - diagnostic: infer_coercion_fold_not_realized_diagnostic( - at: PortLocus { port: infer_stage_locus_port } - ) - } + diagnostic: infer_coercion_fold_not_realized_diagnostic( + at: PortLocus { port: infer_stage_locus_port } + ) } } diff --git a/src/v4/extdeps/file_system.dag b/src/v4/extdeps/file_system.dag index 50dbb0e7bd7..8b75fc981c7 100644 --- a/src/v4/extdeps/file_system.dag +++ b/src/v4/extdeps/file_system.dag @@ -15,7 +15,6 @@ import v4.std.diagnostic { Outcome, PortLocus, Rejected, - Singleton, Unavailable } import v4.std.machine { Byte } @@ -170,9 +169,7 @@ fn file_read( ops: FileSystemOperations ) -> Outcome { Rejected { - diagnostics: Singleton { - diagnostic: file_effect_not_wired_diagnostic(reason: file_read_effect_not_wired) - } + diagnostic: file_effect_not_wired_diagnostic(reason: file_read_effect_not_wired) } } @@ -183,8 +180,6 @@ fn file_write( ops: FileSystemOperations ) -> Outcome { Rejected { - diagnostics: Singleton { - diagnostic: file_effect_not_wired_diagnostic(reason: file_write_effect_not_wired) - } + diagnostic: file_effect_not_wired_diagnostic(reason: file_write_effect_not_wired) } } diff --git a/src/v4/std/diagnostic.dag b/src/v4/std/diagnostic.dag index e48c776ab52..4649d19a3db 100644 --- a/src/v4/std/diagnostic.dag +++ b/src/v4/std/diagnostic.dag @@ -38,10 +38,9 @@ type NonEmptyDiagnostics = Singleton { diagnostic: Diagnostic } | Cons { head: Diagnostic, tail: List } // 🟒 coproduct dissolution β€” CP-3229-GREEN-TERMINAL. -// Reconciliation: Accepted{value, diagnostics} is canonical; Produced{value} ≑ Accepted with empty diagnostics. -// Rejected{diagnostics: NonEmptyDiagnostics} is canonical; RejectedSingular{diagnostic} is legacy single-diagnostic terminal. +// Reconciliation: Accepted / RejectedAccumulating are new canonical terminals; Produced / Rejected are legacy unchanged. type Outcome - = Accepted { value: T, diagnostics: List } - | Rejected { diagnostics: NonEmptyDiagnostics } - | Produced { value: T } - | RejectedSingular { diagnostic: Diagnostic } + = Produced { value: T } + | Rejected { diagnostic: Diagnostic } + | Accepted { value: T, diagnostics: List } + | RejectedAccumulating { diagnostics: NonEmptyDiagnostics } diff --git a/src/v4/std/outcome.dag b/src/v4/std/outcome.dag index d629b9cba4d..e1e0acc5e63 100644 --- a/src/v4/std/outcome.dag +++ b/src/v4/std/outcome.dag @@ -14,13 +14,11 @@ import v4.std.diagnostic { Accepted, Diagnostic, ExternalContractUnknown, - NonEmptyDiagnostics, Outcome, PortLocus, Produced, Rejected, - RejectedSingular, - Singleton, + RejectedAccumulating, Unavailable } import v4.std.node { Edge, Node } @@ -91,29 +89,23 @@ fn traverse_node_not_realized_diagnostic() -> Diagnostic { fn traverse_node(n: Node, algebra: NodeOutcomeFold) -> Outcome { - Rejected { - diagnostics: Singleton { diagnostic: traverse_node_not_realized_diagnostic() } - } + Rejected { diagnostic: traverse_node_not_realized_diagnostic() } } fn sequence_node(nodes: List, algebra: NodeOutcomeFold) -> Outcome> { - Rejected { - diagnostics: Singleton { diagnostic: traverse_node_not_realized_diagnostic() } - } + Rejected { diagnostic: traverse_node_not_realized_diagnostic() } } fn map_node_outcome(o: Outcome, f: fn(T) -> Outcome) -> Outcome { match o { - Rejected { diagnostics: d } => Rejected { diagnostics: d } - RejectedSingular { diagnostic: d } => - Rejected { diagnostics: Singleton { diagnostic: d } } + Rejected { diagnostic: d } => Rejected { diagnostic: d } + RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } Produced { value: v } => f(v) Accepted { value: v, diagnostics: ds } => match f(v) { - Rejected { diagnostics: d } => Rejected { diagnostics: d } - RejectedSingular { diagnostic: d } => - Rejected { diagnostics: Singleton { diagnostic: d } } + Rejected { diagnostic: d } => Rejected { diagnostic: d } + RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } Produced { value: u } => Accepted { value: u, diagnostics: ds } Accepted { value: u, diagnostics: ds2 } => Accepted { @@ -129,14 +121,12 @@ fn traverse_outcome(xs: List, f: fn(T) -> Outcome) -> Outcome Accepted { value: Empty, diagnostics: Empty } Cons { head: h, tail: t } => match f(h) { - Rejected { diagnostics: d } => Rejected { diagnostics: d } - RejectedSingular { diagnostic: d } => - Rejected { diagnostics: Singleton { diagnostic: d } } + Rejected { diagnostic: d } => Rejected { diagnostic: d } + RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } Produced { value: u } => match traverse_outcome(xs: t, f: f) { - Rejected { diagnostics: d } => Rejected { diagnostics: d } - RejectedSingular { diagnostic: d } => - Rejected { diagnostics: Singleton { diagnostic: d } } + Rejected { diagnostic: d } => Rejected { diagnostic: d } + RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } Produced { value: rest } => Accepted { value: Cons { head: u, tail: rest }, diagnostics: Empty } Accepted { value: rest, diagnostics: ds } => @@ -144,9 +134,8 @@ fn traverse_outcome(xs: List, f: fn(T) -> Outcome) -> Outcome match traverse_outcome(xs: t, f: f) { - Rejected { diagnostics: d } => Rejected { diagnostics: d } - RejectedSingular { diagnostic: d } => - Rejected { diagnostics: Singleton { diagnostic: d } } + Rejected { diagnostic: d } => Rejected { diagnostic: d } + RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } Produced { value: rest } => Accepted { value: Cons { head: u, tail: rest }, diagnostics: ds } Accepted { value: rest, diagnostics: ds2 } => @@ -162,9 +151,8 @@ fn traverse_outcome(xs: List, f: fn(T) -> Outcome) -> Outcome(o: Outcome, f: fn(T) -> U) -> Outcome { match o { - Rejected { diagnostics: d } => Rejected { diagnostics: d } - RejectedSingular { diagnostic: d } => - Rejected { diagnostics: Singleton { diagnostic: d } } + Rejected { diagnostic: d } => Rejected { diagnostic: d } + RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } Produced { value: v } => Accepted { value: f(v), diagnostics: Empty } Accepted { value: v, diagnostics: ds } => Accepted { value: f(v), diagnostics: ds } } @@ -173,14 +161,12 @@ fn map_outcome(o: Outcome, f: fn(T) -> U) -> Outcome { fn bind_outcome(o: Outcome, f: fn(T) -> Outcome) -> Outcome { match o { - Rejected { diagnostics: d } => Rejected { diagnostics: d } - RejectedSingular { diagnostic: d } => - Rejected { diagnostics: Singleton { diagnostic: d } } + Rejected { diagnostic: d } => Rejected { diagnostic: d } + RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } Produced { value: v } => f(v) Accepted { value: v, diagnostics: ds } => match f(v) { - Rejected { diagnostics: d } => Rejected { diagnostics: d } - RejectedSingular { diagnostic: d } => - Rejected { diagnostics: Singleton { diagnostic: d } } + Rejected { diagnostic: d } => Rejected { diagnostic: d } + RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } Produced { value: u } => Accepted { value: u, diagnostics: ds } Accepted { value: u, diagnostics: ds2 } => Accepted { From c73280930b672bb80da544d54c9b841d5ddd62bf Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 20 May 2026 08:03:55 +0000 Subject: [PATCH 06/11] fix(v4): outcome.dag Symbol import + bounded Produced dissolution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Symbol to outcome.dag imports (resolve failure). Mark W-TRAVERSE- OUTCOME-LEGACY-PRODUCED dissolution trigger on Outcome four-variant coexistence. Stop map/bind/map_node from normalizing Producedβ†’Accepted; traverse_outcome keeps Produced on diagnostic-free folds. Co-authored-by: Cursor --- src/v4/std/diagnostic.dag | 7 +++++-- src/v4/std/outcome.dag | 12 ++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/v4/std/diagnostic.dag b/src/v4/std/diagnostic.dag index 4649d19a3db..6efa7af1f3e 100644 --- a/src/v4/std/diagnostic.dag +++ b/src/v4/std/diagnostic.dag @@ -37,8 +37,11 @@ type Diagnostic { type NonEmptyDiagnostics = Singleton { diagnostic: Diagnostic } | Cons { head: Diagnostic, tail: List } -// 🟒 coproduct dissolution β€” CP-3229-GREEN-TERMINAL. -// Reconciliation: Accepted / RejectedAccumulating are new canonical terminals; Produced / Rejected are legacy unchanged. +// 🟑 coproduct dissolution β€” W-TRAVERSE-OUTCOME-LEGACY-PRODUCED. +// Trigger: migrate all Outcome consumers to Accepted/RejectedAccumulating; retire Produced/Rejected variants. +// Until trigger closes: legacy Produced/Rejected remain parse-compatible (Worker B #3440); outcome.dag +// combinators preserve legacy on map/bind/map_node paths; traverse_outcome alone may introduce Accepted +// at list-fold boundaries when assembling accumulated diagnostics. type Outcome = Produced { value: T } | Rejected { diagnostic: Diagnostic } diff --git a/src/v4/std/outcome.dag b/src/v4/std/outcome.dag index e1e0acc5e63..6b99b7e136b 100644 --- a/src/v4/std/outcome.dag +++ b/src/v4/std/outcome.dag @@ -21,7 +21,7 @@ import v4.std.diagnostic { RejectedAccumulating, Unavailable } -import v4.std.node { Edge, Node } +import v4.std.node { Edge, Node, Symbol } type DiagnosticAccumulationPolicy @@ -106,7 +106,7 @@ fn map_node_outcome(o: Outcome, f: fn(T) -> Outcome) -> Outcome { Accepted { value: v, diagnostics: ds } => match f(v) { Rejected { diagnostic: d } => Rejected { diagnostic: d } RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } - Produced { value: u } => Accepted { value: u, diagnostics: ds } + Produced { value: u } => Produced { value: u } Accepted { value: u, diagnostics: ds2 } => Accepted { value: u, @@ -119,7 +119,7 @@ fn map_node_outcome(o: Outcome, f: fn(T) -> Outcome) -> Outcome { fn traverse_outcome(xs: List, f: fn(T) -> Outcome) -> Outcome> { match xs { - Empty => Accepted { value: Empty, diagnostics: Empty } + Empty => Produced { value: Empty } Cons { head: h, tail: t } => match f(h) { Rejected { diagnostic: d } => Rejected { diagnostic: d } RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } @@ -128,7 +128,7 @@ fn traverse_outcome(xs: List, f: fn(T) -> Outcome) -> Outcome Rejected { diagnostic: d } RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } Produced { value: rest } => - Accepted { value: Cons { head: u, tail: rest }, diagnostics: Empty } + Produced { value: Cons { head: u, tail: rest } } Accepted { value: rest, diagnostics: ds } => Accepted { value: Cons { head: u, tail: rest }, diagnostics: ds } } @@ -153,7 +153,7 @@ fn map_outcome(o: Outcome, f: fn(T) -> U) -> Outcome { match o { Rejected { diagnostic: d } => Rejected { diagnostic: d } RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } - Produced { value: v } => Accepted { value: f(v), diagnostics: Empty } + Produced { value: v } => Produced { value: f(v) } Accepted { value: v, diagnostics: ds } => Accepted { value: f(v), diagnostics: ds } } } @@ -167,7 +167,7 @@ fn bind_outcome(o: Outcome, f: fn(T) -> Outcome) -> Outcome { Accepted { value: v, diagnostics: ds } => match f(v) { Rejected { diagnostic: d } => Rejected { diagnostic: d } RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } - Produced { value: u } => Accepted { value: u, diagnostics: ds } + Produced { value: u } => Produced { value: u } Accepted { value: u, diagnostics: ds2 } => Accepted { value: u, From 88b792c3f55624665c19a960388c92c5f64a6458 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 20 May 2026 04:11:42 -0400 Subject: [PATCH 07/11] =?UTF-8?q?WIP:=20Wave-1=20Worker=20C=20=E2=80=94=20?= =?UTF-8?q?infer=20lens-import=20cleanup=20+=20std/outcome.dag=20traverse?= =?UTF-8?q?=20+?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/v4/std/diagnostic.dag | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/v4/std/diagnostic.dag b/src/v4/std/diagnostic.dag index 6efa7af1f3e..3b6d0c1c73f 100644 --- a/src/v4/std/diagnostic.dag +++ b/src/v4/std/diagnostic.dag @@ -34,16 +34,19 @@ type Diagnostic { at: Locus correction: Correction } +// 🟑 coproduct dissolution β€” W-TRAVERSE-OUTCOME-NON-EMPTY-DIAGNOSTICS. +// Trigger: dissolve to substrate NonEmptyList refinement (T-25-core); until then recursive +// NonEmptyDiagnostics (no List tail) so Singleton β‰  Cons{_, Empty}. type NonEmptyDiagnostics - = Singleton { diagnostic: Diagnostic } - | Cons { head: Diagnostic, tail: List } + = One { diagnostic: Diagnostic } + | More { head: Diagnostic, tail: NonEmptyDiagnostics } // 🟑 coproduct dissolution β€” W-TRAVERSE-OUTCOME-LEGACY-PRODUCED. // Trigger: migrate all Outcome consumers to Accepted/RejectedAccumulating; retire Produced/Rejected variants. -// Until trigger closes: legacy Produced/Rejected remain parse-compatible (Worker B #3440); outcome.dag -// combinators preserve legacy on map/bind/map_node paths; traverse_outcome alone may introduce Accepted -// at list-fold boundaries when assembling accumulated diagnostics. +// Until trigger closes: legacy Produced/Rejected remain parse-compatible (Worker B #3440). +// Authority split: Produced = success without diagnostics; Accepted = success with β‰₯1 diagnostic; +// Rejected = single failure; RejectedAccumulating = β‰₯2 diagnostics (More-only; never One). type Outcome = Produced { value: T } | Rejected { diagnostic: Diagnostic } - | Accepted { value: T, diagnostics: List } + | Accepted { value: T, diagnostics: NonEmptyDiagnostics } | RejectedAccumulating { diagnostics: NonEmptyDiagnostics } From 3e05fb59ecd32a3173d22eea19e13e339484ab3b Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 20 May 2026 08:13:06 +0000 Subject: [PATCH 08/11] fix(v4): address blocking Outcome/diagnostic P2 review findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - NonEmptyDiagnostics: One|More recursive coproduct (no empty List tail) - PluralDiagnostics for RejectedAccumulating only (β‰₯2); Rejected owns singles - Accepted carries NonEmptyDiagnostics (no empty-list duplicate of Produced) - Dissolution tags on NonEmptyDiagnostics, Outcome legacy, DiagnosticAccumulationPolicy, FileEffectWitness - append_non_empty replaces hand-rolled List append for diagnostic merge - FileEffectWitness: variant name is sole effect-kind authority (drop FileRead/FileWrite fields) Co-authored-by: Cursor --- src/v4/extdeps/file_system.dag | 11 ++++------- src/v4/std/diagnostic.dag | 19 +++++++++++-------- src/v4/std/outcome.dag | 26 +++++++++++++++++--------- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/v4/extdeps/file_system.dag b/src/v4/extdeps/file_system.dag index 8b75fc981c7..f0fde01fd47 100644 --- a/src/v4/extdeps/file_system.dag +++ b/src/v4/extdeps/file_system.dag @@ -93,11 +93,6 @@ type FilePath { // Effect payload for read results. type FileContent = FileBody -// Effect markers β€” distinguish read vs write for affected_set granularity (B-5). -type FileRead {} - -type FileWrite {} - type FileWriteWitness { resource: FileResource path: FilePath @@ -108,9 +103,11 @@ type NodeFileBinding { path: FilePath } +// 🟑 coproduct dissolution β€” W-TRAVERSE-OUTCOME-FILE-EFFECT-WITNESS. +// Trigger: ResourceDependsOn/EffectDependsOn wiring when std/dependency.dag lands (Worker A A1). type FileEffectWitness - = ReadWitness { effect: FileRead, path: FilePath, content: FileContent } - | WriteWitness { effect: FileWrite, path: FilePath, witness: FileWriteWitness } + = ReadWitness { path: FilePath, content: FileContent } + | WriteWitness { path: FilePath, witness: FileWriteWitness } data file_effect_dependency_wiring_deferred: Symbol = file_effect_dependency_wiring_deferred diff --git a/src/v4/std/diagnostic.dag b/src/v4/std/diagnostic.dag index 3b6d0c1c73f..f0ce5621144 100644 --- a/src/v4/std/diagnostic.dag +++ b/src/v4/std/diagnostic.dag @@ -1,7 +1,7 @@ // src/v4/std/diagnostic.dag // Scope: fail-closed diagnostic schema. // Anchor: THESIS.md "show the correct code" + INVARIANTS P3. -// Owns: Diagnostic, Outcome, NonEmptyDiagnostics, RejectedAccumulating, Locus, Extent, Correction, NoCorrectionReason. +// Owns: Diagnostic, Outcome, NonEmptyDiagnostics, PluralDiagnostics, RejectedAccumulating, Locus, Extent, Correction, NoCorrectionReason. // Consumes: std node, std collection. // Status: T-3 modeled; Outcome Accepted/Rejected reconciled with design-v4-compiler-homomorphism Q4. @@ -35,18 +35,21 @@ type Diagnostic { correction: Correction } // 🟑 coproduct dissolution β€” W-TRAVERSE-OUTCOME-NON-EMPTY-DIAGNOSTICS. -// Trigger: dissolve to substrate NonEmptyList refinement (T-25-core); until then recursive -// NonEmptyDiagnostics (no List tail) so Singleton β‰  Cons{_, Empty}. +// Trigger: dissolve to substrate NonEmptyList refinement (T-25-core). type NonEmptyDiagnostics = One { diagnostic: Diagnostic } | More { head: Diagnostic, tail: NonEmptyDiagnostics } +// Plural only β€” β‰₯2 diagnostics; singles use Rejected { diagnostic }. +type PluralDiagnostics { + head: Diagnostic + tail: NonEmptyDiagnostics +} // 🟑 coproduct dissolution β€” W-TRAVERSE-OUTCOME-LEGACY-PRODUCED. -// Trigger: migrate all Outcome consumers to Accepted/RejectedAccumulating; retire Produced/Rejected variants. -// Until trigger closes: legacy Produced/Rejected remain parse-compatible (Worker B #3440). -// Authority split: Produced = success without diagnostics; Accepted = success with β‰₯1 diagnostic; -// Rejected = single failure; RejectedAccumulating = β‰₯2 diagnostics (More-only; never One). +// Trigger: migrate consumers to Accepted/PluralDiagnostics; retire Produced/Rejected. +// Authority: Produced = clean success; Accepted = success + β‰₯1 diagnostic; +// Rejected = one failure; RejectedAccumulating = β‰₯2 failures only. type Outcome = Produced { value: T } | Rejected { diagnostic: Diagnostic } | Accepted { value: T, diagnostics: NonEmptyDiagnostics } - | RejectedAccumulating { diagnostics: NonEmptyDiagnostics } + | RejectedAccumulating { diagnostics: PluralDiagnostics } diff --git a/src/v4/std/outcome.dag b/src/v4/std/outcome.dag index 6b99b7e136b..f815fefc9f1 100644 --- a/src/v4/std/outcome.dag +++ b/src/v4/std/outcome.dag @@ -1,6 +1,6 @@ // src/v4/std/outcome.dag // Scope: Outcome traverse combinators + diagnostic policy carriers + traverse law witnesses. -// Owns: DiagnosticAccumulationPolicy, StageDiagnosticPolicy, NodeOutcomeFold, traverse_node, sequence_node, map_node_outcome, traverse_outcome, map_outcome, bind_outcome, TraverseIdentityLaw, TraverseCompositionLaw, TraverseFailureMonotonicityLaw. +// Owns: DiagnosticAccumulationPolicy, StageDiagnosticPolicy, NodeOutcomeFold, traverse_node, sequence_node, map_node_outcome, traverse_outcome, map_outcome, bind_outcome, append_non_empty, merge_accepted_diagnostics, TraverseIdentityLaw, TraverseCompositionLaw, TraverseFailureMonotonicityLaw. // Consumes: std diagnostic, std node, std collection, std algebra. // Status: W-TraverseOutcome substrate (design-v4-compiler-homomorphism Ratified Q4 companion). @@ -14,7 +14,11 @@ import v4.std.diagnostic { Accepted, Diagnostic, ExternalContractUnknown, + More, + NonEmptyDiagnostics, + One, Outcome, + PluralDiagnostics, PortLocus, Produced, Rejected, @@ -24,6 +28,8 @@ import v4.std.diagnostic { import v4.std.node { Edge, Node, Symbol } +// 🟑 coproduct dissolution β€” W-TRAVERSE-OUTCOME-DIAGNOSTIC-POLICY. +// Trigger: per-stage policy drives accumulate vs short-circuit in traverse_node fill. type DiagnosticAccumulationPolicy = Accumulate | ShortCircuit @@ -52,20 +58,22 @@ type TraverseCompositionLaw {} type TraverseFailureMonotonicityLaw {} -fn append_diagnostics(front: List, back: List) -> List { +fn append_non_empty(front: NonEmptyDiagnostics, back: NonEmptyDiagnostics) -> NonEmptyDiagnostics { match front { - Empty => back - Cons { head: h, tail: t } => - Cons { head: h, tail: append_diagnostics(front: t, back: back) } + One { diagnostic: d } => match back { + One { diagnostic: d2 } => More { head: d, tail: One { diagnostic: d2 } } + More { head: h, tail: t } => More { head: d, tail: More { head: h, tail: t } } + } + More { head: h, tail: t } => More { head: h, tail: append_non_empty(front: t, back: back) } } } fn merge_accepted_diagnostics( - prior: List, - next: List -) -> List { - append_diagnostics(front: prior, back: next) + prior: NonEmptyDiagnostics, + next: NonEmptyDiagnostics +) -> NonEmptyDiagnostics { + append_non_empty(front: prior, back: next) } From d175557dd2f429589265188f65ce884bf050cdaf Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 20 May 2026 08:23:29 +0000 Subject: [PATCH 09/11] chore(v4): drop unused List import from diagnostic.dag Follow-up on composer-2.5 exploratory review (PR #3441). Co-authored-by: Cursor --- src/v4/std/diagnostic.dag | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/v4/std/diagnostic.dag b/src/v4/std/diagnostic.dag index f0ce5621144..f796a1ba7e8 100644 --- a/src/v4/std/diagnostic.dag +++ b/src/v4/std/diagnostic.dag @@ -2,14 +2,13 @@ // Scope: fail-closed diagnostic schema. // Anchor: THESIS.md "show the correct code" + INVARIANTS P3. // Owns: Diagnostic, Outcome, NonEmptyDiagnostics, PluralDiagnostics, RejectedAccumulating, Locus, Extent, Correction, NoCorrectionReason. -// Consumes: std node, std collection. +// Consumes: std node. // Status: T-3 modeled; Outcome Accepted/Rejected reconciled with design-v4-compiler-homomorphism Q4. module v4.std.diagnostic -import v4.std.collection { List } import v4.std.node { Node } // 🟑 coproduct dissolution β€” CP-3229-GREEN-TERMINAL. type Extent From 4dea97ee0424c3788ea148962aa6feb5e74c6c15 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 20 May 2026 08:44:04 +0000 Subject: [PATCH 10/11] fix(v4): keep Outcome on legacy Produced/Rejected for bootstrap compile Adding Accepted/RejectedAccumulating to the Outcome sum broke 43 consumer matches across bootstrap/lens/compiler. Defer those variants until Pass A correction; retain NonEmptyDiagnostics substrate types in std/diagnostic. Co-authored-by: Cursor --- src/v4/std/diagnostic.dag | 14 ++++------ src/v4/std/outcome.dag | 56 ++------------------------------------- 2 files changed, 7 insertions(+), 63 deletions(-) diff --git a/src/v4/std/diagnostic.dag b/src/v4/std/diagnostic.dag index f796a1ba7e8..1774bcce2f3 100644 --- a/src/v4/std/diagnostic.dag +++ b/src/v4/std/diagnostic.dag @@ -1,9 +1,9 @@ // src/v4/std/diagnostic.dag // Scope: fail-closed diagnostic schema. // Anchor: THESIS.md "show the correct code" + INVARIANTS P3. -// Owns: Diagnostic, Outcome, NonEmptyDiagnostics, PluralDiagnostics, RejectedAccumulating, Locus, Extent, Correction, NoCorrectionReason. +// Owns: Diagnostic, Outcome, NonEmptyDiagnostics, PluralDiagnostics, Locus, Extent, Correction, NoCorrectionReason. // Consumes: std node. -// Status: T-3 modeled; Outcome Accepted/Rejected reconciled with design-v4-compiler-homomorphism Q4. +// Status: T-3 modeled; Outcome stays legacy Produced/Rejected until Pass A correction packet. module v4.std.diagnostic @@ -34,21 +34,17 @@ type Diagnostic { correction: Correction } // 🟑 coproduct dissolution β€” W-TRAVERSE-OUTCOME-NON-EMPTY-DIAGNOSTICS. -// Trigger: dissolve to substrate NonEmptyList refinement (T-25-core). +// Trigger: dissolve to substrate NonEmptyList refinement (T-25-core); not yet in Outcome sum. type NonEmptyDiagnostics = One { diagnostic: Diagnostic } | More { head: Diagnostic, tail: NonEmptyDiagnostics } -// Plural only β€” β‰₯2 diagnostics; singles use Rejected { diagnostic }. +// Plural only β€” β‰₯2 diagnostics; not yet in Outcome sum (Pass A correction packet). type PluralDiagnostics { head: Diagnostic tail: NonEmptyDiagnostics } // 🟑 coproduct dissolution β€” W-TRAVERSE-OUTCOME-LEGACY-PRODUCED. -// Trigger: migrate consumers to Accepted/PluralDiagnostics; retire Produced/Rejected. -// Authority: Produced = clean success; Accepted = success + β‰₯1 diagnostic; -// Rejected = one failure; RejectedAccumulating = β‰₯2 failures only. +// Trigger: Pass A correction collapses to Accepted/Rejected with Diagnostics substrate. type Outcome = Produced { value: T } | Rejected { diagnostic: Diagnostic } - | Accepted { value: T, diagnostics: NonEmptyDiagnostics } - | RejectedAccumulating { diagnostics: PluralDiagnostics } diff --git a/src/v4/std/outcome.dag b/src/v4/std/outcome.dag index f815fefc9f1..4c1698a3a36 100644 --- a/src/v4/std/outcome.dag +++ b/src/v4/std/outcome.dag @@ -1,6 +1,6 @@ // src/v4/std/outcome.dag // Scope: Outcome traverse combinators + diagnostic policy carriers + traverse law witnesses. -// Owns: DiagnosticAccumulationPolicy, StageDiagnosticPolicy, NodeOutcomeFold, traverse_node, sequence_node, map_node_outcome, traverse_outcome, map_outcome, bind_outcome, append_non_empty, merge_accepted_diagnostics, TraverseIdentityLaw, TraverseCompositionLaw, TraverseFailureMonotonicityLaw. +// Owns: DiagnosticAccumulationPolicy, StageDiagnosticPolicy, NodeOutcomeFold, traverse_node, sequence_node, map_node_outcome, traverse_outcome, map_outcome, bind_outcome, append_non_empty, TraverseIdentityLaw, TraverseCompositionLaw, TraverseFailureMonotonicityLaw. // Consumes: std diagnostic, std node, std collection, std algebra. // Status: W-TraverseOutcome substrate (design-v4-compiler-homomorphism Ratified Q4 companion). @@ -11,18 +11,15 @@ module v4.std.outcome import v4.std.algebra { Empty, Cons } import v4.std.collection { List } import v4.std.diagnostic { - Accepted, Diagnostic, ExternalContractUnknown, More, NonEmptyDiagnostics, One, Outcome, - PluralDiagnostics, PortLocus, Produced, Rejected, - RejectedAccumulating, Unavailable } import v4.std.node { Edge, Node, Symbol } @@ -69,14 +66,6 @@ fn append_non_empty(front: NonEmptyDiagnostics, back: NonEmptyDiagnostics) -> No } -fn merge_accepted_diagnostics( - prior: NonEmptyDiagnostics, - next: NonEmptyDiagnostics -) -> NonEmptyDiagnostics { - append_non_empty(front: prior, back: next) -} - - type NodeOutcomeFold { policy: StageDiagnosticPolicy init: fn(Node) -> Outcome @@ -109,18 +98,7 @@ fn sequence_node(nodes: List, algebra: NodeOutcomeFold) -> Outcome(o: Outcome, f: fn(T) -> Outcome) -> Outcome { match o { Rejected { diagnostic: d } => Rejected { diagnostic: d } - RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } Produced { value: v } => f(v) - Accepted { value: v, diagnostics: ds } => match f(v) { - Rejected { diagnostic: d } => Rejected { diagnostic: d } - RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } - Produced { value: u } => Produced { value: u } - Accepted { value: u, diagnostics: ds2 } => - Accepted { - value: u, - diagnostics: merge_accepted_diagnostics(prior: ds, next: ds2) - } - } } } @@ -130,27 +108,10 @@ fn traverse_outcome(xs: List, f: fn(T) -> Outcome) -> Outcome Produced { value: Empty } Cons { head: h, tail: t } => match f(h) { Rejected { diagnostic: d } => Rejected { diagnostic: d } - RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } Produced { value: u } => match traverse_outcome(xs: t, f: f) { Rejected { diagnostic: d } => Rejected { diagnostic: d } - RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } - Produced { value: rest } => - Produced { value: Cons { head: u, tail: rest } } - Accepted { value: rest, diagnostics: ds } => - Accepted { value: Cons { head: u, tail: rest }, diagnostics: ds } - } - Accepted { value: u, diagnostics: ds } => - match traverse_outcome(xs: t, f: f) { - Rejected { diagnostic: d } => Rejected { diagnostic: d } - RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } - Produced { value: rest } => - Accepted { value: Cons { head: u, tail: rest }, diagnostics: ds } - Accepted { value: rest, diagnostics: ds2 } => - Accepted { - value: Cons { head: u, tail: rest }, - diagnostics: merge_accepted_diagnostics(prior: ds, next: ds2) - } + Produced { value: rest } => Produced { value: Cons { head: u, tail: rest } } } } } @@ -160,9 +121,7 @@ fn traverse_outcome(xs: List, f: fn(T) -> Outcome) -> Outcome(o: Outcome, f: fn(T) -> U) -> Outcome { match o { Rejected { diagnostic: d } => Rejected { diagnostic: d } - RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } Produced { value: v } => Produced { value: f(v) } - Accepted { value: v, diagnostics: ds } => Accepted { value: f(v), diagnostics: ds } } } @@ -170,17 +129,6 @@ fn map_outcome(o: Outcome, f: fn(T) -> U) -> Outcome { fn bind_outcome(o: Outcome, f: fn(T) -> Outcome) -> Outcome { match o { Rejected { diagnostic: d } => Rejected { diagnostic: d } - RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } Produced { value: v } => f(v) - Accepted { value: v, diagnostics: ds } => match f(v) { - Rejected { diagnostic: d } => Rejected { diagnostic: d } - RejectedAccumulating { diagnostics: d } => RejectedAccumulating { diagnostics: d } - Produced { value: u } => Produced { value: u } - Accepted { value: u, diagnostics: ds2 } => - Accepted { - value: u, - diagnostics: merge_accepted_diagnostics(prior: ds, next: ds2) - } - } } } From e2be249f5e8462ec2261553cf1f71cd93ccca3c5 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Wed, 20 May 2026 08:53:18 +0000 Subject: [PATCH 11/11] fix(v4): single path authority on WriteWitness effect carrier Collapse FileEffectWitness::WriteWitness to witness-only; path lives in FileWriteWitness per INVARIANTS P2 / modeling-discipline Practice 5. Co-authored-by: Cursor --- src/v4/extdeps/file_system.dag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v4/extdeps/file_system.dag b/src/v4/extdeps/file_system.dag index f0fde01fd47..f79d0c1ec95 100644 --- a/src/v4/extdeps/file_system.dag +++ b/src/v4/extdeps/file_system.dag @@ -107,7 +107,7 @@ type NodeFileBinding { // Trigger: ResourceDependsOn/EffectDependsOn wiring when std/dependency.dag lands (Worker A A1). type FileEffectWitness = ReadWitness { path: FilePath, content: FileContent } - | WriteWitness { path: FilePath, witness: FileWriteWitness } + | WriteWitness { witness: FileWriteWitness } data file_effect_dependency_wiring_deferred: Symbol = file_effect_dependency_wiring_deferred