Watch
0
0
Fork
You've already forked hyperhive
0

archetype flows behave like the built-ins: switch off per archetype or per agent, board cards, duplicate

The document's disabled list takes archetype:<archetype>/<flow> (off for every member) and the same with @<agent> (off for one member); a key naming nothing is ignored. GET /api/notify/flows lists the archetype flows with their members, the board draws a card for each with a switch, a checkbox per agent and duplicate, and the dry run counts their hits. The flows keep following the archetype.
This commit is contained in:
atlas 2026-11-11 19:08:19 +01:00 • committed by mara
commit 68c24643f6
2 changed files with 908 additions and 102 deletions

View file

@ -387,6 +387,47 @@ test("seedFlow opens an empty or copied draft under the placeholder id", () => {
assert.equal(view.flows.length, 1, "nothing is added to the view");
});
test("seedFlow copies an archetype flow by its key, sending to its one member", () => {
const members = {
...flow(),
id: "new-issues",
name: "New issues",
nodes: flow().nodes.map((n) =>
n.kind === "todo" ? { ...n, to: "members" } : n,
),
};
const archetype = (agents) => ({
revision: 1,
disabled_builtins: [],
builtins: [],
flows: [],
archetype_flows: [
{
archetype: "triage",
key: "archetype:triage/new-issues",
flow: members,
members: agents,
},
],
});
const recipients = (f) =>
f.nodes.filter((n) => n.kind === "todo").map((n) => n.to);
const one = seedFlow(archetype(["alice"]), "archetype:triage/new-issues");
assert.equal(one.id, NEW_FLOW_ID);
assert.equal(one.name, "New issues (copy)");
assert.ok(recipients(one).length > 0, "control: the flow has a todo");
assert.ok(recipients(one).every((to) => to.agent === "alice"));
// Two members: the operator names the agent in the editor.
const two = seedFlow(
archetype(["alice", "bob"]),
"archetype:triage/new-issues",
);
assert.ok(recipients(two).every((to) => to.agent === ""));
assert.equal(seedFlow(archetype(["alice"]), "archetype:triage/gone"), null);
// The archetype's own flow keeps "members".
assert.ok(recipients(members).every((to) => to === "members"));
});
test("savedAs takes the id from the name, clear of every saved id", () => {
const view = {
revision: 1,

View file

@ -3,6 +3,7 @@ import assert from "node:assert/strict";
import { test } from "node:test";
import {
describeNode,
memberKey,
kindValues,
ordinal,
withBuiltin,
@ -114,3 +115,15 @@ test("describeNode says what a reviewer request and an unknown kind are", () =>
);
assert.match(describeNode({ id: "x", kind: "future" }), /future/);
});
test("memberKey names a flow's switch for one agent", () => {
assert.equal(
memberKey("archetype:triage/new-issues", "alice"),
"archetype:triage/new-issues@alice",
);
// The per-agent key joins the same list as a built-in's id.
assert.deepEqual(
withBuiltin(["closed"], memberKey("archetype:t/f", "a"), false),
["archetype:t/f@a", "closed"],
);
});