connect-flow 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. connect_flow-0.1.1/.claude/settings.json +24 -0
  2. connect_flow-0.1.1/.gitignore +10 -0
  3. connect_flow-0.1.1/.gitlab-ci.yml +37 -0
  4. connect_flow-0.1.1/.pre-commit-config.yaml +39 -0
  5. connect_flow-0.1.1/.pre-commit-hooks.yaml +15 -0
  6. connect_flow-0.1.1/CHANGELOG.md +18 -0
  7. connect_flow-0.1.1/CLAUDE.md +127 -0
  8. connect_flow-0.1.1/LICENSE +21 -0
  9. connect_flow-0.1.1/PKG-INFO +716 -0
  10. connect_flow-0.1.1/README.md +689 -0
  11. connect_flow-0.1.1/docs/adr/0001-cross-environment-id-mapping.md +66 -0
  12. connect_flow-0.1.1/docs/adr/0002-default-region-fallback.md +37 -0
  13. connect_flow-0.1.1/docs/adr/0003-boto3-required-dependency.md +37 -0
  14. connect_flow-0.1.1/docs/adr/0004-name-based-addressing.md +64 -0
  15. connect_flow-0.1.1/docs/adr/0005-default-to-stdout.md +54 -0
  16. connect_flow-0.1.1/docs/adr/0006-list-describe-mode.md +44 -0
  17. connect_flow-0.1.1/docs/adr/0007-normalize-opaque-reference-resolution.md +66 -0
  18. connect_flow-0.1.1/docs/adr/0008-unified-instance-flow-addressing.md +83 -0
  19. connect_flow-0.1.1/docs/adr/0009-copy-verb.md +63 -0
  20. connect_flow-0.1.1/docs/adr/0010-diff-instance-addressing.md +40 -0
  21. connect_flow-0.1.1/docs/adr/0011-cli-module-split.md +52 -0
  22. connect_flow-0.1.1/docs/adr/0012-load-module-split.md +50 -0
  23. connect_flow-0.1.1/docs/adr/0013-copy-name-auto-resolution.md +47 -0
  24. connect_flow-0.1.1/docs/adr/0014-copy-preview-exit-code.md +35 -0
  25. connect_flow-0.1.1/docs/adr/0015-copy-writes-by-default.md +59 -0
  26. connect_flow-0.1.1/docs/adr/0016-functional-only-comparison.md +77 -0
  27. connect_flow-0.1.1/docs/adr/0017-emit-map-auto-naming-and-merge.md +64 -0
  28. connect_flow-0.1.1/docs/adr/0018-saved-qualifier-name-based-addressing.md +61 -0
  29. connect_flow-0.1.1/docs/adr/0019-emit-map-deterministic-output.md +62 -0
  30. connect_flow-0.1.1/docs/adr/0020-optional-graphviz-extra.md +36 -0
  31. connect_flow-0.1.1/docs/data-model.md +113 -0
  32. connect_flow-0.1.1/docs/design.md +185 -0
  33. connect_flow-0.1.1/docs/images/messy.after.jpg +0 -0
  34. connect_flow-0.1.1/docs/images/messy.before.jpg +0 -0
  35. connect_flow-0.1.1/docs/safety.md +214 -0
  36. connect_flow-0.1.1/pyproject.toml +50 -0
  37. connect_flow-0.1.1/scripts/release.py +143 -0
  38. connect_flow-0.1.1/src/connect_flow/__init__.py +1 -0
  39. connect_flow-0.1.1/src/connect_flow/__main__.py +4 -0
  40. connect_flow-0.1.1/src/connect_flow/args.py +647 -0
  41. connect_flow-0.1.1/src/connect_flow/arn.py +177 -0
  42. connect_flow-0.1.1/src/connect_flow/autoresolve.py +259 -0
  43. connect_flow-0.1.1/src/connect_flow/aws.py +414 -0
  44. connect_flow-0.1.1/src/connect_flow/cli.py +612 -0
  45. connect_flow-0.1.1/src/connect_flow/diff.py +262 -0
  46. connect_flow-0.1.1/src/connect_flow/digest.py +135 -0
  47. connect_flow-0.1.1/src/connect_flow/exits.py +27 -0
  48. connect_flow-0.1.1/src/connect_flow/flow.py +121 -0
  49. connect_flow-0.1.1/src/connect_flow/graph.py +147 -0
  50. connect_flow-0.1.1/src/connect_flow/layout.py +185 -0
  51. connect_flow-0.1.1/src/connect_flow/lint.py +89 -0
  52. connect_flow-0.1.1/src/connect_flow/load.py +151 -0
  53. connect_flow-0.1.1/src/connect_flow/mapping.py +343 -0
  54. connect_flow-0.1.1/src/connect_flow/metrics.py +94 -0
  55. connect_flow-0.1.1/src/connect_flow/pipeline.py +917 -0
  56. connect_flow-0.1.1/src/connect_flow/py.typed +0 -0
  57. connect_flow-0.1.1/src/connect_flow/remote_write.py +177 -0
  58. connect_flow-0.1.1/src/connect_flow/render.py +66 -0
  59. connect_flow-0.1.1/src/connect_flow/report.py +184 -0
  60. connect_flow-0.1.1/src/connect_flow/resolve.py +324 -0
  61. connect_flow-0.1.1/src/connect_flow/targets.py +167 -0
  62. connect_flow-0.1.1/src/connect_flow/writer.py +211 -0
  63. connect_flow-0.1.1/tests/conftest.py +53 -0
  64. connect_flow-0.1.1/tests/fixtures/README.md +21 -0
  65. connect_flow-0.1.1/tests/fixtures/annotations.json +47 -0
  66. connect_flow-0.1.1/tests/fixtures/compound.json +48 -0
  67. connect_flow-0.1.1/tests/fixtures/env_arns.json +38 -0
  68. connect_flow-0.1.1/tests/fixtures/env_map.json +5 -0
  69. connect_flow-0.1.1/tests/fixtures/first_match.json +61 -0
  70. connect_flow-0.1.1/tests/fixtures/mapped.json +38 -0
  71. connect_flow-0.1.1/tests/fixtures/messy.json +113 -0
  72. connect_flow-0.1.1/tests/fixtures/messy.pretty.json +113 -0
  73. connect_flow-0.1.1/tests/fixtures/no_entry_point.json +33 -0
  74. connect_flow-0.1.1/tests/fixtures/style_4space +34 -0
  75. connect_flow-0.1.1/tests/fixtures/style_expanded.json +45 -0
  76. connect_flow-0.1.1/tests/fixtures/tiny.json +45 -0
  77. connect_flow-0.1.1/tests/fixtures/unreachable.json +56 -0
  78. connect_flow-0.1.1/tests/test_arn.py +105 -0
  79. connect_flow-0.1.1/tests/test_autoresolve.py +260 -0
  80. connect_flow-0.1.1/tests/test_aws.py +400 -0
  81. connect_flow-0.1.1/tests/test_cli.py +245 -0
  82. connect_flow-0.1.1/tests/test_cli_diff.py +232 -0
  83. connect_flow-0.1.1/tests/test_cli_diff_normalize.py +177 -0
  84. connect_flow-0.1.1/tests/test_cli_pull_list.py +564 -0
  85. connect_flow-0.1.1/tests/test_cli_remote.py +387 -0
  86. connect_flow-0.1.1/tests/test_cli_translate.py +723 -0
  87. connect_flow-0.1.1/tests/test_corpus.py +64 -0
  88. connect_flow-0.1.1/tests/test_diff.py +320 -0
  89. connect_flow-0.1.1/tests/test_digest.py +128 -0
  90. connect_flow-0.1.1/tests/test_graph.py +88 -0
  91. connect_flow-0.1.1/tests/test_import_drift.py +95 -0
  92. connect_flow-0.1.1/tests/test_layout.py +83 -0
  93. connect_flow-0.1.1/tests/test_lint.py +93 -0
  94. connect_flow-0.1.1/tests/test_mapping.py +306 -0
  95. connect_flow-0.1.1/tests/test_metrics.py +71 -0
  96. connect_flow-0.1.1/tests/test_render.py +48 -0
  97. connect_flow-0.1.1/tests/test_targets.py +101 -0
  98. connect_flow-0.1.1/tests/test_writer.py +128 -0
@@ -0,0 +1,24 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "Bash|Grep",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "graphify hook-guard search"
10
+ }
11
+ ]
12
+ },
13
+ {
14
+ "matcher": "Read|Glob",
15
+ "hooks": [
16
+ {
17
+ "type": "command",
18
+ "command": "graphify hook-guard read"
19
+ }
20
+ ]
21
+ }
22
+ ]
23
+ }
24
+ }
@@ -0,0 +1,10 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ uv.lock
5
+ *.egg-info/
6
+ build/
7
+ dist/
8
+ .pytest_cache/
9
+ *.bak
10
+ graphify-out/
@@ -0,0 +1,37 @@
1
+ default:
2
+ image: ghcr.io/astral-sh/uv:python3.12-bookworm-slim
3
+
4
+ stages:
5
+ - test
6
+ - publish
7
+
8
+ test:
9
+ stage: test
10
+ script:
11
+ # pygraphviz's wheel needs several system shared libs (expat, glib,
12
+ # ...) that bookworm-slim omits; installing the graphviz apt package
13
+ # pulls its whole correct dependency tree instead of chasing each
14
+ # missing .so one at a time.
15
+ - apt-get update && apt-get install -y --no-install-recommends graphviz
16
+ - uv sync --all-extras
17
+ - uv run pytest
18
+
19
+ publish:
20
+ stage: publish
21
+ rules:
22
+ - if: $CI_COMMIT_TAG
23
+ # PyPI trusted publishing (no stored secret): this job requests a short-
24
+ # lived OIDC token scoped to `pypi`; twine >=6.0 auto-detects it via the
25
+ # PYPI_ID_TOKEN env var and exchanges it for a PyPI API token itself when
26
+ # no credentials are passed - see https://docs.pypi.org/trusted-publishers/
27
+ # using-a-publisher/. One-time setup: on pypi.org -> Your projects ->
28
+ # connect-flow -> Settings -> Publishing, add a GitLab publisher for
29
+ # namespace `mludvig`, project `connect-flow`, workflow filename
30
+ # `.gitlab-ci.yml` (a "pending" publisher can be added before the project
31
+ # exists on PyPI at all, for the very first release).
32
+ id_tokens:
33
+ PYPI_ID_TOKEN:
34
+ aud: pypi
35
+ script:
36
+ - uv build
37
+ - uvx twine upload dist/*
@@ -0,0 +1,39 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v6.0.0
4
+ hooks:
5
+ - id: check-added-large-files
6
+ - id: check-case-conflict
7
+ - id: check-merge-conflict
8
+ - id: check-json
9
+ - id: check-yaml
10
+ - id: check-xml
11
+ - id: detect-aws-credentials
12
+ args: ["--allow-missing-credentials"]
13
+ - id: mixed-line-ending
14
+ args: ["--fix", "auto"]
15
+ - id: end-of-file-fixer
16
+ - id: trailing-whitespace
17
+
18
+ - repo: local
19
+ hooks:
20
+ - id: ruff-check
21
+ name: ruff check
22
+ entry: uv run ruff check --fix
23
+ language: system
24
+ types: [python]
25
+ pass_filenames: true
26
+
27
+ - id: ruff-format
28
+ name: ruff format
29
+ entry: uv run ruff format
30
+ language: system
31
+ types: [python]
32
+ pass_filenames: true
33
+
34
+ #- id: mypy
35
+ # name: mypy
36
+ # entry: uv run mypy src/
37
+ # language: system
38
+ # types: [python]
39
+ # pass_filenames: false
@@ -0,0 +1,15 @@
1
+ # Hook definitions published FOR OTHER REPOS to consume.
2
+ #
3
+ # The audience here is a repo that stores Amazon Connect contact flow JSON
4
+ # and wants it kept in the neutral layout.
5
+ #
6
+ # Consumers should narrow `files:` to wherever their flow JSON actually
7
+ # lives — the default below matches every .json in the repo.
8
+
9
+ - id: connect-flow
10
+ name: connect-flow
11
+ description: Auto-arrange Amazon Connect contact flow JSON without changing behaviour
12
+ entry: connect-flow
13
+ language: python
14
+ files: \.json$
15
+ args: [prettify, --check]
@@ -0,0 +1,18 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
+
6
+ ## [Unreleased]
7
+
8
+ ## [0.1.1] - 2026-09-10
9
+
10
+ - `pygraphviz` is now an optional `[graphviz]` extra instead of a required
11
+ dependency - `lint`, `diff` and `digest` no longer need it installed.
12
+ - Added PyPI release metadata (classifiers, license files, project URLs).
13
+ - Added a GitLab CI pipeline that runs tests on every push and publishes to
14
+ PyPI via trusted publishing on tagged commits.
15
+
16
+ ## [0.1.0] - 2026-09-03
17
+
18
+ Initial release.
@@ -0,0 +1,127 @@
1
+ # CLAUDE.md
2
+
3
+ Guidance for working on this repository.
4
+
5
+ ## What this is
6
+
7
+ A standalone CLI that rewrites Amazon Connect contact flow JSON block
8
+ *positions* into a readable auto-layout, guaranteeing no behavioural change.
9
+ See `README.md` for usage and `docs/design.md`, `docs/data-model.md`,
10
+ `docs/safety.md` for the reasoning — read those before changing `writer.py`,
11
+ `digest.py`, or `graph.py`, since each encodes a specific, previously
12
+ measured or tested invariant, not an arbitrary choice.
13
+
14
+ ## The one rule that matters most
15
+
16
+ `digest.py`'s semantic digest is the safety boundary. Any change to
17
+ `writer.py`, `flow.py`, or `digest.py` itself must preserve: digests are
18
+ invariant under any position/geometry change, and are sensitive to every
19
+ other field. If you're not sure whether a change preserves this, add a test
20
+ in `tests/test_digest.py` proving it before proceeding, not after.
21
+
22
+ ## Never re-serialise a flow file
23
+
24
+ `writer.py` patches position literals via regex, in place, and must never
25
+ route through `json.dumps` on a whole file. See `docs/safety.md` for why —
26
+ in short, it destroys the property that a rewrite's diff is exactly the
27
+ coordinates that changed.
28
+
29
+ ## Edge order into `dot` must never be re-sorted
30
+
31
+ `layout.py` feeds `graph.edges` to `pygraphviz` in the exact order
32
+ `graph.py` produced them (i.e., in designer port order), because
33
+ `ordering="out"` depends on insertion order as each node's port order. This
34
+ has regressed once already from an innocuous-looking `sorted(...)` — see the
35
+ comment in `layout.py` and `docs/design.md`'s "Edge order and weight" section
36
+ before touching this.
37
+
38
+ ## Test data must stay synthetic
39
+
40
+ This repo is open source and must never contain real customer data,
41
+ identifiers, or references to any specific deployment. Everything under
42
+ `tests/fixtures/` is hand-authored from scratch with throwaway UUIDs — see
43
+ `tests/fixtures/README.md`. Do not add a fixture derived from, or copy any
44
+ fragment of, a real flow export. `tests/test_corpus.py` exists precisely so
45
+ that real-corpus validation can happen locally (via `$CONNECT_FLOW_CORPUS`)
46
+ without ever needing to commit that corpus.
47
+
48
+ This rule isn't limited to fixtures: help text, error messages, docstrings,
49
+ version/snapshot descriptions, and doc examples are all committed text too,
50
+ and all of them handle real ARNs, account ids, instance ids and resource
51
+ names at runtime. Every example anywhere in this repo uses the one
52
+ placeholder set (`11111111-1111-…` instance, `22222222-…`/`33333333-…`
53
+ resource, account `000000000000`) — never a value copied from a real
54
+ console session or CLI run, even a redacted-looking one.
55
+
56
+ ## The CLI is six modules, not one
57
+
58
+ `cli.py` is the verb dispatch and the five verb runners only. The rest of
59
+ the CLI is layered beneath it, and imports flow one way — never back into
60
+ `cli.py`, which is what keeps the graph acyclic. `resolve.py` and `load.py`
61
+ are a parallel pair, not a chain — neither imports the other:
62
+
63
+ ```
64
+ args.py -> resolve.py -> remote_write.py -> pipeline.py -> cli.py
65
+ load.py ---------^
66
+ ```
67
+
68
+ - `args.py` — parser construction and cross-flag validation.
69
+ - `resolve.py` — strings to regions, clients, instance ids, resolved targets.
70
+ Never touches a flow's contents.
71
+ - `load.py` — an already-resolved target to a loaded `Flow`, wherever it
72
+ lives; the only code in the CLI layer that reads one. See
73
+ `docs/adr/0012-load-module-split.md` for why this is split from `resolve.py`
74
+ rather than folded into it.
75
+ - `remote_write.py` — confirm and upload; the only code that mutates a live
76
+ instance, kept isolated so it can be audited alone.
77
+ - `pipeline.py` — the shared transform-and-emit engine behind `prettify`,
78
+ `copy` and `normalize`.
79
+
80
+ New CLI code goes in the layer it belongs to, not in `cli.py` by default —
81
+ see `docs/adr/0011-cli-module-split.md` for why the file was split. Exit
82
+ codes come from `exits.py` directly in every module; `cli.py` only
83
+ re-exports them (along with `main_connect_flow`,
84
+ `build_connect_flow_parser` and `_classify_copy_endpoint`) so the console
85
+ script, `__main__.py` and the tests address one unchanged name.
86
+
87
+ ## `boto3` stays contained to `aws.py`
88
+
89
+ `src/connect_flow/aws.py` is the *only* module allowed to import
90
+ `boto3` or `botocore`. `boto3` is a required dependency (see
91
+ `docs/adr/0003-boto3-required-dependency.md` for why the optional `[aws]`
92
+ extra was dropped), but the containment still matters: `aws.py`'s job is to
93
+ be the only place that ever sees a `botocore.ClientError`, translating every
94
+ AWS failure into `AwsError` before anything above it can observe a botocore
95
+ type — and to keep `resolve.py`, `load.py`, `cli.py`, `targets.py`, `arn.py`
96
+ free of credential/region resolution details that belong to boto3, not to
97
+ this tool's own logic. `resolve.py` is where region and client resolution
98
+ now lives, so it is the module most likely to tempt you — it calls
99
+ `aws.get_default_region()`/`aws.client()` and must keep doing so rather
100
+ than reaching past them. `load.py` constructs `aws.RemoteFlowClient`
101
+ instances from a factory it's handed, for the same reason — it must keep
102
+ going through `aws.py` rather than reaching past it. If
103
+ you're adding remote-target code anywhere else and find yourself reaching
104
+ for `boto3` directly, that's a sign the operation belongs in `aws.py`
105
+ instead.
106
+
107
+ ## Commands
108
+
109
+ ```
110
+ uv sync
111
+ uv run pytest
112
+ uv run connect-flow --version
113
+ ```
114
+
115
+ No linter or type-checker is currently wired in; keep changes consistent
116
+ with the existing style (dataclasses, `from __future__ import annotations`,
117
+ docstrings that explain *why*, not what).
118
+
119
+ ## graphify
120
+
121
+ This project has a knowledge graph at graphify-out/ with god nodes, community structure, and cross-file relationships.
122
+
123
+ Rules:
124
+ - For codebase questions, first run `graphify query "<question>"` when graphify-out/graph.json exists. Use `graphify path "<A>" "<B>"` for relationships and `graphify explain "<concept>"` for focused concepts. These return a scoped subgraph, usually much smaller than GRAPH_REPORT.md or raw grep output.
125
+ - If graphify-out/wiki/index.md exists, use it for broad navigation instead of raw source browsing.
126
+ - Read graphify-out/GRAPH_REPORT.md only for broad architecture review or when query/path/explain do not surface enough context.
127
+ - After modifying code, run `graphify update .` to keep the graph current (AST-only, no API cost).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 connect-flow contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.