Commit Graph

279 Commits

Author SHA1 Message Date
Lucio Lelii 4b638414c9 Drop the coloured stripe from a step's left edge
The badge and the kind under the name already say what sort of step it
is; the stripe only added weight. A resource card keeps its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 09:56:44 +02:00
Lucio Lelii b6d61c2b8e Draw a step's header the way a resource card is drawn
The saturated blue (or amber) header bar gives way to a light one: a
tinted badge with the step's icon, its name in dark type with its kind
under it in the step's colour, and quiet borderless buttons that colour
on hover. The kind of step stays in a stripe on the left, as on a
resource card.

A step's icon now follows its kind - automated, human, decision, end,
branches rejoin - with the pictures the palette already uses for them,
instead of one raster image for every automated block. A schema that
names an icon still wins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 09:52:46 +02:00
Lucio Lelii 3f4fb97626 Let a resource be expanded to edit it at full size
Like a block, a resource can now be lifted out of the canvas into a
panel at full size, with every field open, whatever the zoom - an init
script above all is unreadable in a card on a zoomed-out canvas. The
settings button still opens the fields in place for a quick look.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 09:47:11 +02:00
Lucio Lelii 2bf0f40804 Put a resource's handle bar on its right edge
Its links now leave to the right from the bar's middle, as a step's
outputs do, and come into the step's handle from the left.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 09:36:20 +02:00
Lucio Lelii 0afe66d372 Make a resource's handle a bar along its top edge
A small square on the edge was hard to see and hard to grab, and Rete
anchored its links 12px to one side of it - the offset it gives an
output on a node's right. The handle now runs along the whole top edge,
and its links start from the bar's middle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 09:27:18 +02:00
Lucio Lelii 733de8df7a Draw a resource as a compact card, not one more step
A resource looked like a step: a header bar, a "Used by / Steps" row
with the handle on the right, every field open. It is now a card - its
type's icon, its name, one line saying where the store is - with the
fields behind a settings button and its handle on the top edge. Its
links leave that handle vertically and come into the step from the
left, so they read as something the steps hang from rather than one
more connection in the chain.

Saved links to a resource were not drawn at all when the resource
types had not been fetched yet - which was always, unless the
Resources palette had been opened - because the link was checked
against a kind nobody knew. The editor now fetches the resource types
before drawing, and puts saved links back without checking them again:
the server has.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 09:23:42 +02:00
Lucio Lelii b60fc3c1d6 Show each kind of store as a resource of its own
The resource palette now lists an S3 storage and a PostgreSQL database
as two cards, each with the label and icon its type declares, and the
node on the canvas takes the same. A step's resource field names a
kind, so a storage operation links to either store.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 09:09:47 +02:00
Lucio Lelii 52b26acddd Describe a resource node from the resource catalog, not the block one
The resource node inherited the generic node's schema lookup, which asks
the block catalog. Storage is not a block, so the lookup found nothing:
the node showed no fields, and every click on it fetched the catalog
again and covered it with the loading overlay, so nothing inside could
be clicked.

The lookup is now a hook the resource node overrides with the resource
catalog, and a resource is never recreated on the server - it has no
ports for the server to recompute.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 08:58:23 +02:00
Lucio Lelii 612fe41515 Give resources a palette of their own, beside blocks and containers
They were squeezed into the blocks palette as one more kind of block,
which is exactly the confusion the model change was meant to end. The
sidebar now has a third entry with its own icon and its own list, and
the blocks palette knows nothing about resources again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-29 08:53:22 +02:00
Lucio Lelii 067cd2f287 Draw a resource as its own kind of node, not a block
A storage was a block with a visual role that said "not a step", which
left resource-shaped exceptions scattered through the editor, the node
components, the palette and the execution view.

The flow now carries resources beside its blocks, and the canvas draws
them with a component of their own: no ports, no dependencies, one
handle for the steps that use it. What a step links is read from the
schema flag x-resource-ref, so the editor no longer knows the word
"storage" anywhere - a new kind of resource needs no code here.

The configuration form the two kinds of node share is now a component of
its own (app-node-parameters), so a resource is edited exactly as a
block while it is drawn differently.

Resource types come from /resources/types, and share the palette with
the blocks under the Resource kind.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-29 08:24:18 +02:00
Lucio Lelii ee2696814c Link storage operations to a Storage node on the canvas
A Storage node has a 'Used by' handle and a storage operation a 'Works
on' one; the link drawn between them, teal and dotted, is what sets the
operation's storageNode. It is neither a connection nor a dependency: it
carries no value and orders nothing, and loops leave it out. An
operation works on one node, so a new link replaces the old one, and
removing the link empties the field. On load the links are drawn back
from what each operation says.

Fields the schema marks x-ui-hidden are left out of every form and chip
- storageNode is set by the link, not typed - and kept in the
configuration.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 17:39:37 +02:00
Lucio Lelii 1a7d139535 Put the palette's kind filter in a menu on the search field
The chips took four lines of a narrow sidebar. A filter button at the
end of the search field opens the kinds as checkboxes instead, and
shows how many are picked; its tooltip says which. The menu lives in
the overlay, so its font is set globally, where the palette's own
styles do not reach. A storage's icon is 'storage': 'database' is not
in the icon font, and showed nothing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 16:09:23 +02:00
Lucio Lelii 53439558b6 Filter the palette by kind of block
A row of chips under the search, one for each kind the palette has -
automated and human steps, decisions, rejoins, ends, resources - in a
fixed order. Picking some narrows the list to them, together with the
search; picking none shows every block, and Clear goes back to that.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 16:03:33 +02:00
Lucio Lelii 46062fca40 Call a storage node a Resource, in the palette and on the node
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 16:01:17 +02:00
Lucio Lelii a8b1920fdb Say what each block is in the palette, from its role
The palette knew two kinds of block, a human step and an automated one,
so a Storage node, an End and a routing block all read 'Automated step'.
It now goes by the role the block's type declares: a resource that is
not a step, an end of a path, an automated or a human decision, a rejoin
of branches, a human step, an automated step - each with its own icon.

The server's BRANCH_REJOIN role is read as the web's MERGE, which it had
never matched, so a rejoin was taken for an ordinary activity.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 16:00:20 +02:00
Lucio Lelii 672f53d9ab Offer Use default on a node field only where there is one
The editor a node opens for a single field offered Use default on every
optional field that was not a checkbox: a storage's connection, its
operation, its scope, and any other choice between alternatives, where
there is nothing to go back to. The settings dialog already followed the
schema's x-ui-defaults-when-empty; this editor now does too, so the
button is on the model's sampling knobs and nowhere else.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 15:39:20 +02:00
Lucio Lelii 1aa32efa80 Draw and wire storage nodes as resources, not steps
A storage node comes from the server with the RESOURCE role. In the
editor it is drawn apart from the steps - dashed, teal, labelled
'Storage · not a step' - and its targets take a connection from every
step that writes there; a view parameter (<view>.<parameter>) still
takes one.

Its edges are left out when the editor works out which connections lead
back round a loop: a write followed by a read of the same node is not a
cycle, the same view the server takes.

The execution view draws the node from the flow, since it never has a
step, with no status of its own, so the reads and writes on its edges
still have somewhere to go.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 12:23:08 +02:00
Lucio Lelii 3abcecb944 Ask for a personal storage connection from the vault
A storage step on the runner's own connection needs it before the
execution starts. The gate now takes any requirement the server marks as
paid from the vault (vaultBacked), with the key prefixes as a fallback,
so the S3 or PostgreSQL connection shows up beside the LLM credentials.

Adding one opens a form with the storage type's own fields, from
/storage/types - endpoint, bucket, keys, or host, database, user - and
saves them together as the JSON the server encrypts whole.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 11:55:45 +02:00
Lucio Lelii 9639b2bfe8 Show a list's full name on hover, and make its count easy to read
A list name too long for the node was cut with an ellipsis and nowhere
readable in full. It now shows whole in a tooltip when it is cut, the
way sidebar titles do. The count is white on a solid badge, and quiet
when the list is empty.

The name tooltip also takes the app's font: it asked for Roboto, which
the app does not load, and fell back to a serif - in the flow list too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 19:35:14 +02:00
Lucio Lelii aa3f54bb1a Show a node's list parameters closed, with how many items they hold
Every list parameter of a node now reads as its name and a count, and
opens on demand to its items; with none there is nothing to open, the
add button is the way in, and the "No items" line is gone. An item
added opens the list, so it is there to see.

The list is one shared component, app-node-list-field, rather than two
near-identical blocks in the block node and a third in the execution
view: the same list reads and behaves the same everywhere, and a
container that gains a list parameter takes the component as it is.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 19:32:03 +02:00
Lucio Lelii f4a526ebbd Edit a list of plain values one value at a time, not as raw JSON
A list whose elements are plain values - the tools an LLM node must use
successfully, the steps an evaluator follows - had no fields to build
its element dialog from, and fell back to a box asking for "Item JSON"
with {} in it, a shape nobody could guess. The element is now edited in
a single field of its own type, labelled and explained as the list is;
an empty value is not added and a value already in the list is refused.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 19:19:05 +02:00
Lucio Lelii 79ae012faa Keep all of docs/ local
The user guide files already committed stay tracked; anything new in
docs/ has to be added on purpose.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 19:04:04 +02:00
Lucio Lelii da269130f6 Ask a secure retriever whether a field is open beside its items
A secure retriever's values are at .../{parameter}/items and its
questions - is the field required, does it take a typed value - at
.../{parameter}/required and /open beside them. The question URL was
made by suffixing the values URL, so for every secure retriever it
pointed under items, where nothing answers: choosing a global input as
a file's source showed an error contacting the server.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 19:04:04 +02:00
Lucio Lelii f2ade91000 Draw a loop's way back above every node between its ends
It cleared the two nodes it connects and nothing else, so when the
loop's own steps sat higher - the usual case - its top ran hidden behind
them, limit label included. It now clears every node between its ends,
and follows any of them being moved.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 14:57:56 +02:00
Lucio Lelii 32b62d10a2 guides moved in his own folder 2026-09-25 14:46:16 +02:00
Lucio Lelii 7459b1e70b Publish the user guide as PDF and DOCX, and keep the rest of docs/ local
docs/ now holds only the user guide in the repository - Markdown, PDF and
the new DOCX. The working notes, the scripts that build the guide and its
screenshots are ignored; render-user-guide.py, tracked until now, leaves
the repository but stays on disk.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 14:44:53 +02:00
Lucio Lelii 7c30ba1a38 Give the Rete plugins one rete in tests, and stop an abandoned editor load
Five unhandled rejections came with every test run, from the pages that
render a real graph. In tests, rete-angular-plugin was loaded by Node and
got rete's CommonJS build while everything else got its ES module, so
the renderer was not a Scope of the rete it was plugged into. Vitest now
processes the rete packages itself, and all of them share one rete.

With the graph actually built, a real bug showed: an editor still loading
when its component was destroyed went on mounting nodes with the
component's destroyed injector. A load now stops once its component is
destroyed or a newer load begins - between nodes and connections, and at
every render, which is dropped for an abandoned editor.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 14:42:26 +02:00
Lucio Lelii 7477027319 Put a copy button next to every value, not only in the full view
The full view opens only when a preview leaves something out, so a short
output or input had no way to be copied. Every value in the Output,
Intermediate and Rounds tabs now has its own copy button, beside the
view button when there is one. The full view's button just says Copy:
it copies any value, JSON or not.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 14:35:08 +02:00
Lucio Lelii 90a04dc480 Let a value be copied from its full view and from the outcome
The full view of an output or input gains a Copy button - Copy JSON for
a JSON value - and the outcome payload a copy button in its corner, which
stays put while a long payload scrolls. JSON is copied indented, whether
it came as an object or as a model's text in a fence, so it pastes as
clean JSON elsewhere; text is copied as shown.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 14:32:19 +02:00
Lucio Lelii 6134590bd8 Keep the JSON full view still while its tree is opened and closed
Sized to its content and centred on the page, the modal grew and shrank
with every node opened or closed, its top edge jumping each time. Showing
a JSON tree it now has a fixed height, and only its content scrolls.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 14:29:31 +02:00
Lucio Lelii 3ffaf6e596 Show JSON values as trees, and keep a read-only graph's connections unselectable
Outputs, intermediate inputs, loop rounds and outcome payloads that are
JSON - objects, arrays, or JSON a model wrote as text, fenced or not -
are shown with the JSON tree instead of their text. A preview opens the
first level and a few entries, summing up the rest; the full view opens
the whole tree. The JSON viewer gains the depth and entry limits this
takes.

In a read-only graph, such as an execution's, connections can no longer
be picked or shown as selected: there is nothing to do with one there.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 14:14:20 +02:00
Lucio Lelii 022df1909a Add a Rounds tab showing each loop of an execution round by round
For an execution with a loop, the side panel gains a Rounds tab: each
loop, which round it is on out of how many, and every round with what
its steps were given - the draft sent for review, the verdict handed on
- and how it ended: went round again, left the loop, or stopped at the
limit. Earlier rounds come from the execution's step history, the last
from its steps as they stand.

From the second round of a loop on, a step's inputs are shown as they
are now rather than as prepared at the start, which is only the first
round's value. This also corrects the Intermediate tab and the inputs
shown on a node in a loop.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 14:03:28 +02:00
Lucio Lelii 46f40fe9be Put a loop's delete button in its label, not between its ends
Every connection puts its × at the midpoint of its two ends, which is on
the curve for an ordinary one. A way back is drawn above the nodes, so
that midpoint landed on whatever connection ran between them - it looked
like that connection's ×, still showing after another was selected.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 13:04:54 +02:00
Lucio Lelii 0c129a5f8d Draw a loop's way back above its nodes, clear of how tall they grow
Below the ports, the way back ran through any node taller than a fixed
drop - most of them, once expanded - and its limit label sat hidden
behind the node. It now runs above the tops of the nodes at both ends,
which move only when a node is dragged, and follows them when one is.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 12:58:58 +02:00
Lucio Lelii f2378216ee Show which round a loop step is on, and answer for the round seen
In an execution, a step on its second round or later carries a Round N
badge. Answers to a person's question - decisions, evaluations, evidence,
revealing the reference - send the round of its loop the question was
opened on, fixed when the dialog opened rather than read at submit time,
so an answer to an old round's question is refused by the server instead
of being applied to a draft the person never saw.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 11:41:13 +02:00
Lucio Lelii fbd27c4344 Draw a loop's way back, and let its limit be set on it
A connection from a router back to where a loop starts is picked out
with the same rule the server uses, drawn underneath the nodes in violet
with its iteration limit, and edited in place when selected. The limit
travels on the connection as its loop settings.

An input used to hold one connection, and Rete dropped the old one before
asking. The entry of a loop takes two - from before the loop, and back
from the router - so inputs now accept several and the editor itself
replaces the old one, except when exactly one of the two leads back.

Connections keep their saved id across loads instead of getting a new one
each time, so settings and selections stay attached. Node capabilities
now carry routesExclusively through to the client.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 11:38:39 +02:00
Lucio Lelii f9491e0904 Add package license to lockfile 2026-09-23 12:12:00 +02:00
Lucio Lelii 1270bf7ea7 Improve execution group management 2026-09-23 12:09:57 +02:00
Lucio Lelii def50c229b Merge branch feature/human-evaluation-node into main 2026-09-22 16:08:33 +02:00
Lucio Lelii 3755081e0f Add a live activity panel to the admin area
Answers one question - is it safe to restart the server - so it answers that
first: a single verdict line, then the two lists behind it. A run in flight
outranks a connected user however many there are, because a person loses an
unsaved edit and comes back while a run is simply gone.

Refreshes itself every fifteen seconds, the page being worthless the moment it
is stale. A failed refresh keeps the last known answer rather than emptying the
page: blank would read as "nobody is connected", the one wrong conclusion
somebody could restart the server on.

PresenceService sends a heartbeat a minute, and only while the tab is visible.
Every authenticated request already counts as a sign of life, but the editor is
quiet - someone reading a flow or typing a prompt makes none for minutes, and
they are the person a restart interrupts worst. A backgrounded tab is not
someone using the application, and counting one would turn the panel into a list
of forgotten tabs, which is the failure the server's own session column already
has.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 10:11:39 +02:00
Lucio Lelii 3d4b49e030 Merge branch 'main' into feature/human-evaluation-node 2026-09-21 15:06:52 +02:00
Lucio Lelii fd30de2b1d Point the web interface at its home on the ISTI Gitea
Seven references followed the move: the licence addendum, the NOTICE, the README
attribution, the citation metadata, the package repository field and the two links
the About dialog shows a user - one of which names the backend service, so it moves
to the service's new home too.

The working notes that used to sit in docs/ are gone from the repository and named
in .gitignore, together with .claude. What remains under docs/ is the user guide,
which is written for whoever runs the application rather than for us.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 15:03:11 +02:00
Lucio Lelii 44be453cbb Summarise a plain-value array row by its value
A task script is a list of strings, and every row read back as "Item 1", "Item 2":
the summary only knew how to read object rows, so the very text being configured was
hidden behind its position. A string, number or boolean row now reads as itself, and
falls back to the position only when it is blank.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 12:15:58 +02:00
Lucio Lelii ed4dae1a39 Let the log be read backwards while it is still running
The panel scrolled itself to the newest line on every poll, so a reader
who scrolled up to follow something got yanked back five seconds later -
exactly when a log is worth reading, and exactly the moment it became
unusable.

Following now stops as soon as the reader leaves the end, and resumes on
its own when they come back to it, so the common case needs no button.
The button is there to say which of the two is happening and to override
it: a log that stops moving on its own otherwise looks like a log that
stopped.

The threshold lives in the utils with the rule, not in the component: not
zero, because a list that grew by a line between the scroll and the
handler would otherwise read as the reader having walked away.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 11:55:51 +02:00
Lucio Lelii 469099dffc Read an event's details as an object, under a title that fits
The first cut put the event's own sentence in the heading and its details
in a textarea of raw JSON. A pruning warning runs to a full paragraph, so
the title wrapped five lines and read as an error page, and finding one
number in the body meant counting braces.

The event now opens in its own dialog: a two-word name from the event
type, the node and time beneath it, the sentence as prose, and the
details as the JSON tree the app already had. The name is derived, not
tabulated - a type nobody has seen yet still reads as words - with the
initialisms that would otherwise shout or look like typos spelled out.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 11:24:17 +02:00
Lucio Lelii 8c3d9c6ed3 Let an event's own numbers be opened from the log
The log showed each event's sentence and threw away everything behind it.
A pruning warning saying "6332 characters over budget" could not tell you
what the total actually was, a tool call could not say which tool: those
numbers reached the browser and were dropped, leaving the API as the only
way to read them.

Rows that carry details now offer an eye, which opens them read-only.
Rows that carry none offer nothing - a button opening an empty box is
worse than no button - so the decision of what is worth opening lives in
the view model, where it can be tested, rather than in the template.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 11:07:38 +02:00
Lucio Lelii 974589aeee Render the evaluation form, and submit a judgement as one act
The interaction dialog knew three shapes: a chat, a free-text answer and
a decision. A HumanEvaluation node needs a fourth - the target to open,
the steps to follow, and one control per criterion - so it gets its own
component beside the others rather than another branch inside one.

The verdict goes to the server whole. Sent a criterion at a time, a
failure partway through would leave the step holding a judgement nobody
made.

While a node hides its reference verdict, that input is dropped from the
inputs the form shows: hiding it in the component alone would leave it
sitting in the panel above. Asking to see it is a deliberate, recorded
act, and it does not come back.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 23:24:43 +02:00
Lucio Lelii b1497ab3c4 Make a project a container of flows, with no run of its own
Running every flow in a project put two different things in one list: the
sort control decided what you saw, while the up/down arrows set the order
the run would actually use. Pressing an arrow therefore rewrote the run
order from whatever the list happened to be sorted by - usually the
alphabetical one - and the row often did not move, so the arrows read as
broken.

Rather than reconcile the two, a project is now just a grouping: the play
button, the "Run project" menu entry and the reorder arrows are gone, with
the client calls behind them. The server keeps its endpoints, so bringing
the feature back is a frontend change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 21:15:58 +02:00
Lucio Lelii 82396e8b8f Drive the view-content button from the schema, not from a skills rule
The button was wired to the literal retriever name "Skills" and to a
hand-built /retriever/Skills/definitions URL, so the node knew about one
particular binding. It now reads x-retriever-definition-url off the item
property: any binding that declares where its value can be read gets the
view, and the node knows nothing about which one it is showing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 20:58:14 +02:00
Lucio Lelii 9802f12203 Show the view-skill button on ungrouped array rows too
The node draws array rows from two near-identical template blocks - one
inside a group fieldset, one for ungrouped fields - and the button was
added to only the first. An LLM node renders its Skills list through the
second, so the button never appeared where it was actually wanted.

The test that missed this only exercised the component class, so it adds
a rendering test covering both blocks, and clears the stored user between
service tests: left behind, it made a sign-in a no-op transition and the
cache-reset tests failed intermittently.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 16:08:54 +02:00