From 7fee358cf98c80b3f82e32f7e88f6d64f701b24a Mon Sep 17 00:00:00 2001 From: Nikita Volkov Date: Sat, 11 Jul 2026 09:19:23 +0300 Subject: [PATCH 1/9] feat(gen): migrate to gen-contract v4.0.1 / gen-sdk v2.0.0, adopt Sdk.Sigs Pins gen/Deps/Contract.dhall (new) and bumps gen/Deps/Sdk.dhall to gen-sdk v2.0.0, removing the gen/Deps/package.dhall barrel. Rewires Structures/CustomKind.dhall and all 11 Interpreters/*.dhall plus all 10 Templates/*.dhall to import Lude/Prelude/Contract/Sdk directly instead of through the barrel, replacing the local gen/Algebras/{Interpreter,Template} sig-constructors with gen-sdk's Sdk.Sigs.interpreter / Sdk.Sigs.template (now deleted). Matches the shape java.gen's current release already uses. Query.dhall, Result.dhall, and ResultColumns.dhall keep their pre-existing bare-export shape (like Member.dhall/ParamsMember.dhall) instead of adopting Sdk.Sigs.interpreter: their `run` functions take 3/4 curried arguments (config, lookup, [rowClassName], input), which doesn't fit Sigs.interpreter's fixed 2-argument Config -> Input -> Result shape. This extends the Member/ParamsMember exception already called out in the migration plan to these three files, confirmed against their actual call sites in Project.dhall/Query.dhall. gen/compile.dhall and gen/Gen.dhall still reference the removed barrel and are intentionally left broken until the next task rewrites the root entry point (Config.dhall/compile.dhall -> Interpret.dhall, Gen.dhall -> package.dhall). --- gen/Algebras/Interpreter.dhall | 21 ------------- gen/Algebras/Template.dhall | 5 --- gen/Algebras/package.dhall | 1 - gen/Deps/Contract.dhall | 2 ++ gen/Deps/Sdk.dhall | 4 +-- gen/Deps/package.dhall | 4 --- gen/Interpreters/CustomType.dhall | 25 +++++++++------ gen/Interpreters/Member.dhall | 27 ++++++++++------ gen/Interpreters/ParamsMember.dhall | 45 ++++++++++++++++----------- gen/Interpreters/Primitive.dhall | 23 +++++++++----- gen/Interpreters/Project.dhall | 25 +++++++++------ gen/Interpreters/Query.dhall | 23 +++++++++----- gen/Interpreters/QueryFragments.dhall | 23 ++++++++------ gen/Interpreters/Result.dhall | 26 ++++++++++------ gen/Interpreters/ResultColumns.dhall | 24 +++++++++----- gen/Interpreters/Scalar.dhall | 21 ++++++++----- gen/Interpreters/Value.dhall | 23 +++++++++----- gen/Structures/CustomKind.dhall | 4 +-- gen/Templates/CompositeModule.dhall | 12 +++---- gen/Templates/CoreModule.dhall | 4 +-- gen/Templates/EnumModule.dhall | 20 ++++++------ gen/Templates/FacadeModule.dhall | 8 ++--- gen/Templates/InitModule.dhall | 4 +-- gen/Templates/RegisterModule.dhall | 8 ++--- gen/Templates/RowsModule.dhall | 12 +++---- gen/Templates/RuntimeModule.dhall | 4 +-- gen/Templates/StatementModule.dhall | 12 +++---- gen/Templates/TypesInit.dhall | 8 ++--- 28 files changed, 230 insertions(+), 188 deletions(-) delete mode 100644 gen/Algebras/Interpreter.dhall delete mode 100644 gen/Algebras/Template.dhall delete mode 100644 gen/Algebras/package.dhall create mode 100644 gen/Deps/Contract.dhall delete mode 100644 gen/Deps/package.dhall diff --git a/gen/Algebras/Interpreter.dhall b/gen/Algebras/Interpreter.dhall deleted file mode 100644 index 7532153..0000000 --- a/gen/Algebras/Interpreter.dhall +++ /dev/null @@ -1,21 +0,0 @@ -let Deps = ../Deps/package.dhall - -let OnUnsupported = ../Structures/OnUnsupported.dhall - -let Config = - { packageName : Text - , importName : Text - , emitSync : Bool - , onUnsupported : OnUnsupported.Mode - } - -let module = - \(Input : Type) -> - \(Output : Type) -> - let Result = Deps.Lude.Compiled.Type Output - - let Run = Config -> Input -> Result - - in \(run : Run) -> { Input, Output, Result, Run, run } - -in { Config, module } diff --git a/gen/Algebras/Template.dhall b/gen/Algebras/Template.dhall deleted file mode 100644 index 15f2624..0000000 --- a/gen/Algebras/Template.dhall +++ /dev/null @@ -1,5 +0,0 @@ -let module = - \(Params : Type) -> - let Run = Params -> Text in \(run : Run) -> { Params, Run, run } - -in { module } diff --git a/gen/Algebras/package.dhall b/gen/Algebras/package.dhall deleted file mode 100644 index f0f64bd..0000000 --- a/gen/Algebras/package.dhall +++ /dev/null @@ -1 +0,0 @@ -{ Interpreter = ./Interpreter.dhall, Template = ./Template.dhall } diff --git a/gen/Deps/Contract.dhall b/gen/Deps/Contract.dhall new file mode 100644 index 0000000..ea9e805 --- /dev/null +++ b/gen/Deps/Contract.dhall @@ -0,0 +1,2 @@ +https://raw.githubusercontent.com/pgenie-io/gen-contract/v4.0.1/src/package.dhall + sha256:4a130ba7fbaa152a776babbb1bf2994a4833931ca76bde9bf6930d354225651e diff --git a/gen/Deps/Sdk.dhall b/gen/Deps/Sdk.dhall index 6cf6014..5602c43 100644 --- a/gen/Deps/Sdk.dhall +++ b/gen/Deps/Sdk.dhall @@ -1,3 +1,3 @@ -https://raw.githubusercontent.com/pgenie-io/gen-sdk/v0.11.0/dhall/package.dhall - sha256:8d43544ecb0e612406af3133bdbca51138c704a77a5a29ef62fe034d0e77a3a6 +https://raw.githubusercontent.com/pgenie-io/gen-sdk/v2.0.0/src/package.dhall + sha256:b9def6ab1179bc4aaae7fc6e91977f094f75934cd5755175c294a9e97ca71b15 as Source diff --git a/gen/Deps/package.dhall b/gen/Deps/package.dhall deleted file mode 100644 index b395cd9..0000000 --- a/gen/Deps/package.dhall +++ /dev/null @@ -1,4 +0,0 @@ -{ Sdk = ./Sdk.dhall -, Lude = ./Lude.dhall -, Prelude = ./Prelude.dhall -} diff --git a/gen/Interpreters/CustomType.dhall b/gen/Interpreters/CustomType.dhall index 7927f0a..676f9ab 100644 --- a/gen/Interpreters/CustomType.dhall +++ b/gen/Interpreters/CustomType.dhall @@ -1,16 +1,16 @@ -let Deps = ../Deps/package.dhall +let Lude = ../Deps/Lude.dhall -let ImportSet = ../Structures/ImportSet.dhall +let Prelude = ../Deps/Prelude.dhall -let CustomKind = ../Structures/CustomKind.dhall +let Model = ../Deps/Contract.dhall -let Algebra = ../Algebras/Interpreter.dhall +let Sdk = ../Deps/Sdk.dhall -let Lude = Deps.Lude +let ImportSet = ../Structures/ImportSet.dhall -let Prelude = Deps.Prelude +let CustomKind = ../Structures/CustomKind.dhall -let Model = Deps.Sdk.Project +let OnUnsupported = ../Structures/OnUnsupported.dhall let MemberGen = ./Member.dhall @@ -18,6 +18,13 @@ let EnumModule = ../Templates/EnumModule.dhall let CompositeModule = ../Templates/CompositeModule.dhall +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } + let Input = Model.CustomType let TypeKind = < Enum | Composite > @@ -75,7 +82,7 @@ let renderExtraImports = ) let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(input : Input) -> let typeName = input.name.inPascalCase @@ -174,4 +181,4 @@ let run = } input.definition -in { Input, Output, TypeKind, run } +in Sdk.Sigs.interpreter Config Input Output run diff --git a/gen/Interpreters/Member.dhall b/gen/Interpreters/Member.dhall index 672dd18..8d2d27c 100644 --- a/gen/Interpreters/Member.dhall +++ b/gen/Interpreters/Member.dhall @@ -1,4 +1,8 @@ -let Deps = ../Deps/package.dhall +let Lude = ../Deps/Lude.dhall + +let Prelude = ../Deps/Prelude.dhall + +let Model = ../Deps/Contract.dhall let ImportSet = ../Structures/ImportSet.dhall @@ -6,14 +10,17 @@ let CustomKind = ../Structures/CustomKind.dhall let PyIdent = ../Structures/PyIdent.dhall -let Algebra = ../Algebras/Interpreter.dhall - -let Lude = Deps.Lude - -let Model = Deps.Sdk.Project +let OnUnsupported = ../Structures/OnUnsupported.dhall let Value = ./Value.dhall +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } + let Input = Model.Member -- decodeExpr is a Dhall function: given the source expression (e.g. row["x"] or @@ -29,7 +36,7 @@ let Output = } let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(lookup : CustomKind.Lookup) -> \(input : Input) -> -- Result-column / composite-field name becomes a dataclass field and decode @@ -95,7 +102,7 @@ let run = \(fields : List CustomKind.CompositeField) -> \(src : Text) -> let fieldTypes = - Deps.Prelude.Text.concatMapSep + Prelude.Text.concatMapSep ", " CustomKind.CompositeField (\(f : CustomKind.CompositeField) -> f.pyType) @@ -120,7 +127,7 @@ let run = , decodeExpr = passthroughDecode } , Custom = - Deps.Prelude.Optional.fold + Prelude.Optional.fold Model.Name value.scalar.customRef (Lude.Compiled.Type Output) @@ -235,6 +242,6 @@ let run = in Lude.Compiled.flatMap Value.Output Output buildOutput compiledValue -let Run = Algebra.Config -> CustomKind.Lookup -> Input -> Lude.Compiled.Type Output +let Run = Config -> CustomKind.Lookup -> Input -> Lude.Compiled.Type Output in { Input, Output, Run, run } diff --git a/gen/Interpreters/ParamsMember.dhall b/gen/Interpreters/ParamsMember.dhall index 47bef3f..2a4b469 100644 --- a/gen/Interpreters/ParamsMember.dhall +++ b/gen/Interpreters/ParamsMember.dhall @@ -1,17 +1,24 @@ -let Deps = ../Deps/package.dhall +let Lude = ../Deps/Lude.dhall -let ImportSet = ../Structures/ImportSet.dhall +let Prelude = ../Deps/Prelude.dhall -let CustomKind = ../Structures/CustomKind.dhall +let Model = ../Deps/Contract.dhall -let Algebra = ../Algebras/Interpreter.dhall +let ImportSet = ../Structures/ImportSet.dhall -let Lude = Deps.Lude +let CustomKind = ../Structures/CustomKind.dhall -let Model = Deps.Sdk.Project +let OnUnsupported = ../Structures/OnUnsupported.dhall let Value = ./Value.dhall +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } + let Input = Model.Member let PyIdent = ../Structures/PyIdent.dhall @@ -192,7 +199,7 @@ let scalarIsJsonb = let valueIsArray = \(value : Model.Value) -> - Deps.Prelude.Optional.fold + Prelude.Optional.fold Model.ArraySettings value.arraySettings Bool @@ -204,15 +211,15 @@ let valueIsArray = -- the two must not be collapsed. let isJsonbScalar = \(value : Model.Value) -> - Deps.Prelude.Bool.and - [ scalarIsJsonb value, Deps.Prelude.Bool.not (valueIsArray value) ] + Prelude.Bool.and + [ scalarIsJsonb value, Prelude.Bool.not (valueIsArray value) ] let isJsonScalar = \(value : Model.Value) -> - Deps.Prelude.Bool.and + Prelude.Bool.and [ scalarIsJson value - , Deps.Prelude.Bool.not (scalarIsJsonb value) - , Deps.Prelude.Bool.not (valueIsArray value) + , Prelude.Bool.not (scalarIsJsonb value) + , Prelude.Bool.not (valueIsArray value) ] -- A json/jsonb ARRAY param has no faithful psycopg bind (Jsonb wraps a scalar, @@ -221,10 +228,10 @@ let isJsonScalar = -- in SQL is the supported route (the param then types as text[], not json[]). let isJsonArray = \(value : Model.Value) -> - Deps.Prelude.Bool.and [ scalarIsJson value, valueIsArray value ] + Prelude.Bool.and [ scalarIsJson value, valueIsArray value ] let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(lookup : CustomKind.Lookup) -> \(input : Input) -> let fieldName = pySafeName input.name.inSnakeCase @@ -251,7 +258,7 @@ let run = let compositeBind = \(fields : List CustomKind.CompositeField) -> let joinedFields = - Deps.Prelude.Text.concatMapSep + Prelude.Text.concatMapSep ", " CustomKind.CompositeField ( \(f : CustomKind.CompositeField) -> @@ -265,8 +272,8 @@ let run = -- as a 1-tuple, so force it for exactly one field; concatMapSep -- already inserts the internal comma for two or more. let trailingComma = - if Deps.Prelude.Natural.equal - ( Deps.Prelude.List.length + if Prelude.Natural.equal + ( Prelude.List.length CustomKind.CompositeField fields ) @@ -301,7 +308,7 @@ let run = , needsJsonbImport } - in Deps.Prelude.Optional.fold + in Prelude.Optional.fold Model.Name value.scalar.customRef (Lude.Compiled.Type Output) @@ -377,6 +384,6 @@ let run = in Lude.Compiled.flatMap Value.Output Output buildOutput compiledValue -let Run = Algebra.Config -> CustomKind.Lookup -> Input -> Lude.Compiled.Type Output +let Run = Config -> CustomKind.Lookup -> Input -> Lude.Compiled.Type Output in { Input, Output, Run, run } diff --git a/gen/Interpreters/Primitive.dhall b/gen/Interpreters/Primitive.dhall index 0e1cb70..052ef1b 100644 --- a/gen/Interpreters/Primitive.dhall +++ b/gen/Interpreters/Primitive.dhall @@ -1,10 +1,19 @@ -let Deps = ../Deps/package.dhall +let Lude = ../Deps/Lude.dhall + +let Model = ../Deps/Contract.dhall + +let Sdk = ../Deps/Sdk.dhall let ImportSet = ../Structures/ImportSet.dhall -let Algebra = ../Algebras/Interpreter.dhall +let OnUnsupported = ../Structures/OnUnsupported.dhall -let Model = Deps.Sdk.Project +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } let Input = Model.Primitive @@ -13,16 +22,16 @@ let Output = { pyType : Text, imports : ImportSet.Type } let supported = \(pyType : Text) -> \(imports : ImportSet.Type) -> - Deps.Lude.Compiled.ok Output { pyType, imports } + Lude.Compiled.ok Output { pyType, imports } let unsupported = \(pgType : Text) -> - Deps.Lude.Compiled.report Output [ pgType ] "Unsupported type" + Lude.Compiled.report Output [ pgType ] "Unsupported type" let plain = \(pyType : Text) -> supported pyType ImportSet.empty let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(input : Input) -> merge { Bit = unsupported "bit" @@ -89,4 +98,4 @@ let run = } input -in Algebra.module Input Output run +in Sdk.Sigs.interpreter Config Input Output run diff --git a/gen/Interpreters/Project.dhall b/gen/Interpreters/Project.dhall index d51438f..d4fa6d6 100644 --- a/gen/Interpreters/Project.dhall +++ b/gen/Interpreters/Project.dhall @@ -1,12 +1,10 @@ -let Deps = ../Deps/package.dhall +let Lude = ../Deps/Lude.dhall -let Algebra = ../Algebras/Interpreter.dhall +let Prelude = ../Deps/Prelude.dhall -let Lude = Deps.Lude +let Model = ../Deps/Contract.dhall -let Prelude = Deps.Prelude - -let Model = Deps.Sdk.Project +let Sdk = ../Deps/Sdk.dhall let CustomKind = ../Structures/CustomKind.dhall @@ -40,6 +38,13 @@ let OnUnsupported = ../Structures/OnUnsupported.dhall let Report = { path : List Text, message : Text } +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } + let Input = Model.Project let Output = Lude.Files.Type @@ -67,7 +72,7 @@ let withHeader = -- Internal interpreter config (importName etc.) is only needed to satisfy -- Value.run's signature; rendering a composite field's pyType does not read it. let lookupConfig - : Algebra.Config + : Config = { packageName = "" , importName = "" , emitSync = False @@ -175,7 +180,7 @@ let buildLookup = (\(_ : Model.Name) -> CustomKind.TypeKind.Absent) let combineOutputs = - \(config : Algebra.Config) -> + \(config : Config) -> \(input : Input) -> \(queries : List QueryGen.Output) -> -- Already the post-Skip-filter surviving set (see `run`); equal to @@ -430,7 +435,7 @@ let combineOutputs = let QueryCheck = { query : Model.Query, keep : Bool, warning : Optional Report } let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(input : Input) -> let skip = merge { Fail = False, Skip = True } config.onUnsupported @@ -542,4 +547,4 @@ let run = in Lude.Compiled.appendWarnings Output skipWarnings combined -in Algebra.module Input Output run +in Sdk.Sigs.interpreter Config Input Output run diff --git a/gen/Interpreters/Query.dhall b/gen/Interpreters/Query.dhall index b0e688d..daa1d47 100644 --- a/gen/Interpreters/Query.dhall +++ b/gen/Interpreters/Query.dhall @@ -1,6 +1,6 @@ -let Deps = ../Deps/package.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Interpreter.dhall +let Lude = ../Deps/Lude.dhall let ImportSet = ../Structures/ImportSet.dhall @@ -10,6 +10,8 @@ let PyIdent = ../Structures/PyIdent.dhall let Surface = ../Structures/Surface.dhall +let OnUnsupported = ../Structures/OnUnsupported.dhall + let RowsModule = ../Templates/RowsModule.dhall let ResultModule = ./Result.dhall @@ -20,13 +22,16 @@ let ParamsMember = ./ParamsMember.dhall let StatementModule = ../Templates/StatementModule.dhall -let Prelude = Deps.Prelude - -let Lude = Deps.Lude +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } let Compiled = Lude.Compiled -let Model = Deps.Sdk.Project +let Model = ../Deps/Contract.dhall let Input = Model.Query @@ -46,7 +51,7 @@ let Output = } let render = - \(config : Algebra.Config) -> + \(config : Config) -> \(input : Input) -> \(result : ResultModule.Output) -> \(fragments : QueryFragmentsModule.Output) -> @@ -131,7 +136,7 @@ let render = } let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(lookup : CustomKind.Lookup) -> \(input : Input) -> let rowClassName = input.name.inPascalCase ++ "Row" @@ -172,4 +177,6 @@ let run = ) ) +let Run = Config -> CustomKind.Lookup -> Input -> Lude.Compiled.Type Output + in { Input, Output, run } diff --git a/gen/Interpreters/QueryFragments.dhall b/gen/Interpreters/QueryFragments.dhall index 066bc75..7813900 100644 --- a/gen/Interpreters/QueryFragments.dhall +++ b/gen/Interpreters/QueryFragments.dhall @@ -1,16 +1,21 @@ -let Deps = ../Deps/package.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Interpreter.dhall +let Lude = ../Deps/Lude.dhall -let Prelude = Deps.Prelude +let Sdk = ../Deps/Sdk.dhall -let Sdk = Deps.Sdk +let Compiled = Lude.Compiled -let Lude = Deps.Lude +let Model = ../Deps/Contract.dhall -let Compiled = Lude.Compiled +let OnUnsupported = ../Structures/OnUnsupported.dhall -let Model = Deps.Sdk.Project +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } let Input = Model.QueryFragments @@ -47,8 +52,8 @@ let renderSql Prelude.Text.concatMap Model.QueryFragment renderFragment fragments let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(input : Input) -> Compiled.ok Output { sqlLiteral = renderSql input } -in Algebra.module Input Output run +in Sdk.Sigs.interpreter Config Input Output run diff --git a/gen/Interpreters/Result.dhall b/gen/Interpreters/Result.dhall index a6638e3..83f3ef1 100644 --- a/gen/Interpreters/Result.dhall +++ b/gen/Interpreters/Result.dhall @@ -1,20 +1,25 @@ -let Deps = ../Deps/package.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Interpreter.dhall +let Lude = ../Deps/Lude.dhall + +let Model = ../Deps/Contract.dhall let ImportSet = ../Structures/ImportSet.dhall let CustomKind = ../Structures/CustomKind.dhall -let ResultColumns = ./ResultColumns.dhall - -let Prelude = Deps.Prelude +let OnUnsupported = ../Structures/OnUnsupported.dhall -let Lude = Deps.Lude +let ResultColumns = ./ResultColumns.dhall let Compiled = Lude.Compiled -let Model = Deps.Sdk.Project +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } let Input = Model.Result @@ -57,7 +62,7 @@ let cardinalityShape cardinality let rowsOutput = - \(config : Algebra.Config) -> + \(config : Config) -> \(lookup : CustomKind.Lookup) -> \(rowClassName : Text) -> \(rows : Model.ResultRows) -> @@ -84,7 +89,7 @@ let rowsOutput = (ResultColumns.run config lookup rowClassName columns) let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(lookup : CustomKind.Lookup) -> \(rowClassName : Text) -> \(input : Input) -> @@ -96,4 +101,7 @@ let run = } input +let Run = + Config -> CustomKind.Lookup -> Text -> Input -> Lude.Compiled.Type Output + in { Input, Output, RowClass, run } diff --git a/gen/Interpreters/ResultColumns.dhall b/gen/Interpreters/ResultColumns.dhall index ef550ec..b2fc737 100644 --- a/gen/Interpreters/ResultColumns.dhall +++ b/gen/Interpreters/ResultColumns.dhall @@ -1,20 +1,25 @@ -let Deps = ../Deps/package.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Interpreter.dhall +let Lude = ../Deps/Lude.dhall + +let Model = ../Deps/Contract.dhall let ImportSet = ../Structures/ImportSet.dhall let CustomKind = ../Structures/CustomKind.dhall -let Member = ./Member.dhall - -let Prelude = Deps.Prelude +let OnUnsupported = ../Structures/OnUnsupported.dhall -let Lude = Deps.Lude +let Member = ./Member.dhall let Compiled = Lude.Compiled -let Model = Deps.Sdk.Project +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } let Input = List Model.Member @@ -53,7 +58,7 @@ let assemble } let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(lookup : CustomKind.Lookup) -> \(rowClassName : Text) -> \(input : Input) -> @@ -73,4 +78,7 @@ let run = input ) +let Run = + Config -> CustomKind.Lookup -> Text -> Input -> Lude.Compiled.Type Output + in { Input, Output, run } diff --git a/gen/Interpreters/Scalar.dhall b/gen/Interpreters/Scalar.dhall index b0eff70..63ec788 100644 --- a/gen/Interpreters/Scalar.dhall +++ b/gen/Interpreters/Scalar.dhall @@ -1,15 +1,22 @@ -let Deps = ../Deps/package.dhall +let Lude = ../Deps/Lude.dhall -let ImportSet = ../Structures/ImportSet.dhall +let Model = ../Deps/Contract.dhall -let Algebra = ../Algebras/Interpreter.dhall +let Sdk = ../Deps/Sdk.dhall -let Lude = Deps.Lude +let ImportSet = ../Structures/ImportSet.dhall -let Model = Deps.Sdk.Project +let OnUnsupported = ../Structures/OnUnsupported.dhall let Primitive = ./Primitive.dhall +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } + let Input = Model.Scalar -- Passthrough for primitives; Custom is opaque here. The enum-vs-composite @@ -24,7 +31,7 @@ let Output = } let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(input : Input) -> merge { Primitive = @@ -52,4 +59,4 @@ let run = } input -in Algebra.module Input Output run /\ { ScalarDecode } +in Sdk.Sigs.interpreter Config Input Output run /\ { ScalarDecode } diff --git a/gen/Interpreters/Value.dhall b/gen/Interpreters/Value.dhall index de69fd0..e1c8d6a 100644 --- a/gen/Interpreters/Value.dhall +++ b/gen/Interpreters/Value.dhall @@ -1,17 +1,24 @@ -let Deps = ../Deps/package.dhall +let Lude = ../Deps/Lude.dhall -let ImportSet = ../Structures/ImportSet.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Interpreter.dhall +let Model = ../Deps/Contract.dhall -let Lude = Deps.Lude +let Sdk = ../Deps/Sdk.dhall -let Prelude = Deps.Prelude +let ImportSet = ../Structures/ImportSet.dhall -let Model = Deps.Sdk.Project +let OnUnsupported = ../Structures/OnUnsupported.dhall let Scalar = ./Scalar.dhall +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } + let Input = Model.Value let Output = @@ -23,7 +30,7 @@ let Output = } let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(input : Input) -> Lude.Compiled.map Scalar.Output @@ -62,4 +69,4 @@ let run = ) (Scalar.run config input.scalar) -in Algebra.module Input Output run +in Sdk.Sigs.interpreter Config Input Output run diff --git a/gen/Structures/CustomKind.dhall b/gen/Structures/CustomKind.dhall index 22d3031..f2c4a1b 100644 --- a/gen/Structures/CustomKind.dhall +++ b/gen/Structures/CustomKind.dhall @@ -1,6 +1,4 @@ -let Deps = ../Deps/package.dhall - -let Model = Deps.Sdk.Project +let Model = ../Deps/Contract.dhall -- A composite field as the decode/encode sites need it: the Python attribute -- name and the rendered Python type (already nullability-applied). Threaded so diff --git a/gen/Templates/CompositeModule.dhall b/gen/Templates/CompositeModule.dhall index 198c77d..b1ab3e0 100644 --- a/gen/Templates/CompositeModule.dhall +++ b/gen/Templates/CompositeModule.dhall @@ -1,6 +1,6 @@ -let Deps = ../Deps/package.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Template.dhall +let Sdk = ../Deps/Sdk.dhall let Field = { fieldName : Text, fieldType : Text } @@ -12,7 +12,7 @@ let Params = { typeName : Text, extraImports : List Text, fields : List Field } let run = \(params : Params) -> let fieldLines = - Deps.Prelude.Text.concatMapSep + Prelude.Text.concatMapSep "\n" Field ( \(field : Field) -> @@ -21,12 +21,12 @@ let run = params.fields let imports = - if Deps.Prelude.List.null Text params.extraImports + if Prelude.List.null Text params.extraImports then "from dataclasses import dataclass" else '' from dataclasses import dataclass - ${Deps.Prelude.Text.concatSep "\n" params.extraImports}'' + ${Prelude.Text.concatSep "\n" params.extraImports}'' in '' ${imports} @@ -43,4 +43,4 @@ let run = ${fieldLines} '' -in Algebra.module Params run /\ { Field } +in Sdk.Sigs.template Params run /\ { Field } diff --git a/gen/Templates/CoreModule.dhall b/gen/Templates/CoreModule.dhall index d3938c2..34a4943 100644 --- a/gen/Templates/CoreModule.dhall +++ b/gen/Templates/CoreModule.dhall @@ -1,4 +1,4 @@ -let Algebra = ../Algebras/Template.dhall +let Sdk = ../Deps/Sdk.dhall -- The surface-agnostic core of a generated package, emitted once at -- _generated/_core.py. It owns the names shared by every module and by both the @@ -43,4 +43,4 @@ let content = ) '' -in Algebra.module {} (\(_ : {}) -> content) +in Sdk.Sigs.template {} (\(_ : {}) -> content) diff --git a/gen/Templates/EnumModule.dhall b/gen/Templates/EnumModule.dhall index 7f82ebf..1ebf486 100644 --- a/gen/Templates/EnumModule.dhall +++ b/gen/Templates/EnumModule.dhall @@ -1,6 +1,6 @@ -let Deps = ../Deps/package.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Template.dhall +let Sdk = ../Deps/Sdk.dhall let Variant = { memberName : Text, pgValue : Text } @@ -16,18 +16,18 @@ let run = let escapeLabel : Text -> Text = \(raw : Text) -> - Deps.Prelude.Function.composeList + Prelude.Function.composeList Text - [ Deps.Prelude.Text.replace "\\" "\\\\" - , Deps.Prelude.Text.replace "\r" "\\r" - , Deps.Prelude.Text.replace "\n" "\\n" - , Deps.Prelude.Text.replace "\t" "\\t" - , Deps.Prelude.Text.replace "\"" "\\\"" + [ Prelude.Text.replace "\\" "\\\\" + , Prelude.Text.replace "\r" "\\r" + , Prelude.Text.replace "\n" "\\n" + , Prelude.Text.replace "\t" "\\t" + , Prelude.Text.replace "\"" "\\\"" ] raw let memberLines = - Deps.Prelude.Text.concatMapSep + Prelude.Text.concatMapSep "\n" Variant ( \(variant : Variant) -> @@ -43,4 +43,4 @@ let run = ${memberLines} '' -in Algebra.module Params run /\ { Variant } +in Sdk.Sigs.template Params run /\ { Variant } diff --git a/gen/Templates/FacadeModule.dhall b/gen/Templates/FacadeModule.dhall index 2beb74c..18d6f2e 100644 --- a/gen/Templates/FacadeModule.dhall +++ b/gen/Templates/FacadeModule.dhall @@ -1,8 +1,6 @@ -let Deps = ../Deps/package.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Template.dhall - -let Prelude = Deps.Prelude +let Sdk = ../Deps/Sdk.dhall -- A statement's public surface: the function and, when the query returns rows, -- its frozen Row dataclass. functionName doubles as the leaf module name. @@ -126,4 +124,4 @@ let run = ${allEntries}] '' -in Algebra.module Params run /\ { StatementExport, TypeExport } +in Sdk.Sigs.template Params run /\ { StatementExport, TypeExport } diff --git a/gen/Templates/InitModule.dhall b/gen/Templates/InitModule.dhall index f609ed6..cf5fc9f 100644 --- a/gen/Templates/InitModule.dhall +++ b/gen/Templates/InitModule.dhall @@ -1,4 +1,4 @@ -let Algebra = ../Algebras/Template.dhall +let Sdk = ../Deps/Sdk.dhall -- A minimal package __init__.py: a module docstring plus an empty __all__. -- Used for the _generated subpackage root and the statements subpackage. The @@ -15,4 +15,4 @@ let render = __all__: list[str] = [] '' -in Algebra.module Params render +in Sdk.Sigs.template Params render diff --git a/gen/Templates/RegisterModule.dhall b/gen/Templates/RegisterModule.dhall index ec6886b..cc94bfb 100644 --- a/gen/Templates/RegisterModule.dhall +++ b/gen/Templates/RegisterModule.dhall @@ -1,11 +1,9 @@ -let Deps = ../Deps/package.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Template.dhall +let Sdk = ../Deps/Sdk.dhall let Surface = ../Structures/Surface.dhall -let Prelude = Deps.Prelude - -- Per-connection type registration, emitted once per surface. psycopg decodes an -- unregistered composite as a text string; registering its CompositeInfo makes -- it decode to a namedtuple, which the generated decode then splats into the @@ -96,4 +94,4 @@ let run = in Prelude.Text.concatSep "\n" allLines ++ "\n" -in Algebra.module Params run +in Sdk.Sigs.template Params run diff --git a/gen/Templates/RowsModule.dhall b/gen/Templates/RowsModule.dhall index 0107fa2..18da31b 100644 --- a/gen/Templates/RowsModule.dhall +++ b/gen/Templates/RowsModule.dhall @@ -1,10 +1,10 @@ -let Deps = ../Deps/package.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Template.dhall +let Lude = ../Deps/Lude.dhall -let ImportSet = ../Structures/ImportSet.dhall +let Sdk = ../Deps/Sdk.dhall -let Prelude = Deps.Prelude +let ImportSet = ../Structures/ImportSet.dhall -- The shared row module: every query's frozen Row dataclass and its decode -- function live here once, so the async and sync statement modules import the @@ -25,7 +25,7 @@ let indentAll \(text : Text) -> let pad = Prelude.Text.replicate n " " - in pad ++ Deps.Lude.Text.indentNonEmpty n text + in pad ++ Lude.Text.indentNonEmpty n text let importLineIf : Bool -> Text -> List Text @@ -118,4 +118,4 @@ let run = ++ Prelude.Text.concatMapSep "\n\n\n" RowDef renderRow params.rows ++ "\n" -in Algebra.module Params run /\ { RowDef } +in Sdk.Sigs.template Params run /\ { RowDef } diff --git a/gen/Templates/RuntimeModule.dhall b/gen/Templates/RuntimeModule.dhall index ddaf53a..baa56bc 100644 --- a/gen/Templates/RuntimeModule.dhall +++ b/gen/Templates/RuntimeModule.dhall @@ -1,4 +1,4 @@ -let Algebra = ../Algebras/Template.dhall +let Sdk = ../Deps/Sdk.dhall -- The fixed _runtime.py body, emitted once per generated package. No per-query -- customization. Mirrors DESIGN section 3 with two strict-clean adjustments the @@ -160,5 +160,5 @@ let syncContent = _ = cur.execute(sql, params) '' -in Algebra.module {} (\(_ : {}) -> content) +in Sdk.Sigs.template {} (\(_ : {}) -> content) /\ { runSync = \(_ : {}) -> syncContent } diff --git a/gen/Templates/StatementModule.dhall b/gen/Templates/StatementModule.dhall index f717aa7..f2a6f61 100644 --- a/gen/Templates/StatementModule.dhall +++ b/gen/Templates/StatementModule.dhall @@ -1,13 +1,13 @@ -let Algebra = ../Algebras/Template.dhall +let Prelude = ../Deps/Prelude.dhall -let Deps = ../Deps/package.dhall +let Lude = ../Deps/Lude.dhall + +let Sdk = ../Deps/Sdk.dhall let ImportSet = ../Structures/ImportSet.dhall let Surface = ../Structures/Surface.dhall -let Prelude = Deps.Prelude - -- Prefix every line (including the first) with `n` spaces, leaving blank lines -- untouched so trailing whitespace never appears. let indentAll @@ -16,7 +16,7 @@ let indentAll \(text : Text) -> let pad = Prelude.Text.replicate n " " - in pad ++ Deps.Lude.Text.indentNonEmpty n text + in pad ++ Lude.Text.indentNonEmpty n text -- A statement module is the thin per-surface I/O wrapper: it imports its Row -- dataclass and decode function from the shared `_rows` module and renders one @@ -187,7 +187,7 @@ let renderCall then "return ${await}${params.helperName}(conn, _SQL, params, ${params.decodeName})" else "return ${await}${params.helperName}(conn, _SQL, params)" -in Algebra.module +in Sdk.Sigs.template Params ( \(params : Params) -> renderImports params diff --git a/gen/Templates/TypesInit.dhall b/gen/Templates/TypesInit.dhall index 7583526..3182085 100644 --- a/gen/Templates/TypesInit.dhall +++ b/gen/Templates/TypesInit.dhall @@ -1,6 +1,6 @@ -let Deps = ../Deps/package.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Template.dhall +let Sdk = ../Deps/Sdk.dhall let Export = { moduleName : Text, typeName : Text } @@ -9,7 +9,7 @@ let Params = { exports : List Export } let run = \(params : Params) -> let exportLines = - Deps.Prelude.Text.concatMapSep + Prelude.Text.concatMapSep "\n" Export ( \(export : Export) -> @@ -21,4 +21,4 @@ let run = ${exportLines} '' -in Algebra.module Params run /\ { Export } +in Sdk.Sigs.template Params run /\ { Export } From a5b4530f477c839541374ac485fa7892e76f52ae Mon Sep 17 00:00:00 2001 From: Nikita Volkov Date: Sat, 11 Jul 2026 09:31:19 +0300 Subject: [PATCH 2/9] feat(gen): rewrite compile.dhall/Gen.dhall as Interpret.dhall/package.dhall Task 4 of the Dhall dependency/layout migration: consumes Interpreters/Project.dhall's Sdk.Sigs.interpreter-shaped run and produces the Sdk.Sigs.generator-built entry point, now that the Deps barrel is gone (Tasks 1-3). - gen/compile.dhall -> gen/Interpret.dhall: drops the outer Optional Config unwrap (Sdk.Sigs.generator substitutes defaultConfig for the omitted-block case), keeps the per-field Prelude.Optional.fold defaults and importName derivation; imports Contract/Prelude directly instead of through the deleted barrel. - gen/Gen.dhall -> gen/package.dhall: builds Sdk.Sigs.generator Config Config/default interpret, with Config/default supplying an all-None Config. --- gen/Gen.dhall | 3 -- gen/Interpret.dhall | 54 ++++++++++++++++++++++++++++++++++++ gen/compile.dhall | 67 --------------------------------------------- gen/package.dhall | 16 +++++++++++ 4 files changed, 70 insertions(+), 70 deletions(-) delete mode 100644 gen/Gen.dhall create mode 100644 gen/Interpret.dhall delete mode 100644 gen/compile.dhall create mode 100644 gen/package.dhall diff --git a/gen/Gen.dhall b/gen/Gen.dhall deleted file mode 100644 index 9cb87fb..0000000 --- a/gen/Gen.dhall +++ /dev/null @@ -1,3 +0,0 @@ -let Sdk = ./Deps/Sdk.dhall - -in Sdk.module ./Config.dhall ./compile.dhall diff --git a/gen/Interpret.dhall b/gen/Interpret.dhall new file mode 100644 index 0000000..de4642f --- /dev/null +++ b/gen/Interpret.dhall @@ -0,0 +1,54 @@ +let Contract = ./Deps/Contract.dhall + +let Prelude = ./Deps/Prelude.dhall + +let Config = ./Config.dhall + +let OnUnsupported = ./Structures/OnUnsupported.dhall + +let ProjectInterpreter = ./Interpreters/Project.dhall + +-- Entry point handed to gen-sdk's Sdk.Sigs.generator as `interpret`. Each +-- field of Config is independently Optional, so a project may omit the +-- whole config block (Sdk.Sigs.generator substitutes an all-None +-- defaultConfig, see package.dhall) or any subset of its keys; `defaults` +-- collects every fallback in one place (packageName from the project name in +-- kebab case, emitSync off, onUnsupported Fail). The async surface is always +-- emitted; emitSync adds the sync mirror. +in \(config : Config) -> + \(project : Contract.Project) -> + let defaults = + { packageName = project.name.inKebabCase + , emitSync = False + , onUnsupported = OnUnsupported.Mode.Fail + } + + let packageName = + Prelude.Optional.fold + Text + config.packageName + Text + (\(t : Text) -> t) + defaults.packageName + + let emitSync = + Prelude.Optional.fold + Bool + config.emitSync + Bool + (\(b : Bool) -> b) + defaults.emitSync + + let onUnsupported = + Prelude.Optional.fold + OnUnsupported.Mode + config.onUnsupported + OnUnsupported.Mode + (\(m : OnUnsupported.Mode) -> m) + defaults.onUnsupported + + let importName = Prelude.Text.replace "-" "_" packageName + + let interpreterConfig = { packageName, importName, emitSync, onUnsupported } + + in ProjectInterpreter.run interpreterConfig project diff --git a/gen/compile.dhall b/gen/compile.dhall deleted file mode 100644 index cd12642..0000000 --- a/gen/compile.dhall +++ /dev/null @@ -1,67 +0,0 @@ -let Deps = ./Deps/package.dhall - -let Model = Deps.Sdk.Project - -let Prelude = Deps.Prelude - -let Config = ./Config.dhall - -let OnUnsupported = ./Structures/OnUnsupported.dhall - -let ProjectInterpreter = ./Interpreters/Project.dhall - --- Entry point handed to gen-sdk's module. `config` and each of its fields are --- Optional, so a project may omit the whole config block or any subset of its --- keys; `defaults` collects every fallback in one place (packageName from the --- project name in kebab case, emitSync off, onUnsupported Fail), so a future --- knob's default is added here alongside the others. The async surface is --- always emitted; emitSync adds the sync mirror. -in \(config : Optional Config) -> - \(project : Model.Project) -> - let defaults = - { packageName = project.name.inKebabCase - , emitSync = False - , onUnsupported = OnUnsupported.Mode.Fail - } - - let packageName = - Prelude.Optional.fold - Config - config - Text - (\(c : Config) -> - Prelude.Optional.fold Text c.packageName Text (\(t : Text) -> t) defaults.packageName - ) - defaults.packageName - - let emitSync = - Prelude.Optional.fold - Config - config - Bool - (\(c : Config) -> - Prelude.Optional.fold Bool c.emitSync Bool (\(b : Bool) -> b) defaults.emitSync - ) - defaults.emitSync - - let onUnsupported = - Prelude.Optional.fold - Config - config - OnUnsupported.Mode - ( \(c : Config) -> - Prelude.Optional.fold - OnUnsupported.Mode - c.onUnsupported - OnUnsupported.Mode - (\(m : OnUnsupported.Mode) -> m) - defaults.onUnsupported - ) - defaults.onUnsupported - - let importName = Prelude.Text.replace "-" "_" packageName - - let interpreterConfig = - { packageName, importName, emitSync, onUnsupported } - - in ProjectInterpreter.run interpreterConfig project diff --git a/gen/package.dhall b/gen/package.dhall new file mode 100644 index 0000000..3d07cf7 --- /dev/null +++ b/gen/package.dhall @@ -0,0 +1,16 @@ +let Sdk = ./Deps/Sdk.dhall + +let OnUnsupported = ./Structures/OnUnsupported.dhall + +let Config = ./Config.dhall + +let Config/default + : Config + = { packageName = None Text + , emitSync = None Bool + , onUnsupported = None OnUnsupported.Mode + } + +let interpret = ./Interpret.dhall + +in Sdk.Sigs.generator Config Config/default interpret From b13e42764765280e075d9390e9cea31dd5e3db8c Mon Sep 17 00:00:00 2001 From: Nikita Volkov Date: Sat, 11 Jul 2026 09:38:42 +0300 Subject: [PATCH 3/9] refactor(gen): rename gen/ to src/, move tests/Exhaustive.dhall to demos/ Pure directory move for gen/ -> src/ (relative imports inside are unaffected). tests/Exhaustive.dhall becomes demos/Exhaustive.dhall and is rewritten to use Sdk.Output.toFileMap, since Sdk.Sigs.generator-built modules no longer expose compileToFileMap. --- {tests => demos}/Exhaustive.dhall | 16 ++++++++-------- {gen => src}/Config.dhall | 0 {gen => src}/Deps/Contract.dhall | 0 {gen => src}/Deps/Lude.dhall | 0 {gen => src}/Deps/Prelude.dhall | 0 {gen => src}/Deps/Sdk.dhall | 0 {gen => src}/Interpret.dhall | 0 {gen => src}/Interpreters/CustomType.dhall | 0 {gen => src}/Interpreters/Member.dhall | 0 {gen => src}/Interpreters/ParamsMember.dhall | 0 {gen => src}/Interpreters/Primitive.dhall | 0 {gen => src}/Interpreters/Project.dhall | 0 {gen => src}/Interpreters/Query.dhall | 0 {gen => src}/Interpreters/QueryFragments.dhall | 0 {gen => src}/Interpreters/Result.dhall | 0 {gen => src}/Interpreters/ResultColumns.dhall | 0 {gen => src}/Interpreters/Scalar.dhall | 0 {gen => src}/Interpreters/Value.dhall | 0 {gen => src}/Structures/CustomKind.dhall | 0 {gen => src}/Structures/ImportSet.dhall | 0 {gen => src}/Structures/OnUnsupported.dhall | 0 {gen => src}/Structures/PyIdent.dhall | 0 {gen => src}/Structures/Surface.dhall | 0 {gen => src}/Templates/CompositeModule.dhall | 0 {gen => src}/Templates/CoreModule.dhall | 0 {gen => src}/Templates/EnumModule.dhall | 0 {gen => src}/Templates/FacadeModule.dhall | 0 {gen => src}/Templates/InitModule.dhall | 0 {gen => src}/Templates/RegisterModule.dhall | 0 {gen => src}/Templates/RowsModule.dhall | 0 {gen => src}/Templates/RuntimeModule.dhall | 0 {gen => src}/Templates/StatementModule.dhall | 0 {gen => src}/Templates/TypesInit.dhall | 0 {gen => src}/package.dhall | 0 34 files changed, 8 insertions(+), 8 deletions(-) rename {tests => demos}/Exhaustive.dhall (63%) rename {gen => src}/Config.dhall (100%) rename {gen => src}/Deps/Contract.dhall (100%) rename {gen => src}/Deps/Lude.dhall (100%) rename {gen => src}/Deps/Prelude.dhall (100%) rename {gen => src}/Deps/Sdk.dhall (100%) rename {gen => src}/Interpret.dhall (100%) rename {gen => src}/Interpreters/CustomType.dhall (100%) rename {gen => src}/Interpreters/Member.dhall (100%) rename {gen => src}/Interpreters/ParamsMember.dhall (100%) rename {gen => src}/Interpreters/Primitive.dhall (100%) rename {gen => src}/Interpreters/Project.dhall (100%) rename {gen => src}/Interpreters/Query.dhall (100%) rename {gen => src}/Interpreters/QueryFragments.dhall (100%) rename {gen => src}/Interpreters/Result.dhall (100%) rename {gen => src}/Interpreters/ResultColumns.dhall (100%) rename {gen => src}/Interpreters/Scalar.dhall (100%) rename {gen => src}/Interpreters/Value.dhall (100%) rename {gen => src}/Structures/CustomKind.dhall (100%) rename {gen => src}/Structures/ImportSet.dhall (100%) rename {gen => src}/Structures/OnUnsupported.dhall (100%) rename {gen => src}/Structures/PyIdent.dhall (100%) rename {gen => src}/Structures/Surface.dhall (100%) rename {gen => src}/Templates/CompositeModule.dhall (100%) rename {gen => src}/Templates/CoreModule.dhall (100%) rename {gen => src}/Templates/EnumModule.dhall (100%) rename {gen => src}/Templates/FacadeModule.dhall (100%) rename {gen => src}/Templates/InitModule.dhall (100%) rename {gen => src}/Templates/RegisterModule.dhall (100%) rename {gen => src}/Templates/RowsModule.dhall (100%) rename {gen => src}/Templates/RuntimeModule.dhall (100%) rename {gen => src}/Templates/StatementModule.dhall (100%) rename {gen => src}/Templates/TypesInit.dhall (100%) rename {gen => src}/package.dhall (100%) diff --git a/tests/Exhaustive.dhall b/demos/Exhaustive.dhall similarity index 63% rename from tests/Exhaustive.dhall rename to demos/Exhaustive.dhall index bfe8b84..35e8f28 100644 --- a/tests/Exhaustive.dhall +++ b/demos/Exhaustive.dhall @@ -1,9 +1,9 @@ -- Applies this generator to gen-sdk's shared cross-backend fixture project --- (the same "music_catalogue" project java.gen's own tests/Exhaustive.dhall +-- (the same "music_catalogue" project java.gen's own demos/Exhaustive.dhall -- exercises), so a Python client compiles from it and passes basedpyright -- strict. Pinned directly at gen-sdk's package.dhall, separately from --- gen/Deps/Sdk.dhall: that file only imports gen-sdk's `module.dhall` (the --- generator-construction function), which has no `Fixtures` field. +-- src/Deps/Sdk.dhall: that file only imports gen-sdk's `package.dhall` `as +-- Source` for RAM, and this fixture load doesn't need that mode. -- -- The fixture project deliberately covers PG types this generator does not -- support (box, inet, money, ranges, ...), so onUnsupported is set to Skip: @@ -13,13 +13,13 @@ -- Intended to be executed with: -- -- ```bash --- dhall to-directory-tree --file tests/Exhaustive.dhall --output --allow-path-separators +-- dhall to-directory-tree --file demos/Exhaustive.dhall --output --allow-path-separators -- ``` -let Sdk = ../gen/Deps/Sdk.dhall +let Sdk = ../src/Deps/Sdk.dhall -let Gen = ../gen/Gen.dhall +let Gen = ../src/package.dhall -let OnUnsupported = ../gen/Structures/OnUnsupported.dhall +let OnUnsupported = ../src/Structures/OnUnsupported.dhall let project = Sdk.Fixtures.Exhaustive @@ -30,4 +30,4 @@ let config = , onUnsupported = Some OnUnsupported.Mode.Skip } -in Gen.compileToFileMap config project +in Sdk.Output.toFileMap (Gen.compile config project) diff --git a/gen/Config.dhall b/src/Config.dhall similarity index 100% rename from gen/Config.dhall rename to src/Config.dhall diff --git a/gen/Deps/Contract.dhall b/src/Deps/Contract.dhall similarity index 100% rename from gen/Deps/Contract.dhall rename to src/Deps/Contract.dhall diff --git a/gen/Deps/Lude.dhall b/src/Deps/Lude.dhall similarity index 100% rename from gen/Deps/Lude.dhall rename to src/Deps/Lude.dhall diff --git a/gen/Deps/Prelude.dhall b/src/Deps/Prelude.dhall similarity index 100% rename from gen/Deps/Prelude.dhall rename to src/Deps/Prelude.dhall diff --git a/gen/Deps/Sdk.dhall b/src/Deps/Sdk.dhall similarity index 100% rename from gen/Deps/Sdk.dhall rename to src/Deps/Sdk.dhall diff --git a/gen/Interpret.dhall b/src/Interpret.dhall similarity index 100% rename from gen/Interpret.dhall rename to src/Interpret.dhall diff --git a/gen/Interpreters/CustomType.dhall b/src/Interpreters/CustomType.dhall similarity index 100% rename from gen/Interpreters/CustomType.dhall rename to src/Interpreters/CustomType.dhall diff --git a/gen/Interpreters/Member.dhall b/src/Interpreters/Member.dhall similarity index 100% rename from gen/Interpreters/Member.dhall rename to src/Interpreters/Member.dhall diff --git a/gen/Interpreters/ParamsMember.dhall b/src/Interpreters/ParamsMember.dhall similarity index 100% rename from gen/Interpreters/ParamsMember.dhall rename to src/Interpreters/ParamsMember.dhall diff --git a/gen/Interpreters/Primitive.dhall b/src/Interpreters/Primitive.dhall similarity index 100% rename from gen/Interpreters/Primitive.dhall rename to src/Interpreters/Primitive.dhall diff --git a/gen/Interpreters/Project.dhall b/src/Interpreters/Project.dhall similarity index 100% rename from gen/Interpreters/Project.dhall rename to src/Interpreters/Project.dhall diff --git a/gen/Interpreters/Query.dhall b/src/Interpreters/Query.dhall similarity index 100% rename from gen/Interpreters/Query.dhall rename to src/Interpreters/Query.dhall diff --git a/gen/Interpreters/QueryFragments.dhall b/src/Interpreters/QueryFragments.dhall similarity index 100% rename from gen/Interpreters/QueryFragments.dhall rename to src/Interpreters/QueryFragments.dhall diff --git a/gen/Interpreters/Result.dhall b/src/Interpreters/Result.dhall similarity index 100% rename from gen/Interpreters/Result.dhall rename to src/Interpreters/Result.dhall diff --git a/gen/Interpreters/ResultColumns.dhall b/src/Interpreters/ResultColumns.dhall similarity index 100% rename from gen/Interpreters/ResultColumns.dhall rename to src/Interpreters/ResultColumns.dhall diff --git a/gen/Interpreters/Scalar.dhall b/src/Interpreters/Scalar.dhall similarity index 100% rename from gen/Interpreters/Scalar.dhall rename to src/Interpreters/Scalar.dhall diff --git a/gen/Interpreters/Value.dhall b/src/Interpreters/Value.dhall similarity index 100% rename from gen/Interpreters/Value.dhall rename to src/Interpreters/Value.dhall diff --git a/gen/Structures/CustomKind.dhall b/src/Structures/CustomKind.dhall similarity index 100% rename from gen/Structures/CustomKind.dhall rename to src/Structures/CustomKind.dhall diff --git a/gen/Structures/ImportSet.dhall b/src/Structures/ImportSet.dhall similarity index 100% rename from gen/Structures/ImportSet.dhall rename to src/Structures/ImportSet.dhall diff --git a/gen/Structures/OnUnsupported.dhall b/src/Structures/OnUnsupported.dhall similarity index 100% rename from gen/Structures/OnUnsupported.dhall rename to src/Structures/OnUnsupported.dhall diff --git a/gen/Structures/PyIdent.dhall b/src/Structures/PyIdent.dhall similarity index 100% rename from gen/Structures/PyIdent.dhall rename to src/Structures/PyIdent.dhall diff --git a/gen/Structures/Surface.dhall b/src/Structures/Surface.dhall similarity index 100% rename from gen/Structures/Surface.dhall rename to src/Structures/Surface.dhall diff --git a/gen/Templates/CompositeModule.dhall b/src/Templates/CompositeModule.dhall similarity index 100% rename from gen/Templates/CompositeModule.dhall rename to src/Templates/CompositeModule.dhall diff --git a/gen/Templates/CoreModule.dhall b/src/Templates/CoreModule.dhall similarity index 100% rename from gen/Templates/CoreModule.dhall rename to src/Templates/CoreModule.dhall diff --git a/gen/Templates/EnumModule.dhall b/src/Templates/EnumModule.dhall similarity index 100% rename from gen/Templates/EnumModule.dhall rename to src/Templates/EnumModule.dhall diff --git a/gen/Templates/FacadeModule.dhall b/src/Templates/FacadeModule.dhall similarity index 100% rename from gen/Templates/FacadeModule.dhall rename to src/Templates/FacadeModule.dhall diff --git a/gen/Templates/InitModule.dhall b/src/Templates/InitModule.dhall similarity index 100% rename from gen/Templates/InitModule.dhall rename to src/Templates/InitModule.dhall diff --git a/gen/Templates/RegisterModule.dhall b/src/Templates/RegisterModule.dhall similarity index 100% rename from gen/Templates/RegisterModule.dhall rename to src/Templates/RegisterModule.dhall diff --git a/gen/Templates/RowsModule.dhall b/src/Templates/RowsModule.dhall similarity index 100% rename from gen/Templates/RowsModule.dhall rename to src/Templates/RowsModule.dhall diff --git a/gen/Templates/RuntimeModule.dhall b/src/Templates/RuntimeModule.dhall similarity index 100% rename from gen/Templates/RuntimeModule.dhall rename to src/Templates/RuntimeModule.dhall diff --git a/gen/Templates/StatementModule.dhall b/src/Templates/StatementModule.dhall similarity index 100% rename from gen/Templates/StatementModule.dhall rename to src/Templates/StatementModule.dhall diff --git a/gen/Templates/TypesInit.dhall b/src/Templates/TypesInit.dhall similarity index 100% rename from gen/Templates/TypesInit.dhall rename to src/Templates/TypesInit.dhall diff --git a/gen/package.dhall b/src/package.dhall similarity index 100% rename from gen/package.dhall rename to src/package.dhall From 2c9073fc3716a54536e77f2cebfe2878c2b21d0a Mon Sep 17 00:00:00 2001 From: Nikita Volkov Date: Sat, 11 Jul 2026 10:01:44 +0300 Subject: [PATCH 4/9] refactor(gen): update path references from gen/ to src/, tests/Exhaustive.dhall to demos/ Tasks 1-5 renamed gen/ to src/, gen/Gen.dhall to src/package.dhall, and tests/Exhaustive.dhall to demos/Exhaustive.dhall. This updates every external reference to the old paths across CI workflows, dev scripts, docs, and the bench harness. - .github/workflows/ci.yml, release.yml, build-contract-shell.sh: path refs. - README.md, AGENTS.md, DESIGN.md: doc path refs; DESIGN.md's section 10 tree diagram and entry-point description rewritten to match the actual post-move src/ layout (Interpret.dhall/package.dhall, no more Algebras/, Sdk.Sigs.generator instead of Sdk.module). - build.bash, mise.toml: dev-script path refs. - bench/generate.sh, bench/as-source.sh: gen/ -> src/ copies; the as-Source/plain-import sha256 substitution pair for gen-sdk is verified via `dhall hash` to be a no-op for v2.0.0 (both modes hash to the same value, unlike the old v0.11.0 pair), so only a comment remains; the lude pair is untouched since lude's pin is unchanged by this migration. - tests/fixture-project/project1.pgn.yaml: the fixture project's own `gen:` key still pointed at the now-deleted gen/Gen.dhall, which would break the harness and the `golden` mise task; updated to src/package.dhall. --- .github/scripts/build-contract-shell.sh | 2 +- .github/workflows/ci.yml | 8 ++--- .github/workflows/release.yml | 2 +- AGENTS.md | 2 +- DESIGN.md | 46 ++++++++++++++----------- README.md | 10 +++--- bench/as-source.sh | 24 ++++++++++--- bench/generate.sh | 26 ++++++++++---- mise.toml | 8 ++--- tests/fixture-project/project1.pgn.yaml | 14 ++++---- 10 files changed, 87 insertions(+), 55 deletions(-) diff --git a/.github/scripts/build-contract-shell.sh b/.github/scripts/build-contract-shell.sh index 3845a1d..9fbc79c 100755 --- a/.github/scripts/build-contract-shell.sh +++ b/.github/scripts/build-contract-shell.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Wraps the Dhall-generated package (from tests/Exhaustive.dhall) in a minimal +# Wraps the Dhall-generated package (from demos/Exhaustive.dhall) in a minimal # consumer shell, mirroring the hand-written tests/golden/ shell (pyproject.toml # + py.typed) so basedpyright strict runs against the same layout a real # consumer would import, per the full_package pattern in tests/conftest.py. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7664a04..b5d526f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,7 +121,7 @@ jobs: with: ref: ${{ inputs.ref || github.sha }} - # The `as Source` import mode on gen/Deps only changes how pgn loads the + # The `as Source` import mode on src/Deps only changes how pgn loads the # remote packages (unnormalized, to save RAM), not what they evaluate to. # The pinned action below bundles a dhall fork that predates the mode, so # strip it here; the evaluation is semantically identical either way. The @@ -132,16 +132,16 @@ jobs: shell: bash run: | set -euo pipefail - sed -i -e 's/^[[:space:]]*as Source$//' -e 's/^[[:space:]]*sha256:[0-9a-f]\{64\}$//' gen/Deps/*.dhall + sed -i -e 's/^[[:space:]]*as Source$//' -e 's/^[[:space:]]*sha256:[0-9a-f]\{64\}$//' src/Deps/*.dhall - # A plain, standard-Dhall evaluator cannot run this: gen/Interpreters/Project.dhall's + # A plain, standard-Dhall evaluator cannot run this: src/Interpreters/Project.dhall's # buildLookup and gen-sdk's own Fixtures.Exhaustive both use Text/equal, a builtin # from pgn's forked Dhall, absent from the upstream dhall-lang Prelude. This action # bundles that same fork. - name: Generate output from Dhall uses: nikita-volkov/dhall-directory-tree.github-action@60a18dc647d6daea805263ea0fed7bb8011f3bcd # v2 with: - dhall_file: tests/Exhaustive.dhall + dhall_file: demos/Exhaustive.dhall output_dir: contract-output - name: Assert the compile did not fail diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 25e92c7..91764bc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -86,7 +86,7 @@ jobs: id: resolve uses: nikita-volkov/dhall-resolve.github-action@7caaf1fdb40ac864bc02e37575f577ee084713a8 # v3 with: - file: gen/Gen.dhall + file: src/package.dhall minify: true - name: Prepare changelog for release diff --git a/AGENTS.md b/AGENTS.md index f6d2533..7da3b7b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,6 +33,6 @@ already says. ## Dhall -`gen/` pins its remote imports by sha256 (`gen/Deps/*.dhall`). Bump those +`src/` pins its remote imports by sha256 (`src/Deps/*.dhall`). Bump those deliberately, one at a time, and re-run the harness before committing a pin change. diff --git a/DESIGN.md b/DESIGN.md index bff5a2c..79e8a7c 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -1,7 +1,7 @@ # python.gen DESIGN Status: current. This describes the generator as shipped, not a plan. The code -is the source of truth: the Dhall generator under `gen/`, the fixture project +is the source of truth: the Dhall generator under `src/`, the fixture project and golden output under `tests/`. When this doc and the tree disagree, the tree wins. @@ -378,34 +378,37 @@ overwritten on every run. Do not hand-edit it. ## 10. Generator decomposition -`gen/Gen.dhall` is the entry point handed to gen-sdk: +`src/package.dhall` is the entry point handed to gen-sdk: ```dhall let Sdk = ./Deps/Sdk.dhall -in Sdk ./Config.dhall ./compile.dhall +let Config = ./Config.dhall + +let interpret = ./Interpret.dhall + +in Sdk.Sigs.generator Config Config/default interpret ``` -The Sdk `module` function has signature `\(Config : Type) -> \(compile) -> -{ contractVersion, Config, compile, compileToFileMap }`, and -`compile : Optional Config -> Project -> Lude.Compiled.Type Lude.Files.Type`, -where `Files.Type = List { path : Text, content : Text }`. `compile.dhall` -folds the optional user config into the internal interpreter config and -calls `Interpreters/Project.dhall`, which traverses queries and custom types -and assembles the file list. +`Sdk.Sigs.generator` has signature `\(Config : Type) -> \(defaultConfig : Config) -> +\(interpret : Config -> Contract.Project -> Contract.Output) -> ...`; it curries +`interpret` against `defaultConfig` whenever the user config is absent and hands +the result to gen-contract's `Contract.module`. `Interpret.dhall` folds the +optional user config into the internal interpreter config and calls +`Interpreters/Project.dhall`, which traverses queries and custom types and +assembles the file list (`Contract.Output`). -`gen/` mirrors a typical pgn gen-sdk generator, Python-flavored. The -algebra/interpreter/template split keeps assembly separate from rendering. +`src/` mirrors a typical pgn gen-sdk generator, Python-flavored: `Interpreters/` +assembles data, `Templates/` renders it to Python text. The interpreter/template +algebra signatures themselves live in gen-sdk's `Sdk.Sigs` (`interpreter.dhall`/ +`template.dhall`), not a local `Algebras/` dir. ```text -gen/ - Gen.dhall # Sdk Config compile (entry handed to gen-sdk) +src/ + package.dhall # Sdk.Sigs.generator Config Config/default interpret (entry handed to gen-sdk) Config.dhall # user config TYPE: { packageName, emitSync, onUnsupported } - compile.dhall # derive interpreter Config from user Config, call Project.run - Deps/ # pinned remote imports (gen-sdk module + Project, lude, Prelude) - Algebras/ - Interpreter.dhall # Config + `module Input Output run` ; Run = Config -> Input -> Compiled Output - Template.dhall # `module Params run` ; Run = Params -> Text + Interpret.dhall # derive interpreter Config from user Config, call Project.run + Deps/ # pinned remote imports: gen-sdk, gen-contract, lude, dhall Prelude Structures/ Surface.dhall # async/sync token table (section 4) CustomKind.dhall # Lookup : Name -> < Enum | Composite | Absent > + composite fields @@ -426,6 +429,7 @@ gen/ Project.dhall # traverse queries+customTypes, assemble all files + facade + header, # apply the Skip filter (section 11) Templates/ + CoreModule.dhall # shared _core.py: JsonValue, NoRowError/DecodeError, require_array RuntimeModule.dhall # async + sync _runtime.py bodies RowsModule.dhall # shared _rows.py (Row dataclasses + decode fns) StatementModule.dhall # one per-surface statement wrapper @@ -629,11 +633,11 @@ SQL rendering are largely driver-agnostic. CI runs two independent jobs (`.github/workflows/ci.yml`): `harness` (the pytest suite against a live Postgres) and `contract` (compiles gen-sdk's -`Fixtures.Exhaustive` via `tests/Exhaustive.dhall` and runs basedpyright +`Fixtures.Exhaustive` via `demos/Exhaustive.dhall` and runs basedpyright strict on the result). The `contract` job needs `nikita-volkov/dhall-directory-tree.github-action`, a Docker action bundling a forked Dhall evaluator; the local `dhall` CLI most people have installed is the standard dhall-lang build and does not -understand `Text/equal`, so it cannot run `tests/Exhaustive.dhall` directly. +understand `Text/equal`, so it cannot run `demos/Exhaustive.dhall` directly. Reproduce the `contract` job locally with [`act`](https://github.com/nektos/act) (not installed in this environment; `act -j contract` pulls the same pinned Docker action and runs the job as GitHub would). diff --git a/README.md b/README.md index db3226f..9e9a17f 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ name: ```yaml artifacts: python: - gen: https://raw.githubusercontent.com/slavashvets/python.gen/master/gen/Gen.dhall + gen: https://raw.githubusercontent.com/slavashvets/python.gen/master/src/package.dhall config: packageName: my-db-client emitSync: true @@ -61,10 +61,10 @@ database. | form | example | works? | | --------------- | --------------------------------------- | ------------------------------------------ | -| plain http(s) | `https://.../python.gen/gen/Gen.dhall` | yes; pgn fetches it and every relative import over HTTP | -| relative path | `../path/to/python.gen/gen/Gen.dhall` | yes, if you keep a local checkout next to your project | -| absolute path | `/abs/path/to/python.gen/gen/Gen.dhall` | yes, but the resulting freeze key is machine-specific | -| `file://` URL | `file:///abs/.../Gen.dhall` | rejected; pgn's project schema does not accept `file://` | +| plain http(s) | `https://.../python.gen/src/package.dhall` | yes; pgn fetches it and every relative import over HTTP | +| relative path | `../path/to/python.gen/src/package.dhall` | yes, if you keep a local checkout next to your project | +| absolute path | `/abs/path/to/python.gen/src/package.dhall` | yes, but the resulting freeze key is machine-specific | +| `file://` URL | `file:///abs/.../package.dhall` | rejected; pgn's project schema does not accept `file://` | Whichever form you use, the freeze file that caches the resolved generator (section "Freeze lifecycle" below) keys on the literal `gen:` value and diff --git a/bench/as-source.sh b/bench/as-source.sh index 203b973..c16945b 100755 --- a/bench/as-source.sh +++ b/bench/as-source.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # Cold-cache benchmark of `pgn generate` on the fixture project, comparing the -# current gen (Deps imported `as Source`) against the same tree with the +# current src (Deps imported `as Source`) against the same tree with the # pre-as-Source Deps (plain pinned imports). Both variants run the same pgn # binary by default, so the measurement isolates the import mode itself. # @@ -24,7 +24,7 @@ trap 'rm -rf "$work"' EXIT prepare() { # $1 = variant root; mirrors the repo layout the fixture expects rm -rf "$1" && mkdir -p "$1/tests" - cp -R "$root/gen" "$1/gen" + cp -R "$root/src" "$1/src" cp -R "$root/tests/fixture-project" "$1/tests/fixture-project" rm -f "$1/tests/fixture-project/freeze1.pgn.yaml" rm -rf "$1/tests/fixture-project/artifacts" @@ -34,9 +34,23 @@ prepare() { # $1 = variant root; mirrors the repo layout the fixture expects # normalized-expression pins (an `as Source` pin hashes the import's source, # so the two modes need different sha256 values for the same version). strip_as_source() { # $1 = variant root - perl -i -ne 'print unless /^\s*as Source$/' "$1"/gen/Deps/*.dhall - perl -i -pe 's/8d43544ecb0e612406af3133bdbca51138c704a77a5a29ef62fe034d0e77a3a6/b9f7bb842345f3864c71e877fda4200306ba5c044a43e6f7713a23bc4769b91a/' "$1/gen/Deps/Sdk.dhall" - perl -i -pe 's/46b527b071eba96a17e76b4bc5774645714dd5b4355974d221e705aa7c126e77/14c43eec97972ae27afe3386ff937d04db66f84273d5551476361db12d2c4b50/' "$1/gen/Deps/Lude.dhall" + perl -i -ne 'print unless /^\s*as Source$/' "$1"/src/Deps/*.dhall + + # gen-sdk v2.0.0's src/package.dhall: `mise x -- dhall hash` against the + # live GitHub-hosted package (both `... as Source` and the plain import) + # returns the SAME sha256 (b9def6ab1179bc4aaae7fc6e91977f094f75934cd5755175c294a9e97ca71b15), + # matching the value already committed in src/Deps/Sdk.dhall -- so, unlike + # the old v0.11.0 pin this pair used to target (where the two genuinely + # differed: 8d43544e...->b9f7bb84...), no character swap is needed here + # after the strip above; the committed pin already equals the plain-import + # target. See docs/superpowers/plans/2026-07-11-gen-sdk-v2-migration.md + # Task 6 for how this was verified (dhall's local import cache made the + # lookup instant; a cold, uncached fetch of a *different* URL hung in this + # sandbox, so treat network reachability here as best-effort, not given). + + # lude v5.1.0 is unchanged by this migration (see src/Deps/Lude.dhall), so + # its existing as-Source -> plain-import hash swap below is still correct. + perl -i -pe 's/46b527b071eba96a17e76b4bc5774645714dd5b4355974d221e705aa7c126e77/14c43eec97972ae27afe3386ff937d04db66f84273d5551476361db12d2c4b50/' "$1/src/Deps/Lude.dhall" } measure() { # $1 = label, $2 = variant root, $3 = pgn binary diff --git a/bench/generate.sh b/bench/generate.sh index a2acd50..2769621 100755 --- a/bench/generate.sh +++ b/bench/generate.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Build the fixture client from the working-tree gen, in one of two variants: -# with - gen/Deps as committed (imports `as Source`) +# Build the fixture client from the working-tree src, in one of two variants: +# with - src/Deps as committed (imports `as Source`) # without - the pre-as-Source Deps (mode stripped, normalized-expression # pins restored; byte-identical to commit fb78869's Deps) # Output lands in ./demo-with-as-source or ./demo-without-as-source. @@ -15,15 +15,29 @@ url="${PGN_TEST_DATABASE_URL:-postgresql://postgres:postgres@localhost:5432/post out="$root/demo-$variant-as-source" rm -rf "$out" && mkdir -p "$out/tests" -cp -R "$root/gen" "$out/gen" +cp -R "$root/src" "$out/src" cp -R "$root/tests/fixture-project" "$out/tests/fixture-project" rm -f "$out/tests/fixture-project/freeze1.pgn.yaml" rm -rf "$out/tests/fixture-project/artifacts" if [ "$variant" = without ]; then - perl -i -ne 'print unless /^\s*as Source$/' "$out"/gen/Deps/*.dhall - perl -i -pe 's/8d43544ecb0e612406af3133bdbca51138c704a77a5a29ef62fe034d0e77a3a6/b9f7bb842345f3864c71e877fda4200306ba5c044a43e6f7713a23bc4769b91a/' "$out/gen/Deps/Sdk.dhall" - perl -i -pe 's/46b527b071eba96a17e76b4bc5774645714dd5b4355974d221e705aa7c126e77/14c43eec97972ae27afe3386ff937d04db66f84273d5551476361db12d2c4b50/' "$out/gen/Deps/Lude.dhall" + perl -i -ne 'print unless /^\s*as Source$/' "$out"/src/Deps/*.dhall + + # gen-sdk v2.0.0's src/package.dhall: `mise x -- dhall hash` against the + # live GitHub-hosted package (both `... as Source` and the plain import) + # returns the SAME sha256 (b9def6ab1179bc4aaae7fc6e91977f094f75934cd5755175c294a9e97ca71b15), + # matching the value already committed in src/Deps/Sdk.dhall -- so, unlike + # the old v0.11.0 pin this pair used to target (where the two genuinely + # differed: 8d43544e...->b9f7bb84...), no character swap is needed here + # after the strip above; the committed pin already equals the plain-import + # target. See docs/superpowers/plans/2026-07-11-gen-sdk-v2-migration.md + # Task 6 for how this was verified (dhall's local import cache made the + # lookup instant; a cold, uncached fetch of a *different* URL hung in this + # sandbox, so treat network reachability here as best-effort, not given). + + # lude v5.1.0 is unchanged by this migration (see src/Deps/Lude.dhall), so + # its existing as-Source -> plain-import hash swap below is still correct. + perl -i -pe 's/46b527b071eba96a17e76b4bc5774645714dd5b4355974d221e705aa7c126e77/14c43eec97972ae27afe3386ff937d04db66f84273d5551476361db12d2c4b50/' "$out/src/Deps/Lude.dhall" fi cd "$out/tests/fixture-project" diff --git a/mise.toml b/mise.toml index 040920b..68903bf 100644 --- a/mise.toml +++ b/mise.toml @@ -35,10 +35,10 @@ run = "bench/generate.sh without" # Regenerates only the "python" artifact: a full 7-artifact generate peaks at # ~31 GB RSS (memory goes to normalizing the generator closure per artifact, # see ci.yml), a single-artifact one at ~10 GB. The temp copy guarantees a -# fresh resolve of the working-tree gen/ (no stale freeze) and keeps pgn's +# fresh resolve of the working-tree src/ (no stale freeze) and keeps pgn's # scratch files out of the repo. [tasks.golden] -description = "Refresh tests/golden from the working-tree gen/ (single-artifact run)" +description = "Refresh tests/golden from the working-tree src/ (single-artifact run)" run = ''' #!/usr/bin/env bash set -euo pipefail @@ -48,14 +48,14 @@ trap 'rm -rf "$tmp"' EXIT cp -R "$root/tests/fixture-project/." "$tmp/fixture" rm -f "$tmp/fixture/freeze1.pgn.yaml" rm -rf "$tmp/fixture/artifacts" -python3 - "$tmp/fixture/project1.pgn.yaml" "$root/gen/Gen.dhall" <<'EOF' +python3 - "$tmp/fixture/project1.pgn.yaml" "$root/src/package.dhall" <<'EOF' import sys from pathlib import Path p = Path(sys.argv[1]) head, sep, _ = p.read_text().partition(" # The variants below") assert sep, "variants marker not found in project1.pgn.yaml" -_ = p.write_text(head.rstrip().replace("../../gen/Gen.dhall", sys.argv[2]) + "\n") +_ = p.write_text(head.rstrip().replace("../../src/package.dhall", sys.argv[2]) + "\n") EOF cd "$tmp/fixture" pgn --database-url "${PGN_TEST_DATABASE_URL:-postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable}" generate diff --git a/tests/fixture-project/project1.pgn.yaml b/tests/fixture-project/project1.pgn.yaml index f6bec82..ff75999 100644 --- a/tests/fixture-project/project1.pgn.yaml +++ b/tests/fixture-project/project1.pgn.yaml @@ -4,7 +4,7 @@ version: 0.0.0 postgres: 18 artifacts: python: - gen: ../../gen/Gen.dhall + gen: ../../src/package.dhall config: packageName: specimen-client emitSync: true @@ -12,25 +12,25 @@ artifacts: # Optional Config knobs (undocumented by pgn itself); see # tests/test_config_variants.py for the assertions. python-name-only: - gen: ../../gen/Gen.dhall + gen: ../../src/package.dhall config: packageName: name-only-client python-sync-only: - gen: ../../gen/Gen.dhall + gen: ../../src/package.dhall config: emitSync: true python-empty: - gen: ../../gen/Gen.dhall + gen: ../../src/package.dhall config: {} python-bare: - gen: ../../gen/Gen.dhall + gen: ../../src/package.dhall python-unknown-key: - gen: ../../gen/Gen.dhall + gen: ../../src/package.dhall config: packageName: unknown-key-client bogusField: 1 python-null: - gen: ../../gen/Gen.dhall + gen: ../../src/package.dhall config: packageName: null-client emitSync: null From a30ac3209dfc7556fe82bacb2b7ca7ac52d5f359 Mon Sep 17 00:00:00 2001 From: Nikita Volkov Date: Sat, 11 Jul 2026 10:05:34 +0300 Subject: [PATCH 5/9] fix(gen): update gen/ path references in the pytest harness to src/ tests/_harness.py's GEN_DIR (and its two consumers) still pointed the harness at the repo-root gen/ directory, which Task 5 deleted when it renamed gen/ to src/. Every test using the session-scoped generated_tree fixture, or building its own copytree in test_unsupported_types.py, would have hit FileNotFoundError on the very first pgn run. Renamed the constant to SRC_DIR (was misleading otherwise), pointed it at src/, matched the copied-tree destination directory name to what project1.pgn.yaml's `gen: ../../src/package.dhall` (fixed in the prior commit) actually resolves against, and fixed the two hardcoded yaml literals in test_unsupported_types.py plus conftest.py's docstring. Confirmed via `python3 -m py_compile` and a repo-wide grep for any remaining gen/-as-directory or GEN_DIR references in .py files (none). --- tests/_harness.py | 2 +- tests/conftest.py | 10 +++++----- tests/test_unsupported_types.py | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/_harness.py b/tests/_harness.py index 037ac41..9e5ffbd 100644 --- a/tests/_harness.py +++ b/tests/_harness.py @@ -23,7 +23,7 @@ DEFAULT_MAX_RSS_GB = 40.0 HERE = Path(__file__).resolve().parent -GEN_DIR = HERE.parent / "gen" +SRC_DIR = HERE.parent / "src" FIXTURE_PROJECT = HERE / "fixture-project" GOLDEN_DIR = HERE / "golden" diff --git a/tests/conftest.py b/tests/conftest.py index d0ccc23..5bac571 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -15,9 +15,9 @@ from tests._harness import ( FIXTURE_PROJECT, - GEN_DIR, GOLDEN_DIR, HERE, + SRC_DIR, admin_database_url, effective_database_name, run_pgn, @@ -67,15 +67,15 @@ def fixture_copy(tmp_path: Path) -> Path: def generated_tree(pgn_bin: str, pgn_admin_url: str, tmp_path_factory: pytest.TempPathFactory) -> Path: """Generate the fixture client once and return its artifacts/python dir. - The generator path in project1.pgn.yaml is `../../gen/Gen.dhall`, + The generator path in project1.pgn.yaml is `../../src/package.dhall`, relative to the fixture project. To keep it resolvable the copy mirrors the - real layout: `/gen` and `/tests/fixture-project`. The freeze + real layout: `/src` and `/tests/fixture-project`. The freeze file is dropped so pgn re-resolves the working-tree generator instead of a - cached hash (a stale freeze makes pgn ignore gen/ edits and silently + cached hash (a stale freeze makes pgn ignore src/ edits and silently emit the old output). """ root = tmp_path_factory.mktemp("pygen") - _ = shutil.copytree(GEN_DIR, root / "gen") + _ = shutil.copytree(SRC_DIR, root / "src") project = shutil.copytree(FIXTURE_PROJECT, root / "tests" / "fixture-project") (project / "freeze1.pgn.yaml").unlink(missing_ok=True) shutil.rmtree(project / "artifacts", ignore_errors=True) diff --git a/tests/test_unsupported_types.py b/tests/test_unsupported_types.py index 9d99eab..642db87 100644 --- a/tests/test_unsupported_types.py +++ b/tests/test_unsupported_types.py @@ -24,14 +24,14 @@ import pytest -from tests._harness import FIXTURE_PROJECT, GEN_DIR, HERE, run_pgn +from tests._harness import FIXTURE_PROJECT, HERE, SRC_DIR, run_pgn HARNESS_ROOT = HERE.parent def test_unsupported_pg_type_fails_loudly(pgn_bin: str, pgn_admin_url: str, tmp_path: Path) -> None: root = tmp_path / "pygen" - _ = shutil.copytree(GEN_DIR, root / "gen") + _ = shutil.copytree(SRC_DIR, root / "src") project = shutil.copytree(FIXTURE_PROJECT, root / "tests" / "fixture-project") (project / "freeze1.pgn.yaml").unlink(missing_ok=True) shutil.rmtree(project / "artifacts", ignore_errors=True) @@ -51,7 +51,7 @@ def test_unsupported_pg_type_fails_loudly(pgn_bin: str, pgn_admin_url: str, tmp_ def test_json_array_param_fails_loudly(pgn_bin: str, pgn_admin_url: str, tmp_path: Path) -> None: root = tmp_path / "pygen" - _ = shutil.copytree(GEN_DIR, root / "gen") + _ = shutil.copytree(SRC_DIR, root / "src") project = shutil.copytree(FIXTURE_PROJECT, root / "tests" / "fixture-project") (project / "freeze1.pgn.yaml").unlink(missing_ok=True) shutil.rmtree(project / "artifacts", ignore_errors=True) @@ -66,7 +66,7 @@ def test_json_array_param_fails_loudly(pgn_bin: str, pgn_admin_url: str, tmp_pat "postgres: 18\n" "artifacts:\n" " python:\n" - " gen: ../../gen/Gen.dhall\n" + " gen: ../../src/package.dhall\n" " config:\n" " onUnsupported: Fail\n" ) @@ -104,7 +104,7 @@ def test_skip_unsupported_drops_offending_units_and_cascades( golden package is held to. """ root = tmp_path / "pygen" - _ = shutil.copytree(GEN_DIR, root / "gen") + _ = shutil.copytree(SRC_DIR, root / "src") project = shutil.copytree(FIXTURE_PROJECT, root / "tests" / "fixture-project") (project / "freeze1.pgn.yaml").unlink(missing_ok=True) shutil.rmtree(project / "artifacts", ignore_errors=True) @@ -129,7 +129,7 @@ def test_skip_unsupported_drops_offending_units_and_cascades( "postgres: 18\n" "artifacts:\n" " python:\n" - " gen: ../../gen/Gen.dhall\n" + " gen: ../../src/package.dhall\n" " config:\n" " onUnsupported: Skip\n" ) From ffea69fe9ccccfafaef73b343fc9514cd6b21840 Mon Sep 17 00:00:00 2001 From: Nikita Volkov Date: Sat, 11 Jul 2026 14:19:59 +0300 Subject: [PATCH 6/9] docs(gen): add Task 7 CHANGELOG entry, fix stale Config.dhall comment Documents the gen-contract v4.0.1 / gen-sdk v2.0.0 migration and gen/->src/ layout restructure under # Upcoming, and updates Config.dhall's doc comment to reference Interpret.dhall instead of the old compile.dhall name. --- CHANGELOG.md | 12 ++++++++++++ src/Config.dhall | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cc7b2c..f98fe04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Upcoming +- Migrated the generator's internal dependencies to `gen-contract` v4.0.1 + and `gen-sdk` v2.0.0, adopting `Sdk.Sigs` in place of the local + `Algebras/` module, and restructured the repository layout to match the + pGenie generator architecture: implementation moved from `gen/` to + `src/`, the public entry point renamed from `gen/Gen.dhall` to + `src/package.dhall`, and the fixture driver moved from + `tests/Exhaustive.dhall` to `demos/Exhaustive.dhall`. No change to + generated output or the public Dhall interface (`artifacts..gen` + URLs pointing at a previously-released `resolved.dhall` are unaffected; + only the next release's URL path changes, from `.../gen/Gen.dhall` — the + unresolved source path some projects may reference directly instead of a + frozen release — to `.../src/package.dhall`). - The test harness now runs every pgn subprocess in its own process group under an RSS watchdog: a thread polls `ps -o rss=` every 2 s and, on breach of `PGN_MAX_RSS_GB` (default 40 GB), kills the whole group and fails the test with diff --git a/src/Config.dhall b/src/Config.dhall index 7ec1fe6..1377127 100644 --- a/src/Config.dhall +++ b/src/Config.dhall @@ -5,7 +5,7 @@ -- unsupported statement/type and its dependents, with a warning) when a query -- or custom type hits a PG shape the generator cannot render; see -- Structures/OnUnsupported.dhall. All fields are Optional so a project may omit --- the whole config block or any subset of its keys; compile.dhall supplies the +-- the whole config block or any subset of its keys; Interpret.dhall supplies the -- defaults. let OnUnsupported = ./Structures/OnUnsupported.dhall From 9c33a23dbbec070c530430900ea7f770054a4fcd Mon Sep 17 00:00:00 2001 From: Nikita Volkov Date: Sat, 11 Jul 2026 14:29:51 +0300 Subject: [PATCH 7/9] refactor(gen): drop dead Run type-alias bindings in Query/Result/ResultColumns These were declared but never exported (unlike Member.dhall/ParamsMember.dhall, which do export Run), left over from documenting each file's actual multi-arg call signature during the gen-sdk v2 migration. Flagged by final review as harmless but pointless; removing rather than exporting keeps these three files' tails exactly as they were pre-migration, matching the migration's "keep bare exports verbatim" instruction for this exception group. --- src/Interpreters/Query.dhall | 2 -- src/Interpreters/Result.dhall | 3 --- src/Interpreters/ResultColumns.dhall | 3 --- 3 files changed, 8 deletions(-) diff --git a/src/Interpreters/Query.dhall b/src/Interpreters/Query.dhall index daa1d47..cce3fcd 100644 --- a/src/Interpreters/Query.dhall +++ b/src/Interpreters/Query.dhall @@ -177,6 +177,4 @@ let run = ) ) -let Run = Config -> CustomKind.Lookup -> Input -> Lude.Compiled.Type Output - in { Input, Output, run } diff --git a/src/Interpreters/Result.dhall b/src/Interpreters/Result.dhall index 83f3ef1..5d652a0 100644 --- a/src/Interpreters/Result.dhall +++ b/src/Interpreters/Result.dhall @@ -101,7 +101,4 @@ let run = } input -let Run = - Config -> CustomKind.Lookup -> Text -> Input -> Lude.Compiled.Type Output - in { Input, Output, RowClass, run } diff --git a/src/Interpreters/ResultColumns.dhall b/src/Interpreters/ResultColumns.dhall index b2fc737..e0bbfaa 100644 --- a/src/Interpreters/ResultColumns.dhall +++ b/src/Interpreters/ResultColumns.dhall @@ -78,7 +78,4 @@ let run = input ) -let Run = - Config -> CustomKind.Lookup -> Text -> Input -> Lude.Compiled.Type Output - in { Input, Output, run } From 15c28361de3192aa2d0e896fa32076c5f841ece3 Mon Sep 17 00:00:00 2001 From: Nikita Volkov Date: Sat, 11 Jul 2026 16:30:39 +0300 Subject: [PATCH 8/9] The plan --- .../plans/2026-07-11-gen-sdk-v2-migration.md | 666 ++++++++++++++++++ 1 file changed, 666 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-11-gen-sdk-v2-migration.md diff --git a/docs/superpowers/plans/2026-07-11-gen-sdk-v2-migration.md b/docs/superpowers/plans/2026-07-11-gen-sdk-v2-migration.md new file mode 100644 index 0000000..5e0499c --- /dev/null +++ b/docs/superpowers/plans/2026-07-11-gen-sdk-v2-migration.md @@ -0,0 +1,666 @@ +# python.gen: migrate to gen-contract v4.0.1 / gen-sdk v2.0.0, adopt architecture layout + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Bring `python.gen` up to the same structural shape as `java.gen`'s +last release (`v1.1.0`): pin `gen-contract`/`gen-sdk` directly (no bundled +SDK), adopt `Sdk.Sigs` in place of the local `Algebras/` folder, and move to +the `src/`-rooted repo layout the normative architecture doc describes — with +**zero change to generated output**. + +**Architecture:** Reference is +`gen-sdk/docs/generator-architecture.md` (already read in full) and +`java.gen`'s current `master` (tag `v1.1.0`, commit `658508c`). `python.gen` +is currently on pre-split `gen-sdk v0.11.0` (no `Sigs`, bundles `Project` +itself) with a hand-rolled `gen/Algebras/{Interpreter,Template}.dhall`, a +`gen/Deps/package.dhall` barrel, and the old `gen/` + `tests/Exhaustive.dhall` +layout — i.e. it has never done *any* of the steps `java.gen` went through +(`gen-migration-plan.md` at the pgenie repo root covers `rust.gen`/`haskell.gen`, +which already had `Sigs`; it does not cover `python.gen` at all). This plan +folds all of `java.gen`'s historical steps into one destination state, since +there is no reason to recreate `java.gen`'s intermediate commits. + +**Tech Stack:** Dhall (fork `pgn`'s dhall, package name `dhll`, needed for the +`Text/equal` builtin and `as Source` import mode — see Global Constraints), +Python 3.12 / `uv` / `pytest` for the harness, `mise` for tool pinning. + +## Global Constraints + +- **No behavior change.** This is a dependency/layout migration, not a + feature change. If `demos/Exhaustive.dhall` (see Task 5) produces different + file paths or content than `tests/Exhaustive.dhall` did before the move, + that is a bug in the migration, not an expected diff. +- **Do not touch interpreter algorithms.** `Interpreters/Project.dhall`'s + `Skip`/`Fail` logic (`typeSucceeds`/`queryChecks`/`effectiveQueries`) is + deliberately hand-rolled instead of using `Typeclasses.Classes.Alternative` + the way `java.gen` does, per an explicit in-file comment: multiple + `QueryGen.run`/`CustomTypeGen.run` call sites for the same query measurably + multiplied Dhall normalization time (seconds → minutes), confirmed by wall-time + bisection. This is orthogonal to the Deps/Sigs migration — leave it as is. + Do **not** add `src/Deps/Typeclasses.dhall` since nothing will use it. +- **`Interpreters/Member.dhall` and `Interpreters/ParamsMember.dhall` keep + their 3-argument `Run` type** (`Config -> CustomKind.Lookup -> Input -> + Compiled Output`) instead of conforming to `Sdk.Sigs.interpreter`'s fixed + 2-argument shape (`Config -> Input -> Result`). `java.gen`'s own + `Member.dhall` doesn't need a lookup table; `python.gen`'s does (custom-type + name resolution — see the `buildLookup`/`IndexedCustomType` comments in + `Interpreters/Project.dhall`). Bundling `Lookup` into `Config` or `Input` + to force-fit the Sig is a bigger, separate refactor — out of scope here. + These two files get the Deps-import and `Algebra.Config` → local `Config` + changes (Task 2) but keep their existing bare `{ Input, Output, Run, run }` + export, not `Sdk.Sigs.interpreter Config Input Output run`. +- **Config narrowing is deferred.** The architecture doc's ideal is each + interpreter declaring only the `Config` fields it (and its children) needs. + `java.gen` does this trivially (`{ useOptional : Bool }` everywhere, since + that's its whole config). `python.gen`'s config has 4 fields + (`packageName`, `importName`, `emitSync`, `onUnsupported`); auditing exactly + which fields each of the 11 interpreters actually reads and narrowing each + is real, separate work with real risk of missing a field some deeply nested + path needs. This plan keeps the **same 4-field `Config` record, declared + locally and verbatim in each interpreter module** (no more shared + `Algebra.Config` alias) — this satisfies "`Config` is a parameter each + module declares itself," just not narrowed. Note it as a follow-up, don't + do it now. +- **Local verification gap.** This sandbox has a global `dhall`/`pgn` (cabal + build, `dhll-1.42.3`) that is **not** the `mise`-pinned `pgn v0.9.1` this + repo's CI/tests actually use — a plain `dhall type --file tests/Exhaustive.dhall` + here fails on the *pre-migration* tree already, with an `as Source` + hash-integrity mismatch on `gen/Deps/Sdk.dhall` (confirmed during planning: + expected `8d43544e...`, actual `573b4655...`). This is a toolchain mismatch, + not evidence of a real problem. **Whoever executes this plan must run + verification through `mise x -- dhall ...` / `mise x -- pgn ...` / `mise x + -- uv run pytest`**, matching `mise.toml`'s pin, not a bare global `dhall`. + If `mise` isn't available in the execution environment either, at minimum + run `dhall format --transitive` (syntax-only, tool-version-agnostic) and + flag that deeper verification (`dhall type`, fixture diff, pytest, + basedpyright) still needs to happen on a properly provisioned machine/CI + before this is considered done. + +--- + +## File Structure + +``` +src/ (was gen/) + package.dhall (was Gen.dhall — now built via Sdk.Sigs.generator) + Config.dhall (unchanged content, moved) + Interpret.dhall (was compile.dhall — Config no longer Optional at top) + Deps/ + Contract.dhall (NEW — gen-contract v4.0.1 pin) + Sdk.dhall (bumped gen-sdk v0.11.0 → v2.0.0) + Lude.dhall (unchanged content, moved) + Prelude.dhall (unchanged content, moved) + (package.dhall barrel REMOVED) + Interpreters/ (11 files: Deps.Sdk.Project → Deps.Contract, Algebra → Sdk.Sigs) + Templates/ (10 files: Algebra → Sdk.Sigs; 7 also de-barrel Deps) + Structures/ (CustomKind.dhall: Deps.Sdk.Project → Deps.Contract; others untouched) + (Algebras/ REMOVED) +demos/ + Exhaustive.dhall (was tests/Exhaustive.dhall — rewritten for Sdk.Output.toFileMap) +tests/ (Python pytest harness — unchanged except any gen/-path references) +.github/workflows/{ci,release}.yml, .github/scripts/build-contract-shell.sh, +README.md, AGENTS.md, DESIGN.md, build.bash, bench/*.sh, mise.toml + (path references updated: gen/ → src/, tests/Exhaustive.dhall → demos/Exhaustive.dhall) +``` + +--- + +### Task 1: Add the new Deps pins and remove the `Deps/package.dhall` barrel + +**Files:** +- Create: `gen/Deps/Contract.dhall` +- Modify: `gen/Deps/Sdk.dhall` +- Delete: `gen/Deps/package.dhall` +- Modify (de-barrel): every file that currently has `let Deps = ../Deps/package.dhall` (see the full list in Tasks 2–3 — do this as part of those tasks, not twice) + +This task only stages the new pins; Task 2 is where the fallout (broken +`Sdk.Project`/`Sdk.Fixtures` references, `Algebras/` removal) gets fixed. Do +not try to get `dhall type` green after this task alone — it won't be, and +that's expected (same as `gen-migration-plan.md`'s phase-0 commit 2 for +`java.gen`). Fold Task 1 and Task 2 into one commit if you'd rather not carry +a known-broken intermediate state. + +- [ ] **Step 1: Create `gen/Deps/Contract.dhall`** — the exact pin `java.gen` + and `gen-sdk` itself use: + +```dhall +https://raw.githubusercontent.com/pgenie-io/gen-contract/v4.0.1/src/package.dhall + sha256:4a130ba7fbaa152a776babbb1bf2994a4833931ca76bde9bf6930d354225651e +``` + +- [ ] **Step 2: Bump `gen/Deps/Sdk.dhall`** to `gen-sdk v2.0.0` (same pin + `java.gen`'s `src/Deps/Sdk.dhall` uses). Preserve the existing `as Source` + import mode (see `AGENTS.md`/CI comments on why `python.gen` uses it — + `java.gen` doesn't, but that's an intentional `python.gen`-specific RAM + optimization, not something this migration should undo): + +```dhall +https://raw.githubusercontent.com/pgenie-io/gen-sdk/v2.0.0/src/package.dhall + sha256:b9def6ab1179bc4aaae7fc6e91977f094f75934cd5755175c294a9e97ca71b15 + as Source +``` + + If keeping `as Source` here, its hash is a *source*-text hash, not the + semantic hash above copied from `java.gen` (which imports plainly). Verify + with `dhall hash` against the raw URL using the repo's pinned toolchain + (`mise x -- dhall hash <<< 'https://raw.githubusercontent.com/pgenie-io/gen-sdk/v2.0.0/src/package.dhall as Source'` + or equivalent) before trusting the semantic hash verbatim in `as Source` + mode — don't guess. + +- [ ] **Step 3: Delete `gen/Deps/package.dhall`.** + +- [ ] **Step 4: Commit** (or fold into Task 2's commit). + +--- + +### Task 2: Rewire `Structures/CustomKind.dhall` and all 11 `Interpreters/*.dhall` + +**Files:** +- Modify: `gen/Structures/CustomKind.dhall` +- Modify: `gen/Interpreters/{CustomType,Member,ParamsMember,Primitive,Project,Query,QueryFragments,Result,ResultColumns,Scalar,Value}.dhall` +- Delete: `gen/Algebras/` (all three files) + +**The mechanical recipe, applied to every file above:** + +1. Replace `let Deps = ../Deps/package.dhall` with direct imports of exactly + what the file uses. Every one of these files uses `Deps.Sdk.Project` + (→ becomes a direct `Deps/Contract.dhall` import) and `Deps.Lude`/`Deps.Prelude` + (→ direct imports). Concretely, replace: + ```dhall + let Deps = ../Deps/package.dhall + ``` + with (only the lines this particular file actually needs — check with + `grep -n 'Deps\.' ` first): + ```dhall + let Lude = ../Deps/Lude.dhall + + let Prelude = ../Deps/Prelude.dhall + + let Model = ../Deps/Contract.dhall + ``` + and change every remaining `Deps.Lude` → `Lude`, `Deps.Prelude` → `Prelude` + in the body. `QueryFragments.dhall` additionally has an unused + `let Sdk = Deps.Sdk` line (line 7) — drop it, nothing in the file + references the `Sdk` binding. + +2. Replace the line `let Model = Deps.Sdk.Project` (now redundant with step + 1's `Model` binding) — don't duplicate it, step 1 already introduces + `Model` pointed at `Deps/Contract.dhall`. + +3. Delete `let Algebra = ../Algebras/Interpreter.dhall`. + +4. Add a local `Config` type declaration (same 4 fields everywhere per the + Global Constraints note on deferred narrowing): + ```dhall + let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } + ``` + This needs `OnUnsupported = ../Structures/OnUnsupported.dhall` imported in + any file that doesn't already import it (check first — `Project.dhall` + already does). + +5. Change every `\(config : Algebra.Config) ->` to `\(config : Config) ->`. + +6. Change the tail: + - **10 of the 11 files** (`CustomType`, `Primitive`, `Project`, `Query`, + `QueryFragments`, `Result`, `ResultColumns`, `Scalar`, `Value` — 9 files, + not 10; `Member`/`ParamsMember` are the exception below) end with + `Algebra.module Input Output run` (or, for `Scalar.dhall`, + `Algebra.module Input Output run /\ { ScalarDecode }`). Change to: + ```dhall + Sdk.Sigs.interpreter Config Input Output run + ``` + (`Scalar.dhall`: `Sdk.Sigs.interpreter Config Input Output run /\ { ScalarDecode }`), + which needs `let Sdk = ../Deps/Sdk.dhall` imported (it isn't currently, + since `Deps.Sdk.Project` used to come through the barrel — add it). + - **`Member.dhall` and `ParamsMember.dhall`** keep their existing tail + verbatim: `in { Input, Output, Run, run }`, just with `Algebra.Config` → + `Config` in the `Run` type alias line + (`let Run = Config -> CustomKind.Lookup -> Input -> Lude.Compiled.Type Output`). + No `Sdk.Sigs.interpreter` here — see Global Constraints. + +7. `Interpreters/Project.dhall` specifically: its two `\(config : Algebra.Config) ->` + occurrences (the `combineOutputs` and `run` functions) both become + `\(config : Config) ->`; its `lookupConfig : Algebra.Config` type + annotation (used to type-check `Value.run` calls for composite-field + rendering) becomes `lookupConfig : Config`. No other logic in this file + changes — the `Skip`/`Fail` machinery is untouched per Global Constraints. + +- [ ] **Step 1: Apply the recipe to all 11 `Interpreters/*.dhall` files and `Structures/CustomKind.dhall`.** +- [ ] **Step 2: Delete `gen/Algebras/`.** +- [ ] **Step 3: Verify** (with the `mise`-pinned toolchain, not the bare local `dhall` — see Global Constraints): + ```bash + mise x -- dhall type --file gen/Interpreters/Project.dhall + ``` + Expected: prints the interpreter's type (a record with `Input`, `Output`, + `Result`, `Run`, `run` fields) with no error. This alone pulls in every + other `Interpreters/*.dhall` transitively, so it's a full check of this task. +- [ ] **Step 4: Commit.** + +--- + +### Task 3: Rewire the 10 `Templates/*.dhall` files + +**Files:** +- Modify: `gen/Templates/{CompositeModule,CoreModule,EnumModule,FacadeModule,InitModule,RegisterModule,RowsModule,RuntimeModule,StatementModule,TypesInit}.dhall` + +**Recipe:** + +1. `CoreModule.dhall`, `InitModule.dhall`, `RuntimeModule.dhall` don't import + `Deps` at all (no `Prelude`/`Lude` need) — only change: drop + `let Algebra = ../Algebras/Template.dhall`, add `let Sdk = ../Deps/Sdk.dhall`, + and change the tail. `CoreModule`/`RuntimeModule` end with + `Algebra.module {} (\(_ : {}) -> content)` → `Sdk.Sigs.template {} (\(_ : {}) -> content)`. + `InitModule` ends with `Algebra.module Params render` → + `Sdk.Sigs.template Params render`. + +2. The other 7 (`CompositeModule`, `EnumModule`, `FacadeModule`, + `RegisterModule`, `RowsModule`, `StatementModule`, `TypesInit`) currently + have `let Deps = ../Deps/package.dhall`, and only ever use + `Deps.Prelude.*` (all seven) and, additionally, `Deps.Lude.Text.indentNonEmpty` + (`RowsModule`, `StatementModule` only — confirmed by + `grep -n 'Deps\.' gen/Templates/*.dhall` during planning). Replace the + barrel import with: + ```dhall + let Prelude = ../Deps/Prelude.dhall + ``` + adding `let Lude = ../Deps/Lude.dhall` only in `RowsModule.dhall` and + `StatementModule.dhall`. Then replace `Deps.Prelude.` → `Prelude.` and + `Deps.Lude.` → `Lude.` throughout each file's body. Drop + `let Algebra = ../Algebras/Template.dhall`, add `let Sdk = ../Deps/Sdk.dhall`. + +3. Tails for these 7: `Algebra.module Params run` → + `Sdk.Sigs.template Params run` (`FacadeModule`, `RegisterModule`, + `StatementModule`); with a combined record for the other 4: + `Algebra.module Params run /\ { Field }` (`CompositeModule`) → + `Sdk.Sigs.template Params run /\ { Field }`; `/\ { Variant }` (`EnumModule`); + `/\ { StatementExport, TypeExport }` (`FacadeModule` — check which of + `FacadeModule`/others actually has this combinator vs a plain + `Algebra.module Params run`, per the earlier grep output, before editing — + don't assume, re-`grep -n 'Algebra.module' gen/Templates/*.dhall` and + match each file's exact current tail); `/\ { RowDef }` (`RowsModule`); + `/\ { Export }` (`TypesInit`). + +- [ ] **Step 1: Apply the recipe to all 10 files.** +- [ ] **Step 2: Verify:** + ```bash + mise x -- dhall type --file gen/Interpreters/Project.dhall + ``` + (Templates are only reachable transitively through `Interpreters/Project.dhall` + and its children, same as Task 2 — this single check covers both tasks once + both are done. If running Task 2 and 3 as separate commits, this step will + fail after Task 2 alone if any interpreter references a not-yet-updated + template's old shape; if so, do Tasks 2 and 3 as one commit instead.) +- [ ] **Step 3: Commit.** + +--- + +### Task 4: Rewrite the root entry point (`Config.dhall`, `compile.dhall` → `Interpret.dhall`, `Gen.dhall` → `package.dhall`) + +**Files:** +- Modify (move, content unchanged): `gen/Config.dhall` +- Modify (move + rewrite): `gen/compile.dhall` → `gen/Interpret.dhall` +- Modify (move + rewrite): `gen/Gen.dhall` → `gen/package.dhall` + +(Paths shown as `gen/...` here since Task 5 does the `gen/` → `src/` directory +move; do this task first, in place, then Task 5 is a pure `git mv` sweep with +no further content changes.) + +**Interfaces:** +- Consumes: `Interpreters/Project.dhall`'s `run` (produced by Task 2, + now `Sdk.Sigs.interpreter`-shaped: `.run : Config -> Contract.Project -> Compiled Output`). +- Produces: `package.dhall`'s `Sdk.Sigs.generator`-built value + (`{ contractVersion, Config, compile }`), consumed by Task 5's + `demos/Exhaustive.dhall` and by any pGenie project's `artifacts..gen` URL. + +`Sdk.Sigs.generator`'s shape (from the architecture doc): +```dhall +\(Config : Type) -> +\(defaultConfig : Config) -> +\(interpret : Config -> Contract.Project -> Contract.Output) -> + let compile = \(config : Optional Config) -> + merge { None = interpret defaultConfig, Some = interpret } config + in Contract.module Config compile +``` +Note `interpret` takes a **bare** `Config`, not `Optional Config` — the outer +"config block omitted entirely" case is handled once, by substituting +`defaultConfig`, not by `interpret` itself. `python.gen`'s current +`compile.dhall` handles *two* levels of optionality (the whole block, and +each field within it) with a doubled `Prelude.Optional.fold`. Only the outer +level goes away; each field inside `Config` stays individually `Optional` (so +a project can supply `emitSync: true` alone and still get default +`packageName`/`onUnsupported`) — that per-field defaulting is +`python.gen`-specific richness `java.gen` doesn't have (its `Config` has one +non-Optional `Bool` field), and this migration must not lose it. + +- [ ] **Step 1: `gen/Config.dhall`** — content unchanged, just confirm it + still reads (no edits needed here; listed for completeness since Task 5 + moves the file). + +- [ ] **Step 2: Rewrite `gen/compile.dhall` as `gen/Interpret.dhall`** — + drop the outer `Optional Config` unwrap (the two outermost + `Prelude.Optional.fold Config config Text (\(c : Config) -> ...)` / + `... Bool ...` / `... OnUnsupported.Mode ...` wrappers), keep everything + else (the per-field defaults, `importName` derivation) as is: + +```dhall +let Deps = ./Deps/package.dhall + +-- NOTE: Task 5 changes this to ./Deps/Contract.dhall / ./Deps/Prelude.dhall +-- directly once the Deps barrel is gone (Task 1) — write it that way now, +-- don't reintroduce the barrel: +let Contract = ./Deps/Contract.dhall + +let Prelude = ./Deps/Prelude.dhall + +let Config = ./Config.dhall + +let OnUnsupported = ./Structures/OnUnsupported.dhall + +let ProjectInterpreter = ./Interpreters/Project.dhall + +-- Entry point handed to gen-sdk's Sdk.Sigs.generator as `interpret`. Each +-- field of Config is independently Optional, so a project may omit the +-- whole config block (Sdk.Sigs.generator substitutes an all-None +-- defaultConfig, see package.dhall) or any subset of its keys; `defaults` +-- collects every fallback in one place (packageName from the project name in +-- kebab case, emitSync off, onUnsupported Fail). The async surface is always +-- emitted; emitSync adds the sync mirror. +in \(config : Config) -> + \(project : Contract.Project) -> + let defaults = + { packageName = project.name.inKebabCase + , emitSync = False + , onUnsupported = OnUnsupported.Mode.Fail + } + + let packageName = + Prelude.Optional.fold + Text + config.packageName + Text + (\(t : Text) -> t) + defaults.packageName + + let emitSync = + Prelude.Optional.fold + Bool + config.emitSync + Bool + (\(b : Bool) -> b) + defaults.emitSync + + let onUnsupported = + Prelude.Optional.fold + OnUnsupported.Mode + config.onUnsupported + OnUnsupported.Mode + (\(m : OnUnsupported.Mode) -> m) + defaults.onUnsupported + + let importName = Prelude.Text.replace "-" "_" packageName + + let interpreterConfig = { packageName, importName, emitSync, onUnsupported } + + in ProjectInterpreter.run interpreterConfig project +``` + +- [ ] **Step 3: Rewrite `gen/Gen.dhall` as `gen/package.dhall`:** + +```dhall +let Sdk = ./Deps/Sdk.dhall + +let OnUnsupported = ./Structures/OnUnsupported.dhall + +let Config = ./Config.dhall + +let Config/default + : Config + = { packageName = None Text + , emitSync = None Bool + , onUnsupported = None OnUnsupported.Mode + } + +let interpret = ./Interpret.dhall + +in Sdk.Sigs.generator Config Config/default interpret +``` + +- [ ] **Step 4: Verify:** + ```bash + mise x -- dhall type --file gen/package.dhall + ``` + Expected type: a record with `contractVersion`, `Config`, `compile` fields + (`compile : Optional Config -> Contract.Project -> Contract.Output`). +- [ ] **Step 5: Commit.** + +--- + +### Task 5: Move `gen/` → `src/`, `tests/Exhaustive.dhall` → `demos/Exhaustive.dhall` + +**Files:** +- Move: `gen/` → `src/` (whole tree, `git mv`) +- Move + rewrite: `tests/Exhaustive.dhall` → `demos/Exhaustive.dhall` + +- [ ] **Step 1:** + ```bash + git mv gen src + mkdir -p demos + git mv tests/Exhaustive.dhall demos/Exhaustive.dhall + ``` + All the `../Deps/...`, `./Interpreters/...`, `../Templates/...` style + relative imports inside `src/` are untouched by this move (they're relative + to their own file, not to the repo root), so no content changes are needed + inside `src/` itself from the move alone. + +- [ ] **Step 2: Rewrite `demos/Exhaustive.dhall`.** Its old body called + `Gen.compileToFileMap config project` — `Sdk.Sigs.generator`-built modules + don't have a `compileToFileMap` field (per the architecture doc: "there is + no `compileToFileMap` on the module — turning an `Output` into files is the + caller's job, via `Sdk.Output.toFileMap`"). New content: + +```dhall +-- Applies this generator to gen-sdk's shared cross-backend fixture project +-- (the same "music_catalogue" project java.gen's own demos/Exhaustive.dhall +-- exercises), so a Python client compiles from it and passes basedpyright +-- strict. Pinned directly at gen-sdk's package.dhall, separately from +-- src/Deps/Sdk.dhall: that file only imports gen-sdk's `package.dhall` `as +-- Source` for RAM, and this fixture load doesn't need that mode. +-- +-- The fixture project deliberately covers PG types this generator does not +-- support (box, inet, money, ranges, ...), so onUnsupported is set to Skip: +-- those statements/types are dropped with a warning instead of aborting the +-- whole compile. +-- +-- Intended to be executed with: +-- +-- ```bash +-- dhall to-directory-tree --file demos/Exhaustive.dhall --output --allow-path-separators +-- ``` +let Sdk = ../src/Deps/Sdk.dhall + +let Gen = ../src/package.dhall + +let OnUnsupported = ../src/Structures/OnUnsupported.dhall + +let project = Sdk.Fixtures.Exhaustive + +let config = + Some + { packageName = None Text + , emitSync = Some True + , onUnsupported = Some OnUnsupported.Mode.Skip + } + +in Sdk.Output.toFileMap (Gen.compile config project) +``` + +- [ ] **Step 3: Verify:** + ```bash + mise x -- dhall type --file demos/Exhaustive.dhall + ``` + Expected: `List { mapKey : Text, mapValue : Text }` (or however this + fork/version of Dhall renders `Prelude.Map.Type Text Text`), no error. +- [ ] **Step 4: Commit.** + +--- + +### Task 6: Update every external reference to the old paths + +**Files:** +- Modify: `.github/workflows/ci.yml` +- Modify: `.github/workflows/release.yml` +- Modify: `.github/scripts/build-contract-shell.sh` +- Modify: `README.md` +- Modify: `AGENTS.md` +- Modify: `DESIGN.md` +- Modify: `build.bash` +- Modify: `bench/generate.sh`, `bench/as-source.sh` +- Modify: `mise.toml` (the `golden` task) + +**Path substitutions to apply everywhere they occur** (verify each hit with +`grep -rn` first — don't blind-sed across the whole repo, `tests/golden/` +contains generated Python that must NOT be touched): + +| Old | New | +|---|---| +| `gen/Gen.dhall` | `src/package.dhall` | +| `gen/Deps/*.dhall` | `src/Deps/*.dhall` | +| `tests/Exhaustive.dhall` | `demos/Exhaustive.dhall` | +| `gen/` (prose/dir references) | `src/` | + +Specific known hits (from `grep -rn "gen/Gen\.dhall\|gen/Deps\|tests/Exhaustive"` +run during planning): + +- `.github/workflows/ci.yml`: the `contract` job's "Strip `as Source`..." + step does `sed -i ... gen/Deps/*.dhall` → `src/Deps/*.dhall`; the + "Generate output from Dhall" step's `dhall_file: tests/Exhaustive.dhall` → + `demos/Exhaustive.dhall`. +- `.github/workflows/release.yml`: the "Resolve Dhall" step's + `file: gen/Gen.dhall` → `file: src/package.dhall`. +- `.github/scripts/build-contract-shell.sh`: comment references + `tests/Exhaustive.dhall` → `demos/Exhaustive.dhall` (comment only, verify + no functional path argument needs changing — it's invoked with + `contract-output` as a positional arg per `ci.yml`, not a hardcoded path). +- `README.md`: line ~38 `gen: https://raw.githubusercontent.com/slavashvets/python.gen/master/gen/Gen.dhall` + → `.../src/package.dhall`; lines ~64-66, the three example URLs + (`.../gen/Gen.dhall`) → `.../src/package.dhall`. +- `AGENTS.md`: line ~36 "`gen/` pins its remote imports by sha256 + (`gen/Deps/*.dhall`)" → "`src/` pins its remote imports by sha256 + (`src/Deps/*.dhall`)". +- `DESIGN.md`: line 4 (`gen/`), line 381 (`gen/Gen.dhall` "is the entry point + handed to gen-sdk"), line 397 (`gen/` mirrors...), line 401 (the `gen/` + tree diagram — replace with the new `src/` tree, matching Task 5's actual + post-move layout), lines 632/636 (`tests/Exhaustive.dhall` → `demos/Exhaustive.dhall`). +- `build.bash`: this is a scratch/dev script (mostly commented-out lines) — + update the live lines: `target=tests/Exhaustive.dhall` → + `target=demos/Exhaustive.dhall`; the commented `# target=gen/Gen.dhall` and + `# dhall freeze gen/Deps/*.dhall` lines → `src/` equivalents (keep them + commented, just fix the paths so they're not stale if uncommented later). +- `bench/generate.sh`, `bench/as-source.sh`: both `cp -R "$root/gen" "$out/gen"` / + `"$1/gen"` → `"$root/src" "$out/src"` (and update the `gen/Deps/*.dhall` + perl substitutions to `src/Deps/*.dhall`). **The `as Source` → plain-import + sha256 substitutions in both scripts' `strip_as_source` are pinned to the + *old* `gen-sdk v0.11.0`/`lude v5.1.0` source-vs-normalized hash pairs** + (`8d43544e...`→`b9f7bb84...` for Sdk, `46b527b0...`→`14c43eec...` for Lude). + Since `Task 1` bumps `gen-sdk` to `v2.0.0`, these substitution pairs are now + wrong and must be recomputed for the new pin using the repo's actual pinned + toolchain (`mise x -- dhall hash` on the plain, non-`as-Source` import) — + don't guess these; if the recompute can't happen in this pass, leave a + `# TODO` in the script rather than shipping a silently-wrong benchmark. +- `mise.toml`'s `golden` task: the `python3 - ... "$root/gen/Gen.dhall"` arg + and the fixture-project string replace target `"../../gen/Gen.dhall"` → + `"../../src/package.dhall"`. + +- [ ] **Step 1: Apply all substitutions above.** +- [ ] **Step 2: Confirm no stragglers:** + ```bash + grep -rn "gen/Gen\.dhall\|gen/Deps\|gen/Interpreters\|gen/Templates\|gen/Structures\|gen/Config\.dhall\|gen/compile\.dhall\|tests/Exhaustive" \ + --include="*.md" --include="*.yml" --include="*.yaml" --include="*.toml" --include="*.sh" --include="*.bash" . + ``` + Expected: no output (everything left under `tests/golden/` or `tests/fixture-project/` + that isn't a generator-path reference is fine and out of scope — check any + hit manually rather than assuming). +- [ ] **Step 3: Commit.** + +--- + +### Task 7: Format, verify end-to-end, update CHANGELOG + +- [ ] **Step 1: Format everything:** + ```bash + mise x -- dhall format --transitive src/package.dhall + mise x -- dhall format --transitive demos/Exhaustive.dhall + ``` + +- [ ] **Step 2: Full type-check:** + ```bash + mise x -- dhall type --file src/package.dhall + mise x -- dhall type --file demos/Exhaustive.dhall + ``` + +- [ ] **Step 3: Regenerate the Exhaustive fixture and confirm no diff in + output** (this is the load-bearing check — everything above only proves + the Dhall type-checks, not that it still produces the same files): + ```bash + mise x -- dhall to-directory-tree --allow-path-separators --file demos/Exhaustive.dhall --output /tmp/pygen-after + ``` + Compare against a snapshot taken from the pre-migration tree the same way + (`git stash`, regenerate to `/tmp/pygen-before`, `git stash pop`, `diff -rq + /tmp/pygen-before /tmp/pygen-after`). Expected: **no diff**. Any diff here + is a migration bug, not an intentional update — per Global Constraints, go + fix it rather than accepting the new output. + +- [ ] **Step 4: Run the Python harness:** + ```bash + mise x -- uv sync + mise x -- uv run pytest tests -v + ``` + (Needs a reachable Postgres — `PGN_TEST_DATABASE_URL`, see `ci.yml` for the + Docker Compose equivalent — and the `mise`-pinned `pgn 0.9.1`, since the + harness shells out to it.) Expected: all green, no new failures relative to + a pre-migration run. + +- [ ] **Step 5: Add a CHANGELOG.md entry** under `# Upcoming` (the file + already starts with that heading), non-breaking, modeled on `java.gen`'s + own `v1.1.0` entry: + ```markdown + - Migrated the generator's internal dependencies to `gen-contract` v4.0.1 + and `gen-sdk` v2.0.0, adopting `Sdk.Sigs` in place of the local + `Algebras/` module, and restructured the repository layout to match the + pGenie generator architecture: implementation moved from `gen/` to + `src/`, the public entry point renamed from `gen/Gen.dhall` to + `src/package.dhall`, and the fixture driver moved from + `tests/Exhaustive.dhall` to `demos/Exhaustive.dhall`. No change to + generated output or the public Dhall interface (`artifacts..gen` + URLs pointing at a previously-released `resolved.dhall` are unaffected; + only the next release's URL path changes, from `.../gen/Gen.dhall` — the + unresolved source path some projects may reference directly instead of a + frozen release — to `.../src/package.dhall`). + ``` + Adjust the last parenthetical if no project in practice points at the + unresolved source path (check `README.md`'s own recommended usage — if it + only ever recommends the frozen `resolved.dhall` release asset, simplify + this to "no change to generated output or the public Dhall interface"). + +- [ ] **Step 6: Commit.** + +## Deferred / explicitly out of scope (record as follow-ups, don't do now) + +- Narrowing each interpreter's `Config` to only the fields it needs (see + Global Constraints). +- Giving `Member.dhall`/`ParamsMember.dhall` a real `Sdk.Sigs.interpreter` + shape by folding `CustomKind.Lookup` into `Config` or `Input` (see Global + Constraints). +- Adding a `Name` interpreter (`java.gen` has `Interpreters/Name.dhall` + centralizing identifier casing/escaping; `python.gen` calls + `Structures/PyIdent.dhall` ad hoc from several interpreters instead). This + is a real architecture-doc deviation but not something `java.gen`'s last + release changed — separate task if wanted. +- Recomputing the `as Source` bench-script hash pairs for `gen-sdk v2.0.0` / + `lude v5.1.0` (flagged inline in Task 6 — needs the real toolchain, not + guessable). From 81ea82c81dee169431839b06bfa530175aebf92f Mon Sep 17 00:00:00 2001 From: Nikita Volkov Date: Sat, 11 Jul 2026 17:31:09 +0300 Subject: [PATCH 9/9] Revert "The plan" This reverts commit 15c28361de3192aa2d0e896fa32076c5f841ece3. --- .../plans/2026-07-11-gen-sdk-v2-migration.md | 666 ------------------ 1 file changed, 666 deletions(-) delete mode 100644 docs/superpowers/plans/2026-07-11-gen-sdk-v2-migration.md diff --git a/docs/superpowers/plans/2026-07-11-gen-sdk-v2-migration.md b/docs/superpowers/plans/2026-07-11-gen-sdk-v2-migration.md deleted file mode 100644 index 5e0499c..0000000 --- a/docs/superpowers/plans/2026-07-11-gen-sdk-v2-migration.md +++ /dev/null @@ -1,666 +0,0 @@ -# python.gen: migrate to gen-contract v4.0.1 / gen-sdk v2.0.0, adopt architecture layout - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Bring `python.gen` up to the same structural shape as `java.gen`'s -last release (`v1.1.0`): pin `gen-contract`/`gen-sdk` directly (no bundled -SDK), adopt `Sdk.Sigs` in place of the local `Algebras/` folder, and move to -the `src/`-rooted repo layout the normative architecture doc describes — with -**zero change to generated output**. - -**Architecture:** Reference is -`gen-sdk/docs/generator-architecture.md` (already read in full) and -`java.gen`'s current `master` (tag `v1.1.0`, commit `658508c`). `python.gen` -is currently on pre-split `gen-sdk v0.11.0` (no `Sigs`, bundles `Project` -itself) with a hand-rolled `gen/Algebras/{Interpreter,Template}.dhall`, a -`gen/Deps/package.dhall` barrel, and the old `gen/` + `tests/Exhaustive.dhall` -layout — i.e. it has never done *any* of the steps `java.gen` went through -(`gen-migration-plan.md` at the pgenie repo root covers `rust.gen`/`haskell.gen`, -which already had `Sigs`; it does not cover `python.gen` at all). This plan -folds all of `java.gen`'s historical steps into one destination state, since -there is no reason to recreate `java.gen`'s intermediate commits. - -**Tech Stack:** Dhall (fork `pgn`'s dhall, package name `dhll`, needed for the -`Text/equal` builtin and `as Source` import mode — see Global Constraints), -Python 3.12 / `uv` / `pytest` for the harness, `mise` for tool pinning. - -## Global Constraints - -- **No behavior change.** This is a dependency/layout migration, not a - feature change. If `demos/Exhaustive.dhall` (see Task 5) produces different - file paths or content than `tests/Exhaustive.dhall` did before the move, - that is a bug in the migration, not an expected diff. -- **Do not touch interpreter algorithms.** `Interpreters/Project.dhall`'s - `Skip`/`Fail` logic (`typeSucceeds`/`queryChecks`/`effectiveQueries`) is - deliberately hand-rolled instead of using `Typeclasses.Classes.Alternative` - the way `java.gen` does, per an explicit in-file comment: multiple - `QueryGen.run`/`CustomTypeGen.run` call sites for the same query measurably - multiplied Dhall normalization time (seconds → minutes), confirmed by wall-time - bisection. This is orthogonal to the Deps/Sigs migration — leave it as is. - Do **not** add `src/Deps/Typeclasses.dhall` since nothing will use it. -- **`Interpreters/Member.dhall` and `Interpreters/ParamsMember.dhall` keep - their 3-argument `Run` type** (`Config -> CustomKind.Lookup -> Input -> - Compiled Output`) instead of conforming to `Sdk.Sigs.interpreter`'s fixed - 2-argument shape (`Config -> Input -> Result`). `java.gen`'s own - `Member.dhall` doesn't need a lookup table; `python.gen`'s does (custom-type - name resolution — see the `buildLookup`/`IndexedCustomType` comments in - `Interpreters/Project.dhall`). Bundling `Lookup` into `Config` or `Input` - to force-fit the Sig is a bigger, separate refactor — out of scope here. - These two files get the Deps-import and `Algebra.Config` → local `Config` - changes (Task 2) but keep their existing bare `{ Input, Output, Run, run }` - export, not `Sdk.Sigs.interpreter Config Input Output run`. -- **Config narrowing is deferred.** The architecture doc's ideal is each - interpreter declaring only the `Config` fields it (and its children) needs. - `java.gen` does this trivially (`{ useOptional : Bool }` everywhere, since - that's its whole config). `python.gen`'s config has 4 fields - (`packageName`, `importName`, `emitSync`, `onUnsupported`); auditing exactly - which fields each of the 11 interpreters actually reads and narrowing each - is real, separate work with real risk of missing a field some deeply nested - path needs. This plan keeps the **same 4-field `Config` record, declared - locally and verbatim in each interpreter module** (no more shared - `Algebra.Config` alias) — this satisfies "`Config` is a parameter each - module declares itself," just not narrowed. Note it as a follow-up, don't - do it now. -- **Local verification gap.** This sandbox has a global `dhall`/`pgn` (cabal - build, `dhll-1.42.3`) that is **not** the `mise`-pinned `pgn v0.9.1` this - repo's CI/tests actually use — a plain `dhall type --file tests/Exhaustive.dhall` - here fails on the *pre-migration* tree already, with an `as Source` - hash-integrity mismatch on `gen/Deps/Sdk.dhall` (confirmed during planning: - expected `8d43544e...`, actual `573b4655...`). This is a toolchain mismatch, - not evidence of a real problem. **Whoever executes this plan must run - verification through `mise x -- dhall ...` / `mise x -- pgn ...` / `mise x - -- uv run pytest`**, matching `mise.toml`'s pin, not a bare global `dhall`. - If `mise` isn't available in the execution environment either, at minimum - run `dhall format --transitive` (syntax-only, tool-version-agnostic) and - flag that deeper verification (`dhall type`, fixture diff, pytest, - basedpyright) still needs to happen on a properly provisioned machine/CI - before this is considered done. - ---- - -## File Structure - -``` -src/ (was gen/) - package.dhall (was Gen.dhall — now built via Sdk.Sigs.generator) - Config.dhall (unchanged content, moved) - Interpret.dhall (was compile.dhall — Config no longer Optional at top) - Deps/ - Contract.dhall (NEW — gen-contract v4.0.1 pin) - Sdk.dhall (bumped gen-sdk v0.11.0 → v2.0.0) - Lude.dhall (unchanged content, moved) - Prelude.dhall (unchanged content, moved) - (package.dhall barrel REMOVED) - Interpreters/ (11 files: Deps.Sdk.Project → Deps.Contract, Algebra → Sdk.Sigs) - Templates/ (10 files: Algebra → Sdk.Sigs; 7 also de-barrel Deps) - Structures/ (CustomKind.dhall: Deps.Sdk.Project → Deps.Contract; others untouched) - (Algebras/ REMOVED) -demos/ - Exhaustive.dhall (was tests/Exhaustive.dhall — rewritten for Sdk.Output.toFileMap) -tests/ (Python pytest harness — unchanged except any gen/-path references) -.github/workflows/{ci,release}.yml, .github/scripts/build-contract-shell.sh, -README.md, AGENTS.md, DESIGN.md, build.bash, bench/*.sh, mise.toml - (path references updated: gen/ → src/, tests/Exhaustive.dhall → demos/Exhaustive.dhall) -``` - ---- - -### Task 1: Add the new Deps pins and remove the `Deps/package.dhall` barrel - -**Files:** -- Create: `gen/Deps/Contract.dhall` -- Modify: `gen/Deps/Sdk.dhall` -- Delete: `gen/Deps/package.dhall` -- Modify (de-barrel): every file that currently has `let Deps = ../Deps/package.dhall` (see the full list in Tasks 2–3 — do this as part of those tasks, not twice) - -This task only stages the new pins; Task 2 is where the fallout (broken -`Sdk.Project`/`Sdk.Fixtures` references, `Algebras/` removal) gets fixed. Do -not try to get `dhall type` green after this task alone — it won't be, and -that's expected (same as `gen-migration-plan.md`'s phase-0 commit 2 for -`java.gen`). Fold Task 1 and Task 2 into one commit if you'd rather not carry -a known-broken intermediate state. - -- [ ] **Step 1: Create `gen/Deps/Contract.dhall`** — the exact pin `java.gen` - and `gen-sdk` itself use: - -```dhall -https://raw.githubusercontent.com/pgenie-io/gen-contract/v4.0.1/src/package.dhall - sha256:4a130ba7fbaa152a776babbb1bf2994a4833931ca76bde9bf6930d354225651e -``` - -- [ ] **Step 2: Bump `gen/Deps/Sdk.dhall`** to `gen-sdk v2.0.0` (same pin - `java.gen`'s `src/Deps/Sdk.dhall` uses). Preserve the existing `as Source` - import mode (see `AGENTS.md`/CI comments on why `python.gen` uses it — - `java.gen` doesn't, but that's an intentional `python.gen`-specific RAM - optimization, not something this migration should undo): - -```dhall -https://raw.githubusercontent.com/pgenie-io/gen-sdk/v2.0.0/src/package.dhall - sha256:b9def6ab1179bc4aaae7fc6e91977f094f75934cd5755175c294a9e97ca71b15 - as Source -``` - - If keeping `as Source` here, its hash is a *source*-text hash, not the - semantic hash above copied from `java.gen` (which imports plainly). Verify - with `dhall hash` against the raw URL using the repo's pinned toolchain - (`mise x -- dhall hash <<< 'https://raw.githubusercontent.com/pgenie-io/gen-sdk/v2.0.0/src/package.dhall as Source'` - or equivalent) before trusting the semantic hash verbatim in `as Source` - mode — don't guess. - -- [ ] **Step 3: Delete `gen/Deps/package.dhall`.** - -- [ ] **Step 4: Commit** (or fold into Task 2's commit). - ---- - -### Task 2: Rewire `Structures/CustomKind.dhall` and all 11 `Interpreters/*.dhall` - -**Files:** -- Modify: `gen/Structures/CustomKind.dhall` -- Modify: `gen/Interpreters/{CustomType,Member,ParamsMember,Primitive,Project,Query,QueryFragments,Result,ResultColumns,Scalar,Value}.dhall` -- Delete: `gen/Algebras/` (all three files) - -**The mechanical recipe, applied to every file above:** - -1. Replace `let Deps = ../Deps/package.dhall` with direct imports of exactly - what the file uses. Every one of these files uses `Deps.Sdk.Project` - (→ becomes a direct `Deps/Contract.dhall` import) and `Deps.Lude`/`Deps.Prelude` - (→ direct imports). Concretely, replace: - ```dhall - let Deps = ../Deps/package.dhall - ``` - with (only the lines this particular file actually needs — check with - `grep -n 'Deps\.' ` first): - ```dhall - let Lude = ../Deps/Lude.dhall - - let Prelude = ../Deps/Prelude.dhall - - let Model = ../Deps/Contract.dhall - ``` - and change every remaining `Deps.Lude` → `Lude`, `Deps.Prelude` → `Prelude` - in the body. `QueryFragments.dhall` additionally has an unused - `let Sdk = Deps.Sdk` line (line 7) — drop it, nothing in the file - references the `Sdk` binding. - -2. Replace the line `let Model = Deps.Sdk.Project` (now redundant with step - 1's `Model` binding) — don't duplicate it, step 1 already introduces - `Model` pointed at `Deps/Contract.dhall`. - -3. Delete `let Algebra = ../Algebras/Interpreter.dhall`. - -4. Add a local `Config` type declaration (same 4 fields everywhere per the - Global Constraints note on deferred narrowing): - ```dhall - let Config = - { packageName : Text - , importName : Text - , emitSync : Bool - , onUnsupported : OnUnsupported.Mode - } - ``` - This needs `OnUnsupported = ../Structures/OnUnsupported.dhall` imported in - any file that doesn't already import it (check first — `Project.dhall` - already does). - -5. Change every `\(config : Algebra.Config) ->` to `\(config : Config) ->`. - -6. Change the tail: - - **10 of the 11 files** (`CustomType`, `Primitive`, `Project`, `Query`, - `QueryFragments`, `Result`, `ResultColumns`, `Scalar`, `Value` — 9 files, - not 10; `Member`/`ParamsMember` are the exception below) end with - `Algebra.module Input Output run` (or, for `Scalar.dhall`, - `Algebra.module Input Output run /\ { ScalarDecode }`). Change to: - ```dhall - Sdk.Sigs.interpreter Config Input Output run - ``` - (`Scalar.dhall`: `Sdk.Sigs.interpreter Config Input Output run /\ { ScalarDecode }`), - which needs `let Sdk = ../Deps/Sdk.dhall` imported (it isn't currently, - since `Deps.Sdk.Project` used to come through the barrel — add it). - - **`Member.dhall` and `ParamsMember.dhall`** keep their existing tail - verbatim: `in { Input, Output, Run, run }`, just with `Algebra.Config` → - `Config` in the `Run` type alias line - (`let Run = Config -> CustomKind.Lookup -> Input -> Lude.Compiled.Type Output`). - No `Sdk.Sigs.interpreter` here — see Global Constraints. - -7. `Interpreters/Project.dhall` specifically: its two `\(config : Algebra.Config) ->` - occurrences (the `combineOutputs` and `run` functions) both become - `\(config : Config) ->`; its `lookupConfig : Algebra.Config` type - annotation (used to type-check `Value.run` calls for composite-field - rendering) becomes `lookupConfig : Config`. No other logic in this file - changes — the `Skip`/`Fail` machinery is untouched per Global Constraints. - -- [ ] **Step 1: Apply the recipe to all 11 `Interpreters/*.dhall` files and `Structures/CustomKind.dhall`.** -- [ ] **Step 2: Delete `gen/Algebras/`.** -- [ ] **Step 3: Verify** (with the `mise`-pinned toolchain, not the bare local `dhall` — see Global Constraints): - ```bash - mise x -- dhall type --file gen/Interpreters/Project.dhall - ``` - Expected: prints the interpreter's type (a record with `Input`, `Output`, - `Result`, `Run`, `run` fields) with no error. This alone pulls in every - other `Interpreters/*.dhall` transitively, so it's a full check of this task. -- [ ] **Step 4: Commit.** - ---- - -### Task 3: Rewire the 10 `Templates/*.dhall` files - -**Files:** -- Modify: `gen/Templates/{CompositeModule,CoreModule,EnumModule,FacadeModule,InitModule,RegisterModule,RowsModule,RuntimeModule,StatementModule,TypesInit}.dhall` - -**Recipe:** - -1. `CoreModule.dhall`, `InitModule.dhall`, `RuntimeModule.dhall` don't import - `Deps` at all (no `Prelude`/`Lude` need) — only change: drop - `let Algebra = ../Algebras/Template.dhall`, add `let Sdk = ../Deps/Sdk.dhall`, - and change the tail. `CoreModule`/`RuntimeModule` end with - `Algebra.module {} (\(_ : {}) -> content)` → `Sdk.Sigs.template {} (\(_ : {}) -> content)`. - `InitModule` ends with `Algebra.module Params render` → - `Sdk.Sigs.template Params render`. - -2. The other 7 (`CompositeModule`, `EnumModule`, `FacadeModule`, - `RegisterModule`, `RowsModule`, `StatementModule`, `TypesInit`) currently - have `let Deps = ../Deps/package.dhall`, and only ever use - `Deps.Prelude.*` (all seven) and, additionally, `Deps.Lude.Text.indentNonEmpty` - (`RowsModule`, `StatementModule` only — confirmed by - `grep -n 'Deps\.' gen/Templates/*.dhall` during planning). Replace the - barrel import with: - ```dhall - let Prelude = ../Deps/Prelude.dhall - ``` - adding `let Lude = ../Deps/Lude.dhall` only in `RowsModule.dhall` and - `StatementModule.dhall`. Then replace `Deps.Prelude.` → `Prelude.` and - `Deps.Lude.` → `Lude.` throughout each file's body. Drop - `let Algebra = ../Algebras/Template.dhall`, add `let Sdk = ../Deps/Sdk.dhall`. - -3. Tails for these 7: `Algebra.module Params run` → - `Sdk.Sigs.template Params run` (`FacadeModule`, `RegisterModule`, - `StatementModule`); with a combined record for the other 4: - `Algebra.module Params run /\ { Field }` (`CompositeModule`) → - `Sdk.Sigs.template Params run /\ { Field }`; `/\ { Variant }` (`EnumModule`); - `/\ { StatementExport, TypeExport }` (`FacadeModule` — check which of - `FacadeModule`/others actually has this combinator vs a plain - `Algebra.module Params run`, per the earlier grep output, before editing — - don't assume, re-`grep -n 'Algebra.module' gen/Templates/*.dhall` and - match each file's exact current tail); `/\ { RowDef }` (`RowsModule`); - `/\ { Export }` (`TypesInit`). - -- [ ] **Step 1: Apply the recipe to all 10 files.** -- [ ] **Step 2: Verify:** - ```bash - mise x -- dhall type --file gen/Interpreters/Project.dhall - ``` - (Templates are only reachable transitively through `Interpreters/Project.dhall` - and its children, same as Task 2 — this single check covers both tasks once - both are done. If running Task 2 and 3 as separate commits, this step will - fail after Task 2 alone if any interpreter references a not-yet-updated - template's old shape; if so, do Tasks 2 and 3 as one commit instead.) -- [ ] **Step 3: Commit.** - ---- - -### Task 4: Rewrite the root entry point (`Config.dhall`, `compile.dhall` → `Interpret.dhall`, `Gen.dhall` → `package.dhall`) - -**Files:** -- Modify (move, content unchanged): `gen/Config.dhall` -- Modify (move + rewrite): `gen/compile.dhall` → `gen/Interpret.dhall` -- Modify (move + rewrite): `gen/Gen.dhall` → `gen/package.dhall` - -(Paths shown as `gen/...` here since Task 5 does the `gen/` → `src/` directory -move; do this task first, in place, then Task 5 is a pure `git mv` sweep with -no further content changes.) - -**Interfaces:** -- Consumes: `Interpreters/Project.dhall`'s `run` (produced by Task 2, - now `Sdk.Sigs.interpreter`-shaped: `.run : Config -> Contract.Project -> Compiled Output`). -- Produces: `package.dhall`'s `Sdk.Sigs.generator`-built value - (`{ contractVersion, Config, compile }`), consumed by Task 5's - `demos/Exhaustive.dhall` and by any pGenie project's `artifacts..gen` URL. - -`Sdk.Sigs.generator`'s shape (from the architecture doc): -```dhall -\(Config : Type) -> -\(defaultConfig : Config) -> -\(interpret : Config -> Contract.Project -> Contract.Output) -> - let compile = \(config : Optional Config) -> - merge { None = interpret defaultConfig, Some = interpret } config - in Contract.module Config compile -``` -Note `interpret` takes a **bare** `Config`, not `Optional Config` — the outer -"config block omitted entirely" case is handled once, by substituting -`defaultConfig`, not by `interpret` itself. `python.gen`'s current -`compile.dhall` handles *two* levels of optionality (the whole block, and -each field within it) with a doubled `Prelude.Optional.fold`. Only the outer -level goes away; each field inside `Config` stays individually `Optional` (so -a project can supply `emitSync: true` alone and still get default -`packageName`/`onUnsupported`) — that per-field defaulting is -`python.gen`-specific richness `java.gen` doesn't have (its `Config` has one -non-Optional `Bool` field), and this migration must not lose it. - -- [ ] **Step 1: `gen/Config.dhall`** — content unchanged, just confirm it - still reads (no edits needed here; listed for completeness since Task 5 - moves the file). - -- [ ] **Step 2: Rewrite `gen/compile.dhall` as `gen/Interpret.dhall`** — - drop the outer `Optional Config` unwrap (the two outermost - `Prelude.Optional.fold Config config Text (\(c : Config) -> ...)` / - `... Bool ...` / `... OnUnsupported.Mode ...` wrappers), keep everything - else (the per-field defaults, `importName` derivation) as is: - -```dhall -let Deps = ./Deps/package.dhall - --- NOTE: Task 5 changes this to ./Deps/Contract.dhall / ./Deps/Prelude.dhall --- directly once the Deps barrel is gone (Task 1) — write it that way now, --- don't reintroduce the barrel: -let Contract = ./Deps/Contract.dhall - -let Prelude = ./Deps/Prelude.dhall - -let Config = ./Config.dhall - -let OnUnsupported = ./Structures/OnUnsupported.dhall - -let ProjectInterpreter = ./Interpreters/Project.dhall - --- Entry point handed to gen-sdk's Sdk.Sigs.generator as `interpret`. Each --- field of Config is independently Optional, so a project may omit the --- whole config block (Sdk.Sigs.generator substitutes an all-None --- defaultConfig, see package.dhall) or any subset of its keys; `defaults` --- collects every fallback in one place (packageName from the project name in --- kebab case, emitSync off, onUnsupported Fail). The async surface is always --- emitted; emitSync adds the sync mirror. -in \(config : Config) -> - \(project : Contract.Project) -> - let defaults = - { packageName = project.name.inKebabCase - , emitSync = False - , onUnsupported = OnUnsupported.Mode.Fail - } - - let packageName = - Prelude.Optional.fold - Text - config.packageName - Text - (\(t : Text) -> t) - defaults.packageName - - let emitSync = - Prelude.Optional.fold - Bool - config.emitSync - Bool - (\(b : Bool) -> b) - defaults.emitSync - - let onUnsupported = - Prelude.Optional.fold - OnUnsupported.Mode - config.onUnsupported - OnUnsupported.Mode - (\(m : OnUnsupported.Mode) -> m) - defaults.onUnsupported - - let importName = Prelude.Text.replace "-" "_" packageName - - let interpreterConfig = { packageName, importName, emitSync, onUnsupported } - - in ProjectInterpreter.run interpreterConfig project -``` - -- [ ] **Step 3: Rewrite `gen/Gen.dhall` as `gen/package.dhall`:** - -```dhall -let Sdk = ./Deps/Sdk.dhall - -let OnUnsupported = ./Structures/OnUnsupported.dhall - -let Config = ./Config.dhall - -let Config/default - : Config - = { packageName = None Text - , emitSync = None Bool - , onUnsupported = None OnUnsupported.Mode - } - -let interpret = ./Interpret.dhall - -in Sdk.Sigs.generator Config Config/default interpret -``` - -- [ ] **Step 4: Verify:** - ```bash - mise x -- dhall type --file gen/package.dhall - ``` - Expected type: a record with `contractVersion`, `Config`, `compile` fields - (`compile : Optional Config -> Contract.Project -> Contract.Output`). -- [ ] **Step 5: Commit.** - ---- - -### Task 5: Move `gen/` → `src/`, `tests/Exhaustive.dhall` → `demos/Exhaustive.dhall` - -**Files:** -- Move: `gen/` → `src/` (whole tree, `git mv`) -- Move + rewrite: `tests/Exhaustive.dhall` → `demos/Exhaustive.dhall` - -- [ ] **Step 1:** - ```bash - git mv gen src - mkdir -p demos - git mv tests/Exhaustive.dhall demos/Exhaustive.dhall - ``` - All the `../Deps/...`, `./Interpreters/...`, `../Templates/...` style - relative imports inside `src/` are untouched by this move (they're relative - to their own file, not to the repo root), so no content changes are needed - inside `src/` itself from the move alone. - -- [ ] **Step 2: Rewrite `demos/Exhaustive.dhall`.** Its old body called - `Gen.compileToFileMap config project` — `Sdk.Sigs.generator`-built modules - don't have a `compileToFileMap` field (per the architecture doc: "there is - no `compileToFileMap` on the module — turning an `Output` into files is the - caller's job, via `Sdk.Output.toFileMap`"). New content: - -```dhall --- Applies this generator to gen-sdk's shared cross-backend fixture project --- (the same "music_catalogue" project java.gen's own demos/Exhaustive.dhall --- exercises), so a Python client compiles from it and passes basedpyright --- strict. Pinned directly at gen-sdk's package.dhall, separately from --- src/Deps/Sdk.dhall: that file only imports gen-sdk's `package.dhall` `as --- Source` for RAM, and this fixture load doesn't need that mode. --- --- The fixture project deliberately covers PG types this generator does not --- support (box, inet, money, ranges, ...), so onUnsupported is set to Skip: --- those statements/types are dropped with a warning instead of aborting the --- whole compile. --- --- Intended to be executed with: --- --- ```bash --- dhall to-directory-tree --file demos/Exhaustive.dhall --output --allow-path-separators --- ``` -let Sdk = ../src/Deps/Sdk.dhall - -let Gen = ../src/package.dhall - -let OnUnsupported = ../src/Structures/OnUnsupported.dhall - -let project = Sdk.Fixtures.Exhaustive - -let config = - Some - { packageName = None Text - , emitSync = Some True - , onUnsupported = Some OnUnsupported.Mode.Skip - } - -in Sdk.Output.toFileMap (Gen.compile config project) -``` - -- [ ] **Step 3: Verify:** - ```bash - mise x -- dhall type --file demos/Exhaustive.dhall - ``` - Expected: `List { mapKey : Text, mapValue : Text }` (or however this - fork/version of Dhall renders `Prelude.Map.Type Text Text`), no error. -- [ ] **Step 4: Commit.** - ---- - -### Task 6: Update every external reference to the old paths - -**Files:** -- Modify: `.github/workflows/ci.yml` -- Modify: `.github/workflows/release.yml` -- Modify: `.github/scripts/build-contract-shell.sh` -- Modify: `README.md` -- Modify: `AGENTS.md` -- Modify: `DESIGN.md` -- Modify: `build.bash` -- Modify: `bench/generate.sh`, `bench/as-source.sh` -- Modify: `mise.toml` (the `golden` task) - -**Path substitutions to apply everywhere they occur** (verify each hit with -`grep -rn` first — don't blind-sed across the whole repo, `tests/golden/` -contains generated Python that must NOT be touched): - -| Old | New | -|---|---| -| `gen/Gen.dhall` | `src/package.dhall` | -| `gen/Deps/*.dhall` | `src/Deps/*.dhall` | -| `tests/Exhaustive.dhall` | `demos/Exhaustive.dhall` | -| `gen/` (prose/dir references) | `src/` | - -Specific known hits (from `grep -rn "gen/Gen\.dhall\|gen/Deps\|tests/Exhaustive"` -run during planning): - -- `.github/workflows/ci.yml`: the `contract` job's "Strip `as Source`..." - step does `sed -i ... gen/Deps/*.dhall` → `src/Deps/*.dhall`; the - "Generate output from Dhall" step's `dhall_file: tests/Exhaustive.dhall` → - `demos/Exhaustive.dhall`. -- `.github/workflows/release.yml`: the "Resolve Dhall" step's - `file: gen/Gen.dhall` → `file: src/package.dhall`. -- `.github/scripts/build-contract-shell.sh`: comment references - `tests/Exhaustive.dhall` → `demos/Exhaustive.dhall` (comment only, verify - no functional path argument needs changing — it's invoked with - `contract-output` as a positional arg per `ci.yml`, not a hardcoded path). -- `README.md`: line ~38 `gen: https://raw.githubusercontent.com/slavashvets/python.gen/master/gen/Gen.dhall` - → `.../src/package.dhall`; lines ~64-66, the three example URLs - (`.../gen/Gen.dhall`) → `.../src/package.dhall`. -- `AGENTS.md`: line ~36 "`gen/` pins its remote imports by sha256 - (`gen/Deps/*.dhall`)" → "`src/` pins its remote imports by sha256 - (`src/Deps/*.dhall`)". -- `DESIGN.md`: line 4 (`gen/`), line 381 (`gen/Gen.dhall` "is the entry point - handed to gen-sdk"), line 397 (`gen/` mirrors...), line 401 (the `gen/` - tree diagram — replace with the new `src/` tree, matching Task 5's actual - post-move layout), lines 632/636 (`tests/Exhaustive.dhall` → `demos/Exhaustive.dhall`). -- `build.bash`: this is a scratch/dev script (mostly commented-out lines) — - update the live lines: `target=tests/Exhaustive.dhall` → - `target=demos/Exhaustive.dhall`; the commented `# target=gen/Gen.dhall` and - `# dhall freeze gen/Deps/*.dhall` lines → `src/` equivalents (keep them - commented, just fix the paths so they're not stale if uncommented later). -- `bench/generate.sh`, `bench/as-source.sh`: both `cp -R "$root/gen" "$out/gen"` / - `"$1/gen"` → `"$root/src" "$out/src"` (and update the `gen/Deps/*.dhall` - perl substitutions to `src/Deps/*.dhall`). **The `as Source` → plain-import - sha256 substitutions in both scripts' `strip_as_source` are pinned to the - *old* `gen-sdk v0.11.0`/`lude v5.1.0` source-vs-normalized hash pairs** - (`8d43544e...`→`b9f7bb84...` for Sdk, `46b527b0...`→`14c43eec...` for Lude). - Since `Task 1` bumps `gen-sdk` to `v2.0.0`, these substitution pairs are now - wrong and must be recomputed for the new pin using the repo's actual pinned - toolchain (`mise x -- dhall hash` on the plain, non-`as-Source` import) — - don't guess these; if the recompute can't happen in this pass, leave a - `# TODO` in the script rather than shipping a silently-wrong benchmark. -- `mise.toml`'s `golden` task: the `python3 - ... "$root/gen/Gen.dhall"` arg - and the fixture-project string replace target `"../../gen/Gen.dhall"` → - `"../../src/package.dhall"`. - -- [ ] **Step 1: Apply all substitutions above.** -- [ ] **Step 2: Confirm no stragglers:** - ```bash - grep -rn "gen/Gen\.dhall\|gen/Deps\|gen/Interpreters\|gen/Templates\|gen/Structures\|gen/Config\.dhall\|gen/compile\.dhall\|tests/Exhaustive" \ - --include="*.md" --include="*.yml" --include="*.yaml" --include="*.toml" --include="*.sh" --include="*.bash" . - ``` - Expected: no output (everything left under `tests/golden/` or `tests/fixture-project/` - that isn't a generator-path reference is fine and out of scope — check any - hit manually rather than assuming). -- [ ] **Step 3: Commit.** - ---- - -### Task 7: Format, verify end-to-end, update CHANGELOG - -- [ ] **Step 1: Format everything:** - ```bash - mise x -- dhall format --transitive src/package.dhall - mise x -- dhall format --transitive demos/Exhaustive.dhall - ``` - -- [ ] **Step 2: Full type-check:** - ```bash - mise x -- dhall type --file src/package.dhall - mise x -- dhall type --file demos/Exhaustive.dhall - ``` - -- [ ] **Step 3: Regenerate the Exhaustive fixture and confirm no diff in - output** (this is the load-bearing check — everything above only proves - the Dhall type-checks, not that it still produces the same files): - ```bash - mise x -- dhall to-directory-tree --allow-path-separators --file demos/Exhaustive.dhall --output /tmp/pygen-after - ``` - Compare against a snapshot taken from the pre-migration tree the same way - (`git stash`, regenerate to `/tmp/pygen-before`, `git stash pop`, `diff -rq - /tmp/pygen-before /tmp/pygen-after`). Expected: **no diff**. Any diff here - is a migration bug, not an intentional update — per Global Constraints, go - fix it rather than accepting the new output. - -- [ ] **Step 4: Run the Python harness:** - ```bash - mise x -- uv sync - mise x -- uv run pytest tests -v - ``` - (Needs a reachable Postgres — `PGN_TEST_DATABASE_URL`, see `ci.yml` for the - Docker Compose equivalent — and the `mise`-pinned `pgn 0.9.1`, since the - harness shells out to it.) Expected: all green, no new failures relative to - a pre-migration run. - -- [ ] **Step 5: Add a CHANGELOG.md entry** under `# Upcoming` (the file - already starts with that heading), non-breaking, modeled on `java.gen`'s - own `v1.1.0` entry: - ```markdown - - Migrated the generator's internal dependencies to `gen-contract` v4.0.1 - and `gen-sdk` v2.0.0, adopting `Sdk.Sigs` in place of the local - `Algebras/` module, and restructured the repository layout to match the - pGenie generator architecture: implementation moved from `gen/` to - `src/`, the public entry point renamed from `gen/Gen.dhall` to - `src/package.dhall`, and the fixture driver moved from - `tests/Exhaustive.dhall` to `demos/Exhaustive.dhall`. No change to - generated output or the public Dhall interface (`artifacts..gen` - URLs pointing at a previously-released `resolved.dhall` are unaffected; - only the next release's URL path changes, from `.../gen/Gen.dhall` — the - unresolved source path some projects may reference directly instead of a - frozen release — to `.../src/package.dhall`). - ``` - Adjust the last parenthetical if no project in practice points at the - unresolved source path (check `README.md`'s own recommended usage — if it - only ever recommends the frozen `resolved.dhall` release asset, simplify - this to "no change to generated output or the public Dhall interface"). - -- [ ] **Step 6: Commit.** - -## Deferred / explicitly out of scope (record as follow-ups, don't do now) - -- Narrowing each interpreter's `Config` to only the fields it needs (see - Global Constraints). -- Giving `Member.dhall`/`ParamsMember.dhall` a real `Sdk.Sigs.interpreter` - shape by folding `CustomKind.Lookup` into `Config` or `Input` (see Global - Constraints). -- Adding a `Name` interpreter (`java.gen` has `Interpreters/Name.dhall` - centralizing identifier casing/escaping; `python.gen` calls - `Structures/PyIdent.dhall` ad hoc from several interpreters instead). This - is a real architecture-doc deviation but not something `java.gen`'s last - release changed — separate task if wanted. -- Recomputing the `as Source` bench-script hash pairs for `gen-sdk v2.0.0` / - `lude v5.1.0` (flagged inline in Task 6 — needs the real toolchain, not - guessable).