Skip to content

Releases: StochasticEntropy/runcomposer

runcomposer v0.1.7

Choose a tag to compare

@github-actions github-actions released this 03 Sep 15:14

Fixed

  • Adding a filter opens the picker. In 0.1.6 the picker sat beside the
    filter builder's own "add" button rather than replacing it, so the most
    obvious control in the panel still opened the builder's inline editor — and
    that editor, handed the catalog's tags (also new in 0.1.6), rendered them as
    a flat, unsorted, unsearchable list of all 1609 with a checkbox each, four
    rows visible, every box ticked by default. That is the screen the picker
    exists to replace, reached by the most likely click. Every add-rule is now
    intercepted and opens the dialog, and the builder is given no tag list at
    all, which removes that list from everywhere it could appear. Choosing tags
    happens in the dialog; the builder shows and edits the resulting expression,
    and its row menu's Edit still takes any raw regex: / prefix: pattern.

    0.1.6 justified leaving that button alone on the grounds that the widget's
    add-rule payload does not name the group the rule was headed for, so an
    interception could not honour a per-row add. That is still true and it was
    the wrong trade: where a group lands is now the dialog's own "join with the
    current filter" choice, which is visible and stated.

Changed

  • The dialog follows the predecessor's layout rather than a rearrangement of
    it
    : the four controls in its order (include/exclude, how the picked tags
    combine, how the group joins what is there, search last), the multi-select
    hint above a bordered tree box, a dot on leaf rows, a kind badge on rows
    standing for one concrete tag, and a footer of "Selected: N" against Cancel
    and Apply. Sized as it was: min(980px, 96vw), 85vh, 52vh of tree.

    Dropped with it: the expression preview line and the staged-removal state,
    both added in 0.1.6 and neither part of that design. A row whose pattern the
    filter already carries still shows a ; it is a mark, not a checkbox, and
    nothing is ever pre-selected. Removing a condition is the filter panel's
    chips, each with a one-click .


Full changelog: https://github.com/StochasticEntropy/runcomposer/blob/v0.1.7/CHANGELOG.md

runcomposer v0.1.6

Choose a tag to compare

@github-actions github-actions released this 03 Sep 14:49

Added

  • The tag tree is a picker dialog, and a whole selection is one filter
    group.
    Resolving the taxonomy against the catalog (0.1.4) made the tree
    complete and, in the same move, unusable: on a real corpus it is 2452
    nodes over 1609 tags
    (up from ~150 written nodes), rendered as an
    always-visible column with no search. Pick tags… in the filter panel now
    opens a modal over that tree with

    • a live search that narrows without flattening — a matching node keeps
      all of its children, so a hit stays under the heading that explains it,
      and a branch matching nothing is dropped;
    • multi-select with checkbox, space, shift-range and ctrl/cmd, full
      arrow-key navigation, and a focus trap that returns focus to the button
      that opened it;
    • three explicit controls — include or exclude, how the picked tags
      combine with each other, how the group joins the filter already built —
      shown next to the expression they produce, before it is applied;
    • one apply, which is also the usable road to a nested filter:
      Checkout AND (Payments OR Cart) is two tags with "match any of them"
      and "AND", not a filter assembled row by row. It produces the same
      selection.tag_filter as the builder's own Add group — the AST is the
      contract (docs/taxonomy.md).
  • The filter reads back as one sentence. Filter as a sentence in the
    filter panel renders the whole tag_filter in the run spec's own grammar —
    (Payments OR Cart) AND NOT Quarantine-Flaky. The builder shows
    widgets and the chip row is flat; this is where a nested filter is legible to
    somebody who did not build it.

  • The filter's value field completes against the catalog's real tags. It
    was handed an empty options map, so typing a tag was blind and you had to
    know its spelling by heart — on 1609 tags, nobody does. GET /api/v1/taxonomy?resolve=true now also serves tags, the catalog's whole
    tag universe in the tree's own order. It is not derivable from the nodes:
    a written leaf whose pattern resolves to exactly one tag already is that
    tag's node and gets no tag child, so a tag reached only through
    regex:^Cart(V2)?$ is spelled nowhere in the tree. The default
    GET /api/v1/taxonomy is unchanged and carries no tags.

  • react-aria-components (Apache-2.0) for the dialog's tree and modal.
    Deliberate: a multi-select tree with real keyboard and screen-reader
    semantics is the reason this work exists and a bad thing to hand-roll. It
    costs the bundle +58 kB gzip (90.6 → 158.8 kB gzip; 287 → 510 kB raw).
    Its Virtualizer is what makes the tree viable at this size — see below.

Fixed

  • Excluding a selection negates the group, not each row. The glue flips
    under de Morgan, and getting it backwards is silent and plausible-looking
    rather than an error. On the shipped demo corpus (60 items),
    NOT (Payments OR Cart) is 38 items and so is the
    (NOT Payments) AND (NOT Cart) the picker writes — while
    (NOT Payments) OR (NOT Cart) is 60, the entire corpus: an exclusion that
    excludes nothing. The four numbers are pinned by a test. The picker cannot produce that shape, and
    under "exclude" its combine control offers exactly one reading, "match
    none of them", because the other one ("does not carry all of them") is
    almost never meant and cannot be worded apart from the first. The raw
    builder can still express it.

  • A ticked list of tag values no longer disappears from the filter.
    Offering the catalog's tags as options lets the widget's value editor write
    an includes list, which the adapter translated to nothing — ticking two
    tags silently dropped the condition, narrowing the preview with nothing
    anywhere to say why. A list is now an OR of literals, and a negated list
    the AND of the negations (the same de Morgan step), listed as
    is any of / is none of.

  • A one-letter search no longer freezes the dialog. Searching opens every
    branch it kept, and one letter keeps all 2452 nodes: that put every row in
    the DOM and took 17 seconds to settle. The tree is virtualized (21 rows
    in the DOM for the whole corpus), and past a budget of 400 matches the
    branches stay closed with the count shown instead of being thrown open —
    nothing hidden, nothing truncated. Every query now costs ~140 ms of
    main-thread work.

  • Closing the picker returns focus to the button that opened it. The dialog
    restores focus to whatever was focused when it opened, which on macOS is
    nothing — clicking a button there does not focus it — so a keyboard user was
    dropped on <body> and had to tab back through the whole page.

Changed

  • The taxonomy sidebar is gone. The compose view is one column, and the
    freed width goes to the filter and the preview. What survives from 0.1.5 is
    the part that was about the filter rather than the panel: a pattern the
    filter already carries is marked in the tree, the mark follows the pattern
    and not the position
    (the only reading that holds once the tree is
    resolved), and a closed branch carries the count of distinct marked patterns
    hidden below it. A row now has three visually distinct states — selected for
    this round, already in the filter, staged for removal — and pressing the mark
    stages a removal, so the dialog edits the tag part of the filter in both
    directions and Cancel cancels both. ui/src/components/TaxonomyTree.jsx,
    its styles and its locale keys are deleted rather than left unused.

Not done

  • The picker does not take over the filter builder's own "add a filter"
    button.
    Intercepting add-rule works, but the payload does not say where
    the rule was going: in @svar-ui/react-filter 2.6.0 both the toolbar's add
    and a row menu's add send {rule, edit} where rule is the click's own
    React event object (probed live). The two are therefore indistinguishable and
    neither names a target group, so an interception would always have to append
    at the top level — silently breaking the "add a group, then add a rule inside
    it" path 0.1.5 opened up. The picker sits beside the builder's own add, as
    the panel's primary action; the raw row is no longer blind either, now that
    its value field completes against real tags.
  • include / exclude are not two builder fields. The single tag field
    keeps its eight operators. The non-technical user no longer meets an operator
    at all — that is what the picker is for — and the readable sentence plus the
    Tag ≠ x chips already say a negation in words, so a second field would
    double every field-dependent path in the adapter to restate what is already
    legible. The trap it was proposed to help with (a negated leaf inside an OR
    group) exists identically either way, and what makes it visible is the
    sentence, not the field name.

Full changelog: https://github.com/StochasticEntropy/runcomposer/blob/v0.1.6/CHANGELOG.md

runcomposer v0.1.5

Choose a tag to compare

@github-actions github-actions released this 03 Sep 12:11

Fixed

  • Removing a single filter condition was a hidden click. SVAR's filter rows
    carry their own menu — Edit, Add filter, Add group, Delete — behind an icon
    the widget draws with an icon font that @svar-ui does not ship: no
    @font-face for wxi exists anywhere in the package, so the trigger
    rendered as a blank 20px box. Everything worked; nothing was visible. The
    glyphs are drawn now, so the widget's own per-row controls (including nested
    groups, which type="list" supported all along) are reachable, and the
    filter panel lists each condition as a chip with a one-click above the
    builder. Clear is unchanged.
  • A taxonomy node is a switch, not an append-only button. Clicking a node
    already in the filter removes it instead of adding a second identical rule,
    and every node currently in the filter is marked in the tree. The mark
    follows the pattern, not the position — which is the only reading that
    holds on the resolved tree of 0.1.4, where a tag hangs under every node whose
    pattern covers it: all of its rows light up and any of them switches it off.
    A closed branch carries the number of distinct active patterns hidden
    underneath it, so nothing active is invisible. Quick-filter chips toggle the
    same way.
  • Adding a condition no longer throws the filter builder away. The panel
    remounted the whole widget on every taxonomy click (key={locale-revision}),
    and re-seeded its store on every render besides, because fields and
    options were fresh objects each time. The widget now keeps its state
    through its own edits; only an external change (taxonomy, quick filter, ,
    Clear) hands it a new value, and a remount (locale or theme switch) hands
    it the current one.

Added

  • npm test in ui/ — Node's own test runner over the filter adapter (no
    new dependency): the toggle, per-condition removal, the operator vocabulary,
    and the nested-group round trip that a click-through cannot pin down. Runs
    in CI alongside the bundle check.
  • A locale guard in the Python suite: EN and DE must carry the same keys, no
    message may be empty, and the bundled ui_dist locales must match the
    source.

Full changelog: https://github.com/StochasticEntropy/runcomposer/blob/v0.1.5/CHANGELOG.md

runcomposer v0.1.4

Choose a tag to compare

@github-actions github-actions released this 03 Sep 11:56

Added

  • The taxonomy tree resolves against the catalog. A written leaf carries
    exactly one pattern, so a pattern standing for a family of tags rendered as
    a single opaque node: regex:^Cart(V2)?$ was one row, and the tags it
    covered had no row of their own and could not be picked. Over a real corpus
    that is most of the tag space hidden behind a handful of patterns. Now every
    node gains one child per concrete catalog tag its own pattern matches, each
    individually selectable; a node whose subtree matches nothing collapses away
    instead of clicking and selecting nothing; and the tags no node anywhere
    claims are gathered under one synthetic node, so nothing in the catalog is
    unreachable. On the shipped demo world that takes tags with a node of their
    own from 9 of 47 to 47 of 47.

    The taxonomy file format does not change. Resolution is derived from the
    live catalog and never written back, and resolved nodes speak the same three
    keys the file is written in — label, filter, children — so the result
    is itself a valid taxonomy document. The additive metadata is id,
    origin, tag_count and item_count
    (docs/taxonomy.md).

  • GET /api/v1/taxonomy?resolve=true serves that tree, plus a resolved
    summary (tags_total, tags_claimed, tags_selectable,
    tags_unassigned, items_total, items_claimed, nodes_written,
    nodes_dropped, nodes_total). The published shape is unchanged: the
    parameter defaults to off and GET /api/v1/taxonomy still returns the
    validated file verbatim, so an existing client sees exactly what it saw
    before and pays none of the resolution cost. Resolution happens server-side,
    against the same cached catalog the selection preview compiles over — the
    tree and the preview therefore cannot disagree about which tags exist, and
    the filter grammar stays in one implementation instead of being mirrored in
    JavaScript.

  • runcomposer taxonomy-check --tree prints the resolved tree — every
    node with its pattern and its item/tag counts — which is the answer to "why
    does that node not show what I expected". The drift report itself gains a
    closing summary of what resolution reaches.

Changed

  • The UI's taxonomy panel renders the resolved tree, and therefore
    collapses: rows open and close, a branch's children are mounted only while
    it is open (the real corpora resolve to thousands of nodes), each row shows
    its item count, and the first level opens on arrival. A leaf's accessible
    name is now its label — it was the pattern, which for a composed alternation
    regex meant a screen reader read hundreds of characters of expression
    instead of one word. The pattern is still the tooltip.

Full changelog: https://github.com/StochasticEntropy/runcomposer/blob/v0.1.4/CHANGELOG.md

runcomposer v0.1.3

Choose a tag to compare

@github-actions github-actions released this 03 Sep 10:55

Fixed

  • The sdist was 40 MB. Hatchling's default sweeps in everything the VCS
    does not ignore, so the homepage's 37 MB of videos and a stray agent
    worktree were being packaged as source. The sdist is now an explicit
    include list; the wheel was always 0.2 MB and is unchanged.

Full changelog: https://github.com/StochasticEntropy/runcomposer/blob/v0.1.3/CHANGELOG.md

runcomposer v0.1.0

Choose a tag to compare

@github-actions github-actions released this 07 Jul 14:21

Changelog

0.1.0 — 2026-07-07

First release: DESIGN.md phases P0–P3 complete, each independently
judge-verified against the design.

Core & spec (P0/P1)

  • runspec 1.0: versioned, JSON-isomorphic run spec with published JSON
    Schema; runcomposer validate (incl. the --for-dispatch profile) with
    the §3 versioning policy (strict known fields, MINOR-forward tolerance,
    refuse higher MAJOR).
  • Lossless tag-filter AST (literal / prefix: / regex:, AND/OR/NOT),
    selection compile with fixed intersection semantics, catalog snapshots.
  • sqlite RunStore (normative schema), full run lifecycle
    (COMPOSED → RUNNING → AWAITING_RESULTS → COMPLETE, computed completion),
    export dispatches, runcomposer-exec — the vendorable single-file
    stdlib-only spec consumer writing the runcomposer_run.json marker.
  • CLI: validate · demo · catalog · compile · spec · dispatch · runs ·
    ingest · gc · export · serve. Compose/preview HTTP API.
  • React UI (en + de, all literals in locale files), taxonomy tree, SVAR
    filter builder behind an adapter, auto-compiled preview, runner-aware
    compose footer — pre-bundled in the wheel, no Node needed to evaluate.

Ingestion transports (P2a)

  • Token-guarded results push API (POST /api/v1/runs/{id}/results),
    file-drop inbox watcher, quarantine inbox with attach/promote, content-hash
    idempotency (byte-identical = no-op, same-shard redelivery = last-writer-
    wins), marker spec_sha256 verification, runcomposer gc retention.

Execution (P2b)

  • robotframework TestSource (id = longname) and robot-pool runner:
    shared process pool, partition fan-out, duration-balanced chunking with
    documented round-robin cold start, live verdicts via the injected Robot
    listener, per-dispatch artifact isolation, §3.3 drift refusal /
    allow_drift intersection with SKIP-reason-drift. Defused
    robot-output-xml parser. All behind the runcomposer[robot] extra.

Reach (P3)

  • Defused junit-xml parser + pytest example corpus (manifest aliases
    map native junit names onto nodeids — the framework-agnosticism proof).
  • History-based selection (failed@latest, run:<id>, before:<time>)
    with derived_from provenance; runs --failed-in latest,
    spec --from-history, UI quick-pick.
  • CTRF export (runcomposer export <run> --format ctrf).
  • ci-trigger runner + the thin CI-side consumer stage (reproducible
    Jenkins-in-docker under ci/jenkins/): webhook-out completion and a
    build-API polling fallback; session-bound CSRF crumb handling.

Post-P3 polish

  • ci-trigger dispatches record the SPEC_JSON hash so §5 marker verification
    works on the CI path.
  • robot-pool: user listener pass-through and pre_run_hooks (completing
    the §6.2a option list).
  • Clearer error when a history selection matches nothing; push requests with
    a mismatched declared format are rejected; gc no longer leaves empty
    artifact directories.