Author editor layout from Python: with_position and flow_direction (v0.1.22) - #65
Merged
Volv-G merged 1 commit intoSep 25, 2026
Conversation
…0.1.22) The pipeline editor stores graph layout in ordinary annotations, so a Python author can already write it — as a hand-serialized JSON string under a key they have to remember. This adds the typed spelling: `.with_position(x, y)` on task and subpipeline handles, and `@pipeline(flow_direction=...)` for the root. Both are pure sugar: they route through `with_annotations` / the `annotations` mapping, so the compiled bundle is byte-identical to the hand-written form and the last write to a key wins regardless of which spelling made it. The key names, value format and validation live in a new stdlib-only `tangle_cli.editor_layout`, shared with `pipelines layout` and the runner's auto-layout gate so the three cannot drift. It sits outside `python_pipeline` because importing that package pulls in the authoring/codegen stack, which the layout and submit paths must not require; callers inject their own `error_cls`.
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
The pipeline editor stores graph layout in ordinary annotations, so a Python author can already write it — as a hand-serialized JSON string under a key they have to remember:
This adds the typed spelling.
.with_position(x, y)on task and subpipeline handles, andflow_direction=on@pipeline:Both spellings stay valid and compile to the same bytes. First step toward a YAML→Python decompiler:
editor.positionappears 1193 times andeditor.flow-direction54 times in our pipeline corpus, and a decompiler cannot emit Python for a document it has no way to express.How
.with_position(...)is a.with_annotations({"editor.position": ...})call, andflow_directionis assigned into theannotationsmapping. That is what makes the collision rule single: the last write wins, whichever spelling made it, with no shadowing and no error.flow_directionis assigned after the mapping, so the typed keyword wins on that key while an already-present key keeps its position in key order.editor.positionis a JSON object string (JSON.stringify/JSON.parseon the editor side), keysx, yplus optionalwidth/height,json.dumpsspacing — the same bytestangle sdk pipelines layoutalready writes. Negative coordinates are normal. Absent dimensions are omitted rather than written as null. The editor also readsw/h; we always write the long names it writes itself.top-to-bottomis accepted although the editor renders left-to-right today, because the corpus contains it and a port of an existing document has to stay expressible.tangle_cli.editor_layoutowns the key names, the format and the validation, and is shared by the authoring sugar,pipelines layout, and the runner's auto-layout gate, so the three cannot drift. It lives outsidepython_pipelinedeliberately: importing that package pulls in the authoring/codegen stack and its optional dependencies, which the layout command and the submit-time gate must not require. Callers injecterror_cls, the same patterncheck_annotationsuses; authoring injectsInvalidEditorLayoutError(aCompileError).bool, non-numeric,NaNand±infcoordinates and unknown flow directions are refused before anything is written.Truewould silently serialize as1; non-finite values are not JSON and break the editor'sJSON.parse. Messages name the coordinate or list the allowed directions and never echo the value.Failure modes
componentRef, so they cannot move component digests, compile identity or cache behaviour. On a subpipeline handle the position applies to the parent task and the child sidecar is byte-identical either way — pinned by a test comparing child sidecar name and bytes..with_position(...)now suppresses auto-layout (and--force-layoutstill overrides). A graph positioned entirely at(0, 0)still counts as unpositioned — pre-existing runner semantics, pinned here because the sugar makes(0, 0)easy to write.editor.positionand the editor reads it there, but anIn[T]parameter has nowhere to hang layout; that needs agraph_input()API and is follow-up work, noted in the README.Review focus
with_annotationsrather than competing with it — this is what keeps one collision rule instead of two.editor_layout.pyoutsidepython_pipeline(two packaging guards assert a minimal no-deps install still imports; there is now a regression test pinning the light import).Tophatting
39 tests: byte identity of the whole compiled bundle against the hand-written annotation, the exact serialized value format, chaining/immutability, last-write-wins in both orders, typed-keyword precedence, 12 hostile coordinate/direction cases asserting the rejected value never appears, fail-before-write, subpipeline parent-task positioning with child sidecar identity, componentRef equality, both auto-layout gates, and agreement with what
pipelines layoutwrites.Checklist
pyproject.toml,packages/tangle-cli/src/tangle_cli/__init__.py,tests/test_packaging.py, and theuv.lockeditable self-entry (lock diff is the one-line version change only).