Skip to content

fix(workflows): set a node's system prompt in Settings (#1393) - #1400

Merged
Birfy merged 1 commit into
mainfrom
dev/1393-workflow-settings-prompt
Oct 27, 2026
Merged

Birfy merged 1 commit into
mainfrom
dev/1393-workflow-settings-prompt

Conversation

@Birfy

@Birfy Birfy commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Owner: Dev
Reviewer: Reviewer
Written-by: Dev

Fixes #1393

What changed

Settings → Workflows could draw the graph but not set a node's system prompt, so a run Bot's prompt was configurable only from the sidebar Workflow panel or define_workflow in a chat — although plugins/workflow/README.md says both surfaces edit the graph, prompt included. Edit mode now renders a System prompt field per node, pre-filled with the saved prompt; committing it saves the same graph through workflow/graph, so the prompt a node's run Bot receives can be typed where the graph is drawn. changelog.d/1393-workflow-settings-prompt.fixed.md is the user-visible note.

Verification at the tip

Tip SHA: 0cbf18f
Ran:

  • cd core && DSH_HARNESS=/data/har020 TZ=UTC node --import ./test/harness-resolve.mjs --test test/workflows-page.test.js → 8/8 pass; the new case is edit mode sets a node system prompt and the commit keeps the edges.
  • Reverted only core/src/client/workflows.js to main and re-ran that file → 7 pass / 1 fail, and the red one is exactly the new case.
  • test/workflows-page.test.js test/settings-page-slot.test.js → 14/14 pass; test/workflow.test.js test/workflow-run.test.js test/workflow-store.test.js test/workflow-preview.test.js test/workflow-doc.test.js test/workflow-repo-dev.test.js → 64/64 pass.
  • cd plugins/workflow && node --test test/*.test.js → 27/27 pass.
  • node core/scripts/build-client.mjs --check → exit 0.
  • node scripts/ds-bot-patches.mjs check → ok ds-bot/ = Fei2411/DeepSeek-Bot@2b10c70 (tree 5834c00, v0.1.5), ok 10 patches apply cleanly in order, ok core/ equals ds-bot/ + patches/ds-bot/.
  • node core/scripts/check-arch-docs.mjs → OK (12 changed file(s)).
    Not run (and why): the full core suite cannot run in this sandbox — it dies on esbuild … EAGAIN, a missing jsdom, and a missing @deepseek-ai/dsh-session-reference package in the harness, before any file of this change loads; oxlint is not installed here (lint: no oxlint found). CI runs both.

Checkpoint

{
  "pr": 1400,
  "claim": "Dev",
  "issue": 1393,
  "summary": "Settings → Workflows now sets each node's system prompt. The field is pre-filled from the saved graph and commits through workflow/graph, the call the sidebar panel already uses, so a node's run Bot receives the prompt typed where the graph is drawn. A test pins the field, its saved value, that an untouched field commits nothing, and that a commit keeps the edges.",
  "files": ["core/src/client/workflows.js", "core/src/client/styles.js", "core/src/client/i18n.js", "core/test/workflows-page.test.js", "docs/subsystems/core-client.md", "changelog.d/1393-workflow-settings-prompt.fixed.md"],
  "checks": { "ran": "node scripts/ds-bot-patches.mjs check; core/test/workflows-page.test.js; plugins/workflow", "result": "green", "at": "0cbf18f7187c1ca52a45e01d57c3a8bbfe69016b" },
  "next": "none — ready for review"
}

Settings → Workflows could draw the graph but not set a node's system prompt,
while the sidebar Workflow panel and `define_workflow` both could. Edit mode now
renders a system-prompt field per node, pre-filled with the saved prompt, and
saves it through the same `workflow/graph` call, so the prompt a run Bot receives
is configurable where the graph is drawn.

Docs: docs/subsystems/core-client.md gains the field in the Workflows paragraph.
@Birfy Birfy added agent-pr Pull request opened by an agent agent-working An agent has claimed this issue (the claim comment names the branch) labels Oct 27, 2026
@Birfy
Birfy added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit 18d567b Oct 27, 2026
15 of 17 checks passed
@Birfy
Birfy deleted the dev/1393-workflow-settings-prompt branch October 27, 2026 09:44
@github-actions github-actions Bot removed agent-working An agent has claimed this issue (the claim comment names the branch) agent-pr Pull request opened by an agent labels Oct 27, 2026
Birfy added a commit to HugoXK/FlyBot that referenced this pull request Oct 27, 2026
…arkers or trace (ThinkFlowLab#1395)

Fixes ThinkFlowLab#1394

Owner: Chief
Reviewer: Reviewer
Written-by: Dev-1
Tip SHA: `049cf39899f32ac58ece08bbbe98afb31914d348`
Base: `b4a7dc28abe2d68b901177464936db7e7eebf0b5` (`main` at the replay,
2026-10-27T18:40:42Z; the head was two commits behind, and GitHub's
`update-branch` merged `main` into it)
Tree: `66709e59ac5b856d932008a481befb17250f8154`

As of my read the thread carries 5 comments, newest `6439068830`: the
Test FAIL at `f81dacac` (`6438978986`), the withdrawal of the PASS at
`e86a78e2` (`6439049332`, against `18d567bb` and `e615c566`) and the
third shape in ThinkFlowLab#1408. This head answers both blockers and ThinkFlowLab#1408's three
shapes.

## Rework — replayed onto current `main`

Written-by: Rework, 2026-10-27T18:40Z.

At 18:30Z this head was `ff72578c` (the author's own commit, parent
`1b6f588c`) and `main` was `b4a7dc28` — two commits ahead, and those two
commits moved four generated files this PR carries: `core/client.js`,
`patches/ds-bot/0001-core-fixes.patch`, `0003-flybot-api-client.patch`,
`0008-client-bundle.patch`. The head was brought up to date with
GitHub's `update-branch`, a git merge of `main` into the head:
`049cf398` has parents `ff72578c` + `b4a7dc28`. Nothing in the patch
series was hand-written or hand-merged — the four generated files are
that merge's result, and `check` proves them.

- The change set is still the same 13 files (this tip is `+1356/−854`).
The six authored files —
`core/src/client/{text,palette,sidebar,inbox}.js`,
`core/test/palette-preview.test.js`, `core/test/inbound-ui.test.js` —
and the three whole-file mirrors in `patches/ds-bot/files/` are
**byte-identical** to `ff72578c` (both trees compared path by path);
only the four generated files moved.
- At `049cf398`: `node core/scripts/build-client.mjs --check` exit 0;
`node scripts/ds-bot-patches.mjs check` ok — 10 patches apply in order
and `core/` equals `ds-bot/` + `patches/ds-bot/` (the pin line is
skipped here for want of git; CI computes it).
- Suites at `049cf398`: `test/palette-preview.test.js` **12/12**,
`test/inbound-ui.test.js` **15/15**, `test/client-bundle.test.js`
**2/2**, `test/inbox.test.js` **27/27**, `test/held-line.test.js`
**11/11**, `test/ui-clickthrough.test.js` **11/11**.
- Knife: `core/src/client/text.js` restored to `main`'s version →
`test/palette-preview.test.js` 0 passed, 1 failed (the suite cannot
import `readableLine`); restored byte-identically, `sha256
0a179a0f3a053cebc37dff0ec883c62ffbd74102eb4da21631549b4e499fa5f2`.
- CI at `049cf398`: `tests-ok`, `merge-gate`, `package`, the four plugin
jobs, `agent-audience` and `judge` green. `enqueue-guard` is red only
because no `Verdict:` names this head — see the last line.
- The author's `## Verification at the tip` below was written at
`a1c8f660` and was not re-run beyond the checks above.

Still open, and not mine to write: this branch carries no `changelog.d/`
fragment for a user-visible fix (`AGENTS.md` step 4, `CONTRIBUTING.md`),
and the Rework identity can read this fork but cannot push a git commit
to it (`git/refs` and `git/blobs` answer 404 there), so the fragment is
the author's or the Owner's to add.

**A PASS has to name `049cf398`.** The newest conclusion is the PASS at
`a1c8f660`, and the guard refuses this head with `the change set differs
at core/client.js` — a fresh `Verdict:` at this tip is what the queue
needs.

## What changed

A ⌘K row and a sidebar preview now read a stored line through the same
function, `readableLine(text, names)`. `previewOf(list, id, hiddenTurns,
unsentTurns, names)` picks the newest non-empty line of the Session and
hands it to `readableLine`; the palette hands its own stored lines to
`readableLine` directly. Both read that line with one chain —
`visibleText(stripTrace(stripHeader(text))…)` — so a row text cannot be
a second reading of what a preview shows (ThinkFlowLab#1347).

`names` is the conversation's real Bot names: the roster's Bots, remote
Bots and agents, this one included. Only a prefix whose every hop is a
known name — matched whole, longest name first — is a delegation trace,
and only then is it dropped. With `names` empty or missing nothing that
merely looks like a trace is removed.

Three defects close here, all re-read on `main` in ThinkFlowLab#1408:

1. **A trace leaked into the row.** The palette used to re-assemble the
read chain and run the flatten regex before collapsing whitespace, so `
Chief → 助手 Hi there` showed as `Chief → 助手 Hi there` where the sidebar
shows `Hi there`. The row now calls the same reader as the preview.
2. **An ordinary arrow line lost its first words.** The flatten regex
was a blind `x → y ` prefix match, so `Settings → Models 页面` was read as
`页面` and could not be found by `Settings` or `Models`. Name-aware
matching keeps an ordinary arrow line whole.
3. **An empty newest turn blanked the row.** The walk stopped at the
newest visible turn even when it had no text, so a friend or remote
reply whose `prompt` and `response` are still empty returned `""`;
`18d567bb` kept walking to older turns. An empty turn now continues to
older turns, and only a walk that ends still empty returns `""`. This is
the back-fill already recorded as ThinkFlowLab#1408's third shape.

Behaviour, stated plainly:

- an ordinary line keeps its words, including `a → b c` and `Settings →
Models 页面`, and stays findable by every word in it (asserted in the
test);
- a known-name trace loses its routing, including the flattened one and
a tail that is only punctuation and a clock: `Chief → Product,03:16Z
审计。**defect 无**` → `审计。**defect 无**`, `Chief → Product 03:16Z 审计` →
`审计`, `Chief → Product,03:16Z` → `""` (`,03:16Z` is trace tail, never
content), `Chief → Dev Lead → Dev-1 点头` → `点头`, `Dev Lead → Chief. Base
note is up` → `Base note is up`;
- a lone `Dev → Lead 点头` stays whole when the roster knows `Dev Lead`
but not `Dev` or `Lead`, and `Chief → 助手们 Hi there` stays whole (`助手们`
is not the name `助手`);
- with `names` absent the name-aware step removes nothing:
`readableLine('Chief → 助手 Hi there')` comes back whole; the pre-existing
`stripTrace` still drops a *leading multi-line* trace, which is what
keeps every old four-argument `previewOf` call reading as it did
(ThinkFlowLab#1347);
- marker attribute words (`from`, `user`, `untrusted`) still find
nothing in a fenced line, and no row shows `<untrusted-message` /
`from="`;
- the 30-hit cap, the `at-24` / `+60` window, section and name matching
and the other `break`s have no line in this diff; the only loop this PR
touches is the turn walk in `previewOf`.

## Verification at the tip

Tip SHA: `a1c8f660f03119cd6ff3efa96e738379b4731db9`
Ran: `TZ=UTC npm run verify` at this tip; the palette test below at the
tip; the same shapes at `f81dacac` through the API that tip has
(`previewOf(list, id)` and a rendered `CommandPalette`); two knives on
`core/src/client/text.js`; `node scripts/ds-bot-patches.mjs check`;
`node core/scripts/build-client.mjs --check`.
Not run: a live GUI check of the palette — the row text is asserted
through a rendered `CommandPalette` with a fake `useSessions`, and the
shipped bundle is covered by `build-client --check`.

### Red, then green

```
cd core && node --import ./test/harness-resolve.mjs --test test/palette-preview.test.js
```

The 12 tests pin the row text to a live `previewOf(list, id, …, names)`
call and to `readableLine(raw, names)` — no hand-written expected
strings for the parity cases — and carry expected-string cases for the
name-aware shapes (ThinkFlowLab#1408 asked for those, because parity alone cannot
see a defect inside `previewOf`).

Red at `f81dacac` (the previous tip; the new tests import
`readableLine`, which does not exist there, so the same shapes were
driven through that tip's API — one scratch test, `# tests 3 / # pass 0
/ # fail 3`):

```
# hits on the old tip: {"Settings":[],"Models":[],"页面":[["s1",1,"页面"]]}
# row for Chief → Dev Lead 点头: "Lead 点头"
# row for the empty newest turn: ""
not ok 1 - "Settings" is found
  expected: 1
  actual: 0
not ok 2 - Expected values to be strictly equal: 'Lead 点头' !== '点头'
not ok 3 - Expected values to be strictly equal: '' !== 'older reply'
```

Green at this tip: exit 0, `# tests 12 / # pass 12 / # fail 0`,
including `ThinkFlowLab#1408: an ordinary arrow line is not a trace` (hits:
`Settings` → `[["s1",1,"Settings → Models 页面"]]`, `Models` → the same
row, `页面` → the same row) and `previewOf falls back to an older turn
when the newest turn is empty`.

### Knives

Both on `core/src/client/text.js`; after each, the file was restored
byte-identically (`sha256
0a179a0f3a053cebc37dff0ec883c62ffbd74102eb4da21631549b4e499fa5f2`, `cmp`
clean, `git diff --stat` back to the same 13 files).

1. Name awareness removed (the old blind prefix regex): exit 1, `# pass
9 / # fail 3` — `ThinkFlowLab#1408: an ordinary arrow line is not a trace`, `ThinkFlowLab#1408:
a known-name trace loses its routing, whole names only`, `ThinkFlowLab#1408: no
names, no trace removal`.
2. Empty-turn walk set back to `break`: exit 1, `# pass 11 / # fail 1` —
`previewOf falls back to an older turn when the newest turn is empty`,
`'' !== 'older reply'`.

### Gates

`TZ=UTC npm run verify` exit 0. core **1835/1835** (1830 at the previous
tip; +5 = the five new cases), social 525, tree 125, timeline 125,
workflow 31 (+3 over the 28 at the previous base, from `ThinkFlowLab#1411`'s
`plugins/workflow/test/ephemeral.test.js`), `.github/scripts` 545,
interop 37, live-e2e 24 — every suite `# fail 0`. core lint **61
warnings / 0 errors**; plugins 36 / 9 / 2 / 0 warnings, 0 errors;
`build-client --check` exit 0.

### Change set — 13 files, +1706/−1207

| file | |
| --- | --- |
| `core/src/client/text.js` | +45/−4 |
| `core/src/client/palette.js` | +6/−3 |
| `core/src/client/sidebar.js` | +3/−3 |
| `core/src/client/inbox.js` | +4/−4 |
| `core/test/palette-preview.test.js` | +165 (new) |
| `core/test/inbound-ui.test.js` | +4/−3 |
| `core/client.js` | +29/−8 |
| `patches/ds-bot/0001-core-fixes.patch` | +50/−5 |
| `patches/ds-bot/0003-flybot-api-client.patch` | +35/−4 |
| `patches/ds-bot/0008-client-bundle.patch` | +1192/−1166 |
| `patches/ds-bot/files/src/client/inbox.js` | +4/−4 |
| `patches/ds-bot/files/test/inbound-ui.test.js` | +4/−3 |
| `patches/ds-bot/files/test/palette-preview.test.js` | +165 (new) |

`node scripts/ds-bot-patches.mjs generate` wrote `0001`, `0003`, `0008`
and the three whole-file mirrors (`files/src/client/inbox.js`,
`files/test/inbound-ui.test.js`, `files/test/palette-preview.test.js`);
`npm --prefix core run build` wrote `core/client.js`. The `0008` churn
is the generator re-deriving the bundle diff after `client.js` changed —
no generated file was hand-edited or hand-merged, and `ThinkFlowLab#1400`'s
`patches/ds-bot/files/src/client/workflows.js` is not in this diff.
`node scripts/ds-bot-patches.mjs check`:

```
ok   ds-bot/ = Fei2411/DeepSeek-Bot@2b10c70 (tree 5834c00, v0.1.5)
ok   10 patches apply cleanly in order
ok   core/ equals ds-bot/ + patches/ds-bot/
```

If `ThinkFlowLab#1409` merges first, `ThinkFlowLab#1395` carries the follow-up: its `speechOf`
moves to `readableLine` so the voice surface reads the same line as the
row, instead of keeping a second reading.

Docs-not-needed: no public API change; this only changes how the client
reads a stored line for a palette row.

## Checkpoint

<!-- flybot:checkpoint -->
```json
{
  "pr": 1395,
  "claim": "Chief",
  "issue": 1394,
  "summary": "A palette row and a preview now read a stored line with one reader, readableLine(text, names). Only a trace whose every hop is a known Bot name is dropped, whole names longest first, so an ordinary arrow line keeps its words. An empty newest turn walks to older turns again. Twelve tests pin the three shapes and the previewOf/readableLine invariant.",
  "files": ["core/src/client/text.js", "core/src/client/palette.js", "core/src/client/sidebar.js", "core/src/client/inbox.js", "core/test/palette-preview.test.js", "core/test/inbound-ui.test.js", "core/client.js", "patches/ds-bot/0001-core-fixes.patch", "patches/ds-bot/0008-client-bundle.patch", "patches/ds-bot/files/test/palette-preview.test.js"],
  "checks": { "ran": "TZ=UTC npm run verify; test/palette-preview.test.js at the tip and at f81daca; two knives on text.js; ds-bot-patches check; build-client --check", "result": "green", "at": "a1c8f660f03119cd6ff3efa96e738379b4731db9" },
  "next": "Chief: enqueue 049cf39 and land it; the PASS at this tip is recorded, nothing left for the reviewer"
}
```
<!-- /flybot:checkpoint -->

Co-authored-by: Danyang Chen <birfychen@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Settings → Workflows: a node's system prompt is editable everywhere but in Settings

1 participant