Releases: StochasticEntropy/runcomposer
Release list
runcomposer v0.1.7
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. Everyadd-ruleis 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'sEditstill takes any rawregex:/prefix:pattern.0.1.6 justified leaving that button alone on the grounds that the widget's
add-rulepayload 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
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_filteras the builder's ownAdd group— the AST is the
contract (docs/taxonomy.md).
- a live search that narrows without flattening — a matching node keeps
-
The filter reads back as one sentence.
Filter as a sentencein the
filter panel renders the wholetag_filterin 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=truenow also servestags, 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/taxonomyis unchanged and carries notags. -
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).
ItsVirtualizeris 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
anincludeslist, 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 andCancelcancels 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. Interceptingadd-ruleworks, but the payload does not say where
the rule was going: in@svar-ui/react-filter2.6.0 both the toolbar's add
and a row menu's add send{rule, edit}whereruleis 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/excludeare not two builder fields. The singletagfield
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 ≠ xchips 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
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-uidoes not ship: no
@font-faceforwxiexists 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, whichtype="list"supported all along) are reachable, and the
filter panel lists each condition as a chip with a one-click✕above the
builder.Clearis 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, becausefieldsand
optionswere 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 testinui/— 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 bundledui_distlocales must match the
source.
Full changelog: https://github.com/StochasticEntropy/runcomposer/blob/v0.1.5/CHANGELOG.md
runcomposer v0.1.4
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 isid,
origin,tag_countanditem_count
(docs/taxonomy.md). -
GET /api/v1/taxonomy?resolve=trueserves that tree, plus aresolved
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 andGET /api/v1/taxonomystill 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 --treeprints 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
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
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-dispatchprofile) 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 theruncomposer_run.jsonmarker. - 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), markerspec_sha256verification,runcomposer gcretention.
Execution (P2b)
robotframeworkTestSource (id = longname) androbot-poolrunner:
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_driftintersection with SKIP-reason-drift. Defused
robot-output-xmlparser. All behind theruncomposer[robot]extra.
Reach (P3)
- Defused
junit-xmlparser + pytest example corpus (manifestaliases
map native junit names onto nodeids — the framework-agnosticism proof). - History-based selection (
failed@latest,run:<id>,before:<time>)
withderived_fromprovenance;runs --failed-in latest,
spec --from-history, UI quick-pick. - CTRF export (
runcomposer export <run> --format ctrf). ci-triggerrunner + the thin CI-side consumer stage (reproducible
Jenkins-in-docker underci/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
listenerpass-through andpre_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;gcno longer leaves empty
artifact directories.