Skip to main content

connect-flow

A minimal IaC toolkit for Amazon Connect contact flows: promote a flow between environments, verify it, and keep its layout readable.

A CLI built around one idea: a contact flow JSON file in your repo should be an environment-neutral source of truth, and each environment (dev, test, prod) a rendering of it. copy/normalize translate the environment-specific IDs and ARNs baked into a flow so one file can be promoted anywhere; diff/lint/digest verify it along the way; prettify auto-arranges its block layout without changing its behaviour.


Why this exists

The problem. A contact flow exported from Amazon Connect is full of environment-specific identifiers baked directly into the JSON — Lex bot alias ARNs, Wisdom AI-agent ARNs, Lambda ARNs, sibling contact-flow/ flow-module/queue/prompt ARNs. Promoting a flow from dev to test to prod means finding and swapping every one of them, by hand, in a multi-thousand- line file — a process that's slow, error-prone, and leaves no record of which identifiers changed or why. And once a flow is edited in three places, there's no cheap way to tell whether the copy in git still matches what's actually deployed.

The solution. connect-flow copy/normalize translate a flow's identifiers against a per-environment map file, so one JSON file in git becomes the environment-neutral template and each environment a rendering of it — see Promoting a flow between environments below. diff/lint/digest answer "has this drifted" and "is this well-formed" without a console round trip. And because flows in git get edited by several people, their layout rots too — crossing edges, error paths visually competing with the happy path, blocks wherever they happened to land mid-edit — which is what prettify is for.

Every rewrite this tool makes, whether it's an identifier or a coordinate, goes through the same discipline: patch only the literals that are supposed to change, in place, with a targeted regex — never json.dumps over the whole document, which would rewrite every line and bury any accidental behavioural change inside an unreadable diff. A semantic digest (a hash of everything behavioural) is taken before and after every write; if it doesn't match what the operation is supposed to produce, nothing is written and the tool reports a safety violation instead. See docs/safety.md for the full mechanism.

So a layout rewrite's diff is exactly the numbers that moved:

       "ActionMetadata": {
         "560e67f4-4322-4788-831e-e49c2a0e7dd2": {
-          "position": { "x": 1284, "y": 61 },
+          "position": { "x": 240, "y": 120 },
           "children": ["d3ee852c-65de-4180-95a6-c8bd1f10b5bf"]
         },

And the layout itself goes from this to this:

   BEFORE — edited by hand over a year    AFTER — one command

   [Play prompt]        [Disconnect]      [Entry]
        │   ┌────────────────┘  ▲            │
   [Entry]  │   [Error handler] │            ▼
        └───┼────┐      │  ▲    │         [Get input] ──► [Compare] ──► [Play prompt]
   [Compare]│  [Get input]─┘    │              ╷              ╷               │
        │   └──────┘     └──────┘              └┄┄┄┄┄┬┄┄┄┄┄┄┄┄┘               ▼
        └───────────────────────┘              [Error handler]          [Disconnect]

   happy path zig-zags, error paths       happy path straight and left-to-right,
   cross it and each other                error paths pushed below, out of the way

What you get

connect-flow uses git-style subcommands. Every one of them takes a TARGET that is either a local path or a live flow / flow-module ARN, interchangeably — so diff-ing a file in your repo against what's actually running in production is one command, not a download step plus a comparison step.

subcommand purpose
connect-flow copy --from SOURCE --to DEST [--from-map FILE] [--to-map FILE] [--apply] promote a flow 1:1, translating identifiers through an intermediate <<...>> token form; a live --to is previewed unless --apply
connect-flow normalize TARGET... [--reverse] [--map FILE] the strictly offline form of the same translation — local files only, no AWS
connect-flow prettify TARGET... rewrite block positions into a readable layout
connect-flow diff TARGET_A [TARGET_B] semantic diff between two targets, local and/or remote
connect-flow lint TARGET... structural checks: dangling transitions, unreachable actions, empty branches
connect-flow digest TARGET... print each target's semantic digest — "is my file still what's live?"
connect-flow list [--instance ID-OR-ALIAS --flows|--flow-modules] list instances, or flows/flow modules within one, or describe one via --flow/--flow-module

Without --from-map/--to-map, copy is a byte-verbatim transfer — fetch a remote flow to disk, or upload a local file to a remote flow, with no identifier translation at all.

--profile/--region/--json/--quiet are accepted either before or after the subcommand, aws-cli style: connect-flow --profile lab --region us-east-1 list and connect-flow list --profile lab --region us-east-1 are equivalent.

How a run works

  SOURCE (--from)                                            DEST (--to)
  ───────────────                                             ───────────
                                                            ┌─► local file  (+ .bak)
  local file ─┐                                             │
              ├─► parse ─► translate/layout ─► patch text ──┼─► live ARN, preview only
  live ARN  ──┘     │      (from-map/to-map,   (regex,      │   (--apply to write)
                    │       dot)                in place)   │
                    └──► digest ── compare ──┘               └─► stdout (--emit-map only)
                                      │
                                   mismatch ───► refuse to write, exit 2

prettify is a different translate/layout step over the same pipeline — geometry only, no identifier maps — with the same digest safety gate.

Promoting a flow between environments

copy and normalize are two thin argument shapes over one engine: read a flow, substitute identifiers against a map file, optionally re-layout, write it somewhere. They differ only in which sources/destinations are legal:

verb source destination touches AWS
copy local, live ARN, or INSTANCE/NAME (--from) local file, or live (--to, previewed unless --apply) reads (+ writes with --apply)
normalize local file only local only never

copy translates in one direction per map: --from-map normalises --from's real identifiers to <<...>> placeholders, --to-map resolves those placeholders to --to's real identifiers — both are optional and independent, so a single copy can do either half, both, or neither (a verbatim transfer). normalize instead takes one map and a --reverse flag, since it always has exactly one local file on both ends.

The map file

One JSON file per environment, in the shape {"<<ARN:Some Name>>": "arn:aws:...", "<<ID:Some Name>>": "22222222-..."} — flat, or grouped into named sections ({"lexv2": {...}, "others": {...}}); both are accepted, and a grouped file's section names are preserved when a skeleton is emitted. A plain, non-<<...>> key such as IVR_BOT_AGENT_ID works too — a placeholder is whatever the map says it is, not only a <<...>> token.

Bootstrap a map for a flow that doesn't have one yet

--emit-map discovers every ARN-shaped literal and documented bare-id field in a flow and writes them as a map — nothing is written to the flow itself. Each entry is named automatically wherever a name is available: a Lambda ARN's function name is parsed straight out of the ARN (works even from normalize, which never touches AWS); a ContactFlowId/FlowModuleId/QueueId is resolved to its live Name via a Describe call against copy's --from instance (turned off with --no-auto-resolve, same flag as live-to-live copy). Anything with no name available — like the Lex bot-alias ARN below — falls back to a <<ARN:RENAME-ME-HASH>>/<<ID:RENAME-ME-HASH>> key, HASH a short hex digest of the value itself (so the key a given value gets doesn't depend on the order flows/modules were fed into --emit-map).

--to is still required but never written to in this mode:

$ connect-flow copy --from contact-flows/main-ivr.json --to flows/main-ivr.json \
    --emit-map maps/dev.json
$ cat maps/dev.json
{
  "discovered": {
    "<<ARN:RENAME-ME-8ed6922a>>": "arn:aws:lex:ap-southeast-2:000000000000:bot-alias/22222222-2222-2222-2222-222222222222/33333333-3333-3333-3333-333333333333"
  }
}

Rename any remaining RENAME-ME keys by hand to something meaningful — the edited file is that environment's map. Running --emit-map again against the same FILE — the natural shape of a loop, once per downloaded flow or module — merges in, rather than overwrites: existing entries and groups are kept as-is, and only genuinely new values are appended, so the file accumulates into one shared map across the loop.

dev → committed template → test

# dev's live flow -> an environment-neutral file, committed to the repo
connect-flow copy --from arn:aws:connect:ap-southeast-2:000000000000:instance/1111.../contact-flow/2222... \
    --from-map maps/dev.json --prettify --to flows/main-ivr.json --profile dev

# template -> test instance (maps/test.json holds test's real values for the same keys);
# previewed first, applied once the diff looks right
connect-flow copy --from flows/main-ivr.json --to-map maps/test.json \
    --to arn:aws:connect:ap-southeast-2:000000000000:instance/1111.../contact-flow/2222... \
    --profile test
connect-flow copy --from flows/main-ivr.json --to-map maps/test.json \
    --to arn:aws:connect:ap-southeast-2:000000000000:instance/1111.../contact-flow/2222... \
    --profile test --apply --publish --yes

copy --to, whether previewing or applying, refuses (exit 2) if the destination's live content no longer normalises back to the source's template form through --to-map — i.e. someone edited it outside this pipeline since it was last copied — unless --force.

Offline: re-point or CI-gate a local file, no credentials needed

connect-flow normalize flows/main-ivr.json --reverse --map maps/test.json -o /tmp/rendered.json
connect-flow normalize contact-flows/*.json --map maps/dev.json --check    # CI gate

normalize never touches AWS on its own code path — no credentials, no network calls — which is what makes it the right verb for a pre-commit hook or a CI job that has no AWS credentials at all.

Unresolved identifiers are a hard error

If a template still contains a <<...>> placeholder — or the resolve/ normalise direction leaves any of the map's own literals in place — after translation, that's refused (exit 2), not silently written: a template with a leftover placeholder is exactly the failure mode this feature exists to catch. --force downgrades it to a warning.

See docs/adr/0001-cross-environment-id-mapping.md for the design decisions behind this (placeholder style, map format, the duplicate-value and substring-shadowing rules), docs/adr/0009-copy-verb.md for copy's two-pass design, and docs/safety.md for how the rewrite itself is proven safe.

prettify

Run connect-flow prettify --help for all options.

Example uses:

Reformat all contact-flows in a directory in-place

connect-flow prettify contact-flows/*/*.json --in-place

What a reformat actually looks like

Here's tests/fixtures/messy.json in the Connect designer, before and after connect-flow prettify — same flow, same behaviour, same digest, only the block positions rewritten:

Before — the happy path doubles back on itself and the error branches cross it:

before

After — the happy path runs left to right along the top, error branches drop below it:

after

Use --render to see that before/after without opening the console

--render writes a STEM.before.png / STEM.after.png pair — connect-flow's own sketch of the graph, drawing the blocks where the rewrite will put them. It combines with --check/--dry-run, which write nothing at all, so you can look before letting the tool near the file:

connect-flow prettify contact-flows/my-flow.json --dry-run --render --render-dir /tmp/renders

The report says whether the layout actually got better, in numbers:

$ connect-flow prettify tests/fixtures/messy.json --dry-run --render --render-dir /tmp/renders
CHANGED tests/fixtures/messy.json
    read: tests/fixtures/messy.json
    wrote: rendered
    before: crossings=2 backward_happy_edges=4 overlaps=0 canvas=1670x1020
    after : crossings=0 backward_happy_edges=0 overlaps=0 canvas=1050x680

Edge crossings and backward happy-path edges both to zero, on the same flow pictured above.

diff

Run connect-flow diff --help for all options.

diff compares two targets semantically — it answers "does this behave differently", not "are these files byte-identical". Layout, key order and designer churn are normalised away first (see Flow normalisation explained below). Either side can be a local path, a live ARN, or INSTANCE/NAME-OR-ID addressing (--kind {flow,flow-module}, default flow, disambiguates which resource the name refers to — see docs/adr/0010-diff-instance-addressing.md), so file↔file, file↔live and live↔live all work the same way. It exits 1 when the two differ, which makes it usable as a CI check.

Example uses:

See what actually changed between two flows

Here tiny-edited.json is a copy of the fixture with one prompt reworded:

$ connect-flow diff tests/fixtures/tiny.json tiny-edited.json
A: tests/fixtures/tiny.json
B: tiny-edited.json
  ~ e5565ba7-ebc9-43da-8d9e-94412c33ab4b

--- e5565ba7-ebc9-43da-8d9e-94412c33ab4b (tests/fixtures/tiny.json)
+++ e5565ba7-ebc9-43da-8d9e-94412c33ab4b (tiny-edited.json)
@@ -1,7 +1,7 @@
 {
   "Identifier": "e5565ba7-ebc9-43da-8d9e-94412c33ab4b",
   "Parameters": {
-    "Text": "Hello!"
+    "Text": "Hello, welcome to support!"
   },
   "Transitions": {
     "Conditions": [],

A per-Identifier summary (+ added, - removed, ~ changed) followed by a unified diff of just the affected blocks — rather than of the whole file, where one edited prompt would be buried among thousands of unchanged lines. --json emits the same structurally.

Confirm a reformat changed nothing

Two files with different layouts and identical behaviour are identical to diff, which is the whole point:

$ connect-flow diff tests/fixtures/messy.json tests/fixtures/messy.pretty.json
identical: tests/fixtures/messy.json == tests/fixtures/messy.pretty.json

Has my repo drifted from what's really deployed?

connect-flow diff contact-flows/main-flow.json \
    arn:aws:connect:ap-southeast-2:000000000000:instance/1111.../contact-flow/2222... \
    --profile prod

Compare the same flow across two environments by name

INSTANCE/NAME-OR-ID addressing (ADR 0008) works on either side, avoiding the need to look up ARNs by hand:

connect-flow diff \
    dev-instance/"CSC Support Flow" \
    prod-instance/"CSC Support Flow" \
    --profile prod

Did someone leave an unpublished draft in the console?

--both-stages compares one flow's saved draft against its own published content — a single ARN, no second target:

connect-flow diff arn:aws:connect:ap-southeast-2:000000000000:instance/1111.../contact-flow/2222... \
    --both-stages --profile prod

Compare a Terraform template against the deployed flow

A .json.tftpl is full of ${...} placeholders that will never match the resolved ARNs in the live flow. --normalize-opaque replaces every placeholder and every ARN-shaped literal with one <opaque> token before comparing, so the structural comparison still works:

connect-flow diff contact-flows/main-flow.json.tftpl \
    arn:aws:connect:ap-southeast-2:000000000000:instance/1111.../contact-flow/2222... \
    --normalize-opaque --profile prod

It's off by default because it deliberately hides a real class of bug — the wrong ARN wired into a block — so reach for it only when comparing a template to its rendered result.

Compare the same flow lineage across two instances

A flow copied from one instance to another keeps its own identity, but every ContactFlowId/FlowModuleId/QueueId it references — an "Invoke Flow Module" block, for example — gets a fresh id in the new instance, even when the referenced flow/module/queue is genuinely "the same one" by name. --normalize-opaque also resolves these against each target's own instance (a live Describe* call), replacing the id with <<FlowModule:Name>> (etc.) — so two references that both resolve to the same Name stop looking like a diff, while a reference that resolves to a different Name still is one:

connect-flow diff arn:aws:connect:ap-southeast-2:000000000000:instance/1111.../contact-flow/2222... \
    arn:aws:connect:ap-southeast-2:000000000000:instance/3333.../contact-flow/4444... \
    --normalize-opaque --profile prod

This only applies to a remote (ARN) target — there's no instance to resolve a bare id against for a local file. A reference id that can't be resolved (deleted resource, no permission) is left as-is and a warning is printed, rather than silently hidden.

lint and digest

Run connect-flow lint --help / connect-flow digest --help for all options. Both are strictly read-only — there is no writer path in either, so neither can ever modify a flow, local or live.

Example uses:

Catch broken wiring before it reaches Connect

lint reports dangling transitions (a NextAction/Conditions[]/Errors[] entry pointing at an Identifier that doesn't exist), blocks unreachable from StartAction, a missing StartAction, and Compare/DistributeByPercentage blocks with no conditions at all. It exits 1 if any target has findings:

$ connect-flow lint tests/fixtures/unreachable.json
ISSUES tests/fixtures/unreachable.json
    unreachable: '4008265c-66ba-4495-b465-9c586352ff0b' is not reachable from StartAction
    unreachable: '41b9ba6c-fef3-48dd-849c-425afcd58a7c' is not reachable from StartAction

$ connect-flow lint tests/fixtures/messy.json
OK     tests/fixtures/messy.json

These are the mistakes a hand-edited or templated flow makes and the console never shows you, because you only find out when a call falls off the end of a branch at 2am.

Answer "is my file still what's live?" in one line

digest prints the semantic hash of each target — the same normalised form diff compares — so a whole flow reduces to one comparable string:

$ connect-flow digest tests/fixtures/messy.json tests/fixtures/messy.pretty.json tests/fixtures/tiny.json
2afe14e5f2b6ec64122ab9e03e866d343a96ce43ff4f259ece5d166b988cf1b1  tests/fixtures/messy.json
2afe14e5f2b6ec64122ab9e03e866d343a96ce43ff4f259ece5d166b988cf1b1  tests/fixtures/messy.pretty.json
50c2bbe077f06890311f33d13d9ece8d39290b957d685eeb83c52d8fddfae72d  tests/fixtures/tiny.json

The first two are the messy and prettified versions of one flow: different files, different layouts, one digest. That's the property the whole tool rests on.

Mixing local and remote targets in a single invocation is the quick "did anything drift" sweep — no diff to read, just two strings to compare:

connect-flow digest contact-flows/main-flow.json \
    arn:aws:connect:ap-southeast-2:000000000000:instance/1111.../contact-flow/2222... \
    --profile prod

Both accept --stage {published,saved} (default: published) for remote targets, and the same --instance/--flow/--flow-module addressing as prettify.

Flow normalisation explained

diff and digest both work on one canonical form of a flow rather than on its raw JSON, and prettify uses the digest of that same form as its safety check. Normalisation is what makes all three trustworthy: without it, a flow re-saved in the console with no edit at all would look "changed" every time, and a tool that cries wolf on every save gets ignored.

What gets normalised away, and why none of it is behavioural:

normalised why it isn't a behavioural difference
all geometry — position, entryPointPosition, annotation size pure layout; the whole point of prettify is to change exactly this and nothing else
Actions[] array order Connect dispatches by Identifier reference, not array position, so the order blocks appear in the file means nothing
JSON key order and formatting whitespace and key ordering carry no meaning to Connect
conditionMetadata[].id on Compare/DistributeByPercentage designer display decoration with no counterpart in Transitions.Conditions[], and Connect regenerates it on every save even with no edit
a compound block's hidden child Identifier (diff only) e.g. inside a "Create Wisdom Session" block; re-randomised on every save, so diff aliases it to <parent-identifier>#<index> and compares that
${...}/<<...>> placeholders and ARN literals (diff --normalize-opaque, opt-in) lets a Terraform template, or a copy/normalize token-form template, be compared to its rendered result; off by default because it also hides a genuinely wrong ARN
ContactFlowId/FlowModuleId/QueueId referenced from inside a flow, resolved to the referenced resource's Name (diff --normalize-opaque, remote targets only) these ids aren't stable across instances, only the Name is — see ADR 0007
the whole designer layer — ActionMetadata, its key order, Annotations, snapToGrid (diff --functional, opt-in) affects only how the console draws the flow; it's rewritten on every save, so two snapshots of an unedited flow always differ here — see ADR 0016

The last two rows are orthogonal axes and compose — --functional chooses which fields are compared, --normalize-opaque chooses how values inside the surviving fields are matched:

# same instance: is the saved draft functionally the published flow?
connect-flow diff 'arn:...:contact-flow/2222...' 'arn:...:contact-flow/2222...:$SAVED' --functional

# across instances: same logic, different queue/flow ids either side
connect-flow diff dev.json 'arn:...:contact-flow/3333...' --functional --normalize-opaque

What is deliberately not normalised, because it is behavioural:

  • Transitions.Conditions[] order — Connect evaluates conditions first-match, so reordering two branches changes which one runs.
  • ActionMetadata key order — not behavioural at runtime, but it's tracked anyway in the digest (as a separate ActionMetadataOrder field), because the writer must prove its own rewrite didn't disturb it. Pass diff --functional to leave it, and the rest of the designer layer, out of a comparison between two independently-saved flows.
  • Everything elseParameters, block Type, StartAction, Settings, annotations' text, and any unrecognised top-level Metadata key are all carried into the canonical form verbatim. The digest is sensitive to every one of them by design; see docs/safety.md for how that's tested rather than merely intended.

Working against live Amazon Connect instances

connect-flow extends the same "provably no behavioural change" guarantee across the network: it can fetch a flow or flow module straight from a live Connect instance, prettify it, and push it back, with a rollback point and post-write verification.

AWS credentials

Use the usual --profile and/or --region cli parameters, or AWS_* environment variables.

connect-flow list on lists the Connect instances visible to those credentials; add --instance plus --flows or --flow-modules to list resources within one instead:

connect-flow list --region us-east-1 --profile lab

connect-flow list --instance 11111111-1111-1111-1111-111111111111 \
    --flow-modules --region us-east-1 --profile lab

--instance accepts either a bare instance id or an alias, resolved via an exact (case-sensitive) name match — no separate flag for each. Pass --flow/--flow-module to list instead to describe that one flow — its ARN, description, version and last-modified time (including the instance it lives in) — rather than enumerating everything in the instance; each also accepts either a bare id or a name, resolved the same way:

connect-flow list --instance 11111111-1111-1111-1111-111111111111 \
    --flow "Main Menu" --region us-east-1 --profile lab

A remote target can also be addressed via --instance plus a repeatable --flow/--flow-module, instead of a full ARN, with --region required in that form:

connect-flow prettify --instance lab-instance \
    --flow-module "Main Menu" --region us-east-1 \
    --dry-run --diff --profile lab

--flow/--flow-module also accept a full ARN, or a value of the form INSTANCE/NAME-OR-ID that names its own instance (id or alias) — useful for addressing a flow in a different instance than --instance, or without --instance at all:

connect-flow list --flow "lab-instance/Main Menu" --region us-east-1 --profile lab

A remote write always defaults to the unpublished draft (:$SAVED), never the live content — pass --publish to write live instead:

# writes a draft only, with a rollback snapshot; live content is untouched
connect-flow prettify arn:aws:connect:us-east-1:000000000000:instance/1111.../flow-module/2222... \
    --in-place --profile prod

# writes the live content, unattended
connect-flow prettify --instance 1111... --flow-module 2222... --region us-east-1 \
    --in-place --publish --yes --profile prod

Every remote write is guarded four ways:

  1. version snapshot   immutable pre-change copy   (--snapshot before|after|both|none)
  2. sha256 check       taken immediately before the write — refuses on conflict
  3. read-back verify   re-reads what Connect stored, recomputes its digest
  4. confirmation       mandatory (--yes) rather than skippable off a TTY

copy --to additionally refuses to upload a file whose digest has drifted from the destination's live content — a straight digest compare for a map-less (byte-verbatim) copy, or the reverse-normalisation drift check described above when --to-map is given — unless --force. See docs/safety.md for the full detail, including the residual race the sha check narrows but can't close.

The pre-change snapshot depends on CreateContactFlow{,Module}Version, which Connect only actually supports for Campaign-type contact flows — for anything else it fails outright, and the write is refused with a pointer to --local-backup DIR (writes the pre-change content to DIR/STEM.json first, no dependency on that API) or --force (proceeds with no rollback point at all).

The remote-write guardrail flags (--yes, --force, --publish, --no-verify, --snapshot, --local-backup) have no short forms — a remote write is never one keystroke. Plain I/O-destination flags like --output/-o aren't subject to that: they don't touch a live instance by themselves, so there's no safety reason to make them harder to type.

Install

pipx install connect-flow

Requires Python 3.11+. The base install only needs boto3, for live-instance support — boto3 needs no configuration to run normalize/digest/lint on local files or diff on two local files, since those never make an AWS call.

prettify, copy --prettify/normalize --prettify, and --render also need pygraphviz to compute a layout; install it with the graphviz extra:

pipx install "connect-flow[graphviz]"
# or, if connect-flow is already installed:
pipx inject connect-flow pygraphviz

pygraphviz's wheel bundles the Graphviz libraries, so no system Graphviz install is needed either way. Running a layout command without the extra installed fails cleanly with an error telling you to install it.

Use it as a pre-commit hook in your flows repo

If you keep exported contact flow JSON in a git repo, connect-flow can run as a pre-commit hook and fail the commit whenever a flow's layout has drifted, so the tidy layout is the state the repo is always in, rather than something someone remembers to run.

This is set up in the repo that holds the flow JSON, not here. connect-flow ships a .pre-commit-hooks.yaml, which is the manifest offering the hook to other repos to install.

Add this to your flows repo's own .pre-commit-config.yaml:

- repo: https://gitlab.com/mludvig/connect-flow
  rev: master
  hooks:
    - id: connect-flow
      files: ^contact-flows/.*\.json$
      ## Uncomment to enable in-place re-formatting
      # args: [prettify, --in-place]

Set files: to wherever your flow JSON actually lives. The hook's default pattern matches every .json in the repo, which will also feed it package.json and friends.

By default the hook runs connect-flow prettify --check: it writes nothing and fails the commit if a flow file would be reformatted — well suited to a CI gate too.

Exit codes

code meaning
0 success — nothing changed, or a write succeeded
1 --check/diff/lint only: at least one target would change, differs, or has findings
2 flow safety violation (would-be behavioural change), or a target failed to parse / isn't a contact flow
3 bad usage (bad flags, missing args, a directory passed as a target)
4 an AWS/remote failure — credentials, access denied, a sha256 conflict, or a resource that doesn't exist

Going deeper

  • docs/design.md — why Graphviz dot rather than a hand-rolled layout engine, with the edge-crossing measurements that settled it.
  • docs/data-model.md — how a flow's ports and happy/error paths are modelled.
  • docs/safety.md — exactly what guarantees "never changes behaviour", and how each one is enforced rather than merely asserted.

License

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

connect_flow-0.1.1.tar.gz (204.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

connect_flow-0.1.1-py3-none-any.whl (88.3 kB view details)

Uploaded Python 3

File details

Details for the file connect_flow-0.1.1.tar.gz.

File metadata

  • Download URL: connect_flow-0.1.1.tar.gz
  • Upload date:
  • Size: 204.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.12.12

File hashes

Hashes for connect_flow-0.1.1.tar.gz
Algorithm Hash digest
SHA256 a846eb437fe353feb39443ea8a8585dd6cbe9472de6b90653778321be26011a9
MD5 f7d994364429d4e05c5bd8e1b8f8540a
BLAKE2b-256 e6295b7569f20aacdaa2e724e43a491f9a969ed6ffc99aa6508882ee0113e6a5

See more details on using hashes here.

File details

Details for the file connect_flow-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: connect_flow-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 88.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.12.12

File hashes

Hashes for connect_flow-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 8bb3033f7bc80d2f9a29b478301e6186ffe6cfe77eb2e6a945043b582ec9ad43
MD5 fe3a0fcf4d5777a0a14aa027b62b9209
BLAKE2b-256 c9daa571d13dfdbda48a4104692cfaf11203a1d61dabb4c242e02661e68c37de

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page