Skip to content

Releases: winterop-com/dirigent

v0.23.3

Choose a tag to compare

@mortenoh mortenoh released this 07 Oct 12:32
v0.23.3
6a3bbf8

Released 2026-10-07. Every package in the workspace moves to 0.23.3 together, and so do
dirigent-dhis2 and dirigent-integration. Nothing in this repository changes; the release
exists for the two siblings.

  • The DHIS2 examples point at a play demo that answers. stable-2-43-1 is gone from
    play.im.dhis2.org and stable-2-43-2 lists no organisation units, so the example pipelines
    dirigent-dhis2 ships in its wheel, its tutorial, and the pipelines in
    dirigent-integration move to stable-2-42-6. On 2.42.6 a rehearsed import reports what it
    would import as well as update, since the demo does not hold the values 2.43.1 did; a dry
    run still writes nothing.

v0.23.2

Choose a tag to compare

@mortenoh mortenoh released this 07 Oct 09:32
v0.23.2
b65d4cd

Released 2026-10-07. Every package in the workspace moves to 0.23.2 together, and so do
dirigent-dhis2 and dirigent-integration.

  • SQLAlchemy 2.1. dirigent-core and dirigent-block-sql require sqlalchemy>=2.1.3, so
    an environment holding SQLAlchemy 2.0 beside dirigent no longer resolves. Nothing about the
    schema or the queries changes; the claim, the run report and the drift check are typed to
    2.1's generic Select and Table.
  • A request is one span, dirigent's. FastAPI 0.142 instruments itself whenever a tracer
    provider is installed, which put a second server span between dirigent's request span and the
    run it created, and would have added exporters from the environment and logged exception
    messages beside configure_telemetry. The server turns FastAPI's own telemetry off, and
    dirigent-server requires fastapi>=0.142.2.
  • The dependencies move. FastAPI 0.142, Starlette 1.7, Uvicorn 0.54, OpenTelemetry 1.45,
    cryptography 50.0.2 and DuckDB 1.5.6 among the Python ones; React Flow 12.12, Vite 8.3.3 and
    oxlint 1.87 in the UI.
  • The dashboard's chart ends when its runs were read. It took the hour of every render, so
    the axis could move under rows that had not been read again; it now takes the moment the
    day's runs answered.
  • dirigent-dhis2 reads DHIS2 2.44. It requires dhis2w-client>=1.31.1, a release
    that recognises a 2.44 instance, as a preview, rather than refusing it as unsupported.

v0.23.1

Choose a tag to compare

@mortenoh mortenoh released this 01 Oct 10:02
v0.23.1
c569e33

Released 2026-10-01. Every package in the workspace moves to 0.23.1 together, and so do
dirigent-dhis2 and dirigent-integration.

  • The pipelines naming a stored schema are a count that opens. They were a list under the
    Schema label, so a schema ten pipelines named pushed Save and Delete off the screen; the row
    above the body now states the number at every count and the names are one press away. The body
    pane is shorter for it, and the window still draws the whole shape.

v0.23.0

Choose a tag to compare

@mortenoh mortenoh released this 01 Oct 05:07
v0.23.0
f45c1a9

Released 2026-10-01. Every package in the workspace moves to 0.23.0 together, and so do
dirigent-dhis2 and dirigent-integration.

  • A run holds the shapes it started with. When a run is created it reads the body of every
    schema its document names and keeps those bodies on the run, the way it keeps its resolved
    parameters, its priority and its worker tags. Every attempt, every later step and every retry
    check against those bodies rather than the table, so an edit reaches the next run of a pipeline
    naming the code and never one already under way. A ${...} in a step's schema is refused when
    the document is applied: a code that is not a code until the run is under way is one nothing else
    can see either, neither the requires preflight nor the dependants a delete is refused for.
  • A schema in use is not changed blind. Deleting a schema a current pipeline names is refused;
    editing one never is, because a code is fixed once minted and refusing the edit would freeze an
    in-use schema for good. The dependants are read from the documents themselves, which already
    declare them, so nothing has to be kept in step.
  • A stored schema is corrected where it is read. The Schemas screen edits name, description and
    body; the apply dialog's differs row offers the same correction in place; and dg schema update
    does it from a shell, taking a body the way create does. A label given empty is cleared and one
    left out is left alone -- which dg could not express before, so seeding quietly wiped the title
    and description of every schema it replaced.
  • A missing connection is minted in the row that says so. The apply dialog's offer opens the
    connection form under the row rather than sending the reader to another screen and losing the
    document they had typed.
  • A label says only what is known. Messages that asserted what the code could not tell --
    that a password was unchanged when the request never answered, that parameters were a JSON object
    at the moment they were not -- now say what is true. Every counted sentence carries both its forms,
    so nothing renders "1 items"; every noun the CLI and the UI both say is held to one word by a test;
    and a lane fails documentation that quotes a label the catalogue no longer has.
  • A form is not hidden under its own cap. Three dialogs capped an inner block and hid the form
    inside it, two of them standing taller than the window with nothing to scroll. A surface caps and
    its body scrolls; only a read-only preview takes a height of its own, and that height belongs to
    the block rather than its callers.
  • An unread catalog is not an empty one. The step menu answered "No block matches that." while
    it was still reading the catalog, so a reader who typed quickly was told the block they wanted did
    not exist and their Enter placed nothing.

v0.22.0

Choose a tag to compare

@mortenoh mortenoh released this 30 Sep 09:24
v0.22.0
e74471b

Released 2026-09-29. Every package in the workspace moves to 0.22.0 together, and so do
dirigent-dhis2 and dirigent-integration.

  • Every word the UI says has a code. lib/labels.ts holds them all, one nested object
    whose path is the code, built the way dirigent_common.messages is built: a catalogue per
    area, a duplicate refused by the compiler rather than at import, and a sentence with a value
    in it written as a function with named parameters. A translation is a second table,
    const nb: typeof LABELS, and the compiler fails a missing code, a stray one, or a sentence
    taking different parameters. scripts/check_ui_labels.py runs in make static and fails a
    user-visible literal outside the catalogue, or a label nothing reads.
  • A refusal renders in the language of the surface that draws it. The wire carries the code
    and the params; each surface renders its own sentence. The browser reads detail only as the
    fallback for a code it does not know. RunOut, ItemOut, StepReport, RunReport,
    LogEntryOut, AlertMessage and RunSnapshot carry what a renderer needs, and a log entry
    holds its code in a column rather than inside fields.
  • A pack contributes the wording its codes are rendered from. labels is the seventh
    Contribution surface, a list of catalogues gathered by the host and served from
    GET /labels, so a pack's refusal reaches a browser in the browser's language. The nine
    in-tree families contribute 152 codes. The field defaults to empty, so a pack that does not
    set it loads unchanged.
  • A refusal a person reads is written for that person. An uncaught block error no longer
    reaches the run view as a Python class name: the sentence says the block failed and points at
    the step's log, which carries what it raised. Six messages that told a browser user to type a
    command say what is wrong instead, and a test walking every catalogue fails a new one.
    Eleven cancellations that were bare English now carry codes.
  • One word per concept. A schedule's firing was spelled nine ways, queued four; a column
    and the fact beside it disagreed on case in seven places. Each is one entry now, with its
    surface forms under it. test_shared_words.py fails the CLI and the UI drifting apart on a
    word they both say.
  • A pack can check its own wording. dirigent_testing.check_pack_messages reports a refusal
    raised under no code, a message nothing reads, and a code two catalogues both define. It is
    opted into with one line, like the package's other assertions.
  • The mono face is JetBrains Mono, self-hosted, with the same advance as what it replaces.
  • One inset per surface. 16px, stated once and spent everywhere; a listing row that fills
    the panel takes it back when pressed again; a dialog draws one control that dismisses it.

v0.21.0

Choose a tag to compare

@mortenoh mortenoh released this 26 Sep 22:33
0301a68

Released 2026-09-27. Every package in the workspace moves to 0.21.0 together, and so do
dirigent-dhis2 and dirigent-integration.

  • A watch keeps a sensor waiting. A third trigger beside schedules and webhooks, declared
    under triggers: watches: in a pipeline document or a kind: triggers document. A watch
    keeps exactly one run of its pipeline waiting on one root sensor step; when that step
    succeeds, the same transaction arms the next run, and the run that succeeded carries on
    downstream. Runs overlap by design: one batch is being transformed while the next is being
    waited for. A run that ends without its sensor succeeding re-arms from the last good point
    after a backoff that doubles from watch_backoff to watch_backoff_max. Apply refuses a
    watch on a step that is unknown, not a root, fans out, is not a sensor, or sits in a
    replace pipeline, and a later version that would break one.
  • The cursor belongs to the watch. A sensor's success can say where it left off:
    Sensor.resume_cursor(output), with a default of none, so every existing sensor starts each
    run fresh as before. The watch stores what it returns and seeds the next run with it, which
    the block reads through ctx.cursor. kafka.consume continues its offsets across runs with
    no consumer group, and playground.arrive numbers each batch on from the last. Pausing
    keeps the cursor, and resuming arms a run from it. dirigent-testing gains carry_cursor,
    which hands a sensor's next poke what the engine would.
  • dg watch lists, shows, pauses and resumes watches, writing watch, watch.paused and
    watch.resumed records; the API has the same four routes under
    /pipelines/{code}/triggers/watches. dg dev --seed, dg apply --paused and
    dg run --local create watches paused, so a demo or a single local run is never competing
    with one.
  • The Triggers screen lists watches beside schedules and webhooks, with their state,
    cursor, failures and last error, and so do the pipelines listing and the pipeline tab.
  • Two documents teach it: triggers/watch-a-sensor.yaml over playground.arrive, runnable
    with no network, and queues/kafka-watch-then-transform.yaml over a real topic.
  • The New connection form suggests the playground as its example code, not a service the
    corpus has left.

v0.20.0

Choose a tag to compare

@mortenoh mortenoh released this 26 Sep 16:36
v0.20.0
af393d6

Released 2026-09-26. Every package in the workspace moves to 0.20.0 together, and so do
dirigent-dhis2 and dirigent-integration.

  • The corpus runs on its own instance. Every example and every page of the documentation
    is off postman-echo.com. A document that needed something to happen -- a list to fan out
    over, a step that fails twice, a delay against a timeout, a payload over the storage
    threshold -- now says so with the playground.generate node, so it runs offline and under
    dg run --local with no connection and no URL. A document that is teaching http.request
    itself still makes a real call, to this instance's own playground. The basics tutorial is
    rewritten and re-shot on it.
  • A step that waits, with nothing to wait for. playground.arrive parks for a number of
    pokes or a length of time and then yields a batch, so a readiness wait can be taught and
    verified with no service to poll. Its output is the shape a queue consumer answers with,
    so a document that later reads a real topic changes very little.
  • A stream to test a consumer against. /api/v1/playground/stream writes newline-delimited
    JSON: an envelope, then a message every interval, then a closing line, so a reader can tell
    an ending from a dropped connection.
  • A failure can say what kind it is. fail_as beside fail_until names the class the
    induced failure carries, so the difference between a transient failure that spends the
    retry budget and a rejected one that spends none can be taught offline, on three steps
    under one policy. Before this, a document could only get a rejected failure from a live
    HTTP call or by naming a Faker provider that does not exist.
  • An unknown provider names a command that exists. The refusal for a Faker provider the
    installed library does not have pointed at a route that was never served.

v0.19.0

Choose a tag to compare

@mortenoh mortenoh released this 26 Sep 08:32
v0.19.0
855d2f0

Released 2026-09-26. Every package in the workspace moves to 0.19.0 together, and so do
dirigent-dhis2 and dirigent-integration.

  • The playground: an instance answers its own test requests. Five paths under
    /api/v1/playground/, unauthenticated and off by one setting, that reflect what they were
    called with and answer the way a document needs them to: a request echoed back whole, a
    chosen status, a redirect that stays on this instance, an endpoint that fails a set number
    of attempts before succeeding, and one that sets the response headers you name. Every
    answer carries the request that produced it under one key, and the resolved knobs beside
    it, so what was sent and what was used are both on screen.
  • A node that makes data, anywhere in a flow. playground.generate takes a field map
    naming the Faker provider that fills each field, so every provider the installed Faker
    offers is reachable in every locale, reproducible under a seed the answer always reports.
    It also fails on demand, delays, drifts its own shape, pages, and pads its payload, so a
    retry example really retries, a gate is really seen catching drift, and an outputs-to-
    storage example really crosses the threshold. It works with no input as a document's first
    step, with an input from upstream in the middle, and as the last step.
  • A shelf of five documents that teach exactly those cases, runnable with no network.

v0.18.4

Choose a tag to compare

@mortenoh mortenoh released this 25 Sep 21:02
v0.18.4
3abaf5d

Released 2026-09-25. Every package in the workspace moves to 0.18.4 together, and so do
dirigent-dhis2 and dirigent-integration.

  • A title reads whole. In a listing that puts a title beside a description, the title
    column has first claim on the width and the description takes what is left; 0.18.3 split
    the two evenly and cut titles short on the Schemas listing and the dashboard.
  • One corner for the window button. Every pane that opens in a window puts the button at
    the bottom right, where the scrollbars meet and no text is; the read-only window for
    produced data had its own corner.
  • Highlight the current line. A preference under Settings, General, off unless asked,
    that every editor follows as it is switched.
  • A search box does not repeat the heading above it. Every listing's search says
    Search; a picker still says what a match is made on, since nothing else does.
  • A card draws no label for a cell that said nothing. A schema with no description no
    longer wears an empty Description on a phone, and the same holds on every listing.
  • The dashboard's Health card fits its width at 1024. Nothing on it scrolls sideways.

v0.18.3

Choose a tag to compare

@mortenoh mortenoh released this 25 Sep 16:16
v0.18.3
0178361

Released 2026-09-25. Every package in the workspace moves to 0.18.3 together, and so do
dirigent-dhis2 and dirigent-integration.

  • The bucket is made by the image itself. dg storage ensure creates the artifact bucket
    the instance's storage root names, idempotently, and the compose stack's migrate service
    runs it after the schema and the connection. The mc sidecar is gone from the stack and
    from what dg init writes: the image it pulled no longer exists on any registry, and a
    one-line job never needed a third-party tool image.
  • A listing follows the width it was given. A table turns into cards when its own box
    cannot hold its columns, not when the window is narrow, and a column holds either a value
    that declares its floor or text that takes what is left and truncates. Every listing is a
    table at 1024 with nothing scrolling sideways; cards remain beside an open panel and on a
    phone.
  • A menu is floored at its control and capped at the screen. A picker's popup grows to its
    longest row, never past the viewport's gutters, so a long row reads whole at phone width.
  • A connection kind declares its mark. A pack sets mark on its ConnectionKind, one
    SVG path on the 24-unit grid, monochrome; the catalog carries it and every screen that
    names the kind draws it. The check at registration and in the conformance kit refuses
    anything that is not path data.
  • The graph's layout engine is its own chunk and its own thread. elk loads when a canvas
    first asks for a layout and runs in a worker; the graph chunk drops from 1615 kB to 183 kB.
  • Screens. A new docs page with nine pictures of an instance running the three showcase
    pipelines, click-to-enlarge on every docs picture, and make docs-shots to re-shoot them.
    The showcase shelf holds the three documents, all runnable locally.