Repository navigation
Conversation
`@Layout()` / `@Layout("name", recursive=...)` marks a @pipeline graph
definition for auto-layout. When the caller passes a `layout_transform`
to `compile_pipeline` (also forwarded by `PipelineCompiler.compile_file`
and `pipelines.compile_pipeline_file`), every covered graph is laid out
post-order before validation and writing, so the root and subgraph YAML
carry `editor.position` annotations. Tangle CLI ships no algorithm.
- Coverage: an explicit declaration wins; `recursive=True` (default)
covers undecorated descendants, `recursive=False` only its own graph;
`@Layout()` resets to the transform's default.
- Shared children compile per (compile key, layout policy): different
inherited policies yield distinct sidecars, the same policy dedups.
Uncovered children keep legacy filenames; cycle detection ignores
layout.
- The transform receives a private copy plus a `GraphLayoutContext`
(governing Layout, first-occurrence path, task interfaces, artifact
dir). A guard allows only `editor.position` changes on graph tasks and
top-level inputs/outputs; failures write nothing.
- `TaskInterface` is exact for @task/subpipeline tasks and approximate
(supplied arguments + consumed outputs, incl. isEnabled) for opaque
refs; nothing is fetched remotely.
- Without a transform, or without @layout(), output is byte-identical
(a warning notes each unapplied @layout()).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
(AI-assisted)
What
This PR adds compile-time graph auto-layout for Python-authored pipelines and bumps the version to 0.1.29.
@Layout()only declares which graphs to lay out. A caller-suppliedlayout_transformdoes the actual layout, so the written root and subgraph YAML containeditor.positionannotations:Tangle CLI ships no layout algorithm and has no algorithm-name allowlist. A downstream distribution (Discovery's tangle-deploy) installs its own engine as the transform.
New public API, exported from
tangle_cli.python_pipelineand re-exported bytangle_cli.pipeline_compiler:Layout,GraphLayoutContext,GraphLayoutTransform,TaskInterface,InvalidLayoutError.Behaviour
recursive=True(the default) passes it down to undecorated descendants.recursive=Falsecovers only that graph.@Layout(...)wins, and its ownrecursiveflag governs further down.@Layout()resets to the transform's default algorithm.GraphLayoutContextprovides the governingLayout, the first-occurrence task-ID path (()for the root), the pipeline name,task_interfacesandartifact_dir.editor.positionvalues on graph tasks and top-levelinputs/outputs. Creating, replacing (which overwrites.with_position) and removing positions is allowed.CompileErrorfrom the transform propagates as-is; any other exception is wrapped. On any failure nothing is written.TaskInterface(inputs, outputs, approximate)gives ordered port names.@taskandsubpipelinetasks get their exact interface, unioned with observed names.ref/@registeredtasks get every supplied argument (edges and literals) plus the outputs other tasks consume, including throughisEnabled, markedapproximate=True.layout_transform(plaintangle sdk pipelines compile), or without@Layout(), compiled bytes, filenames and keys are identical to before.CompileResult.warningsnotes each@Layout()that was not applied.algorithmmust beNoneor an exact non-emptystr, positional or keyword.recursivemust be an exactbool.InvalidLayoutError(aCompileError): a bare@Layout, a duplicate declaration, or a non-graph target (a@task, asubpipeline(...)handle, a class, a partial, aPipelineFnsubclass, …).@pipelinework and return the target unchanged. The intent lives on thePipelineFn, so imported and pre-imported/cached children keep it.How
python_pipeline/layout.py(new): the immutableLayout, the context and protocol types, andTaskInterface.pipeline.pyaddsPipelineFn.layout, which is excluded from equality and from compile keys. An inner@Layout()is snapshotted per wrapper.pipeline_compiler.py:_layout_policy_forresolvesapply/carryper occurrence.(compile key, policy variant), with a variant-aware sidecar hash (legacy hash when uncovered)._apply_layout_transformruns at the end of_compile_pipeline_fn, after children and before_validate_artifact._layout_task_interfacesbuilds the interfaces.@Layout())" section.Failure modes
approximate=True).Testing
tests/test_layout_annotation.py: 22 consolidated, table-driven tests. They cover:isEnabled;git diff --check, anduv lock --checkagainst PyPI are clean.uv buildpasses, and the wheel smoke test reportstangle version=0.1.29.master(pre-existing, in_plan_task_sidecar).isEnabledgeometry gap. It independently killed in-memory mutants for each load-bearing rule (detach copy,recursive=False, inheritance, registry and filename policy, cycle detection,isEnabled, input union, guard, metadata preservation). Review pages:Review focus
_layout_policy_forand the registry/filename keying._strip_positions/_apply_layout_transform.