Repository navigation
fix(workflow): a run Bot takes the profile model, not the model of the Bot that started the run - #1610
Conversation
A run Bot started through run_workflow inherited the model of the Bot that started it: spawnAll wrote the caller into spec.createdBy, and the roster reads that field as "a Bot made this Bot", so a node with no model named started on the caller's model while the same graph started from the board, the canvas or the API took the profile default (ThinkFlowLab#1582). The caller is a connector ceiling, not a parent: it already travels in cappedBy (runCaps names the caller and the graph's author), and capsOf reads that field. spawnAll now writes cappedBy only, the plugin passes cappedBy only, and the roster never treats an ephemeral Bot's createdBy as a maker, so a run record that still carries it keeps the profile default too. ThinkFlowLab#1558's remote-caller refusal read createdBy, which would have made it dead code: remoteCaller now reads the same caps list, so a run started by a friend's Bot still reaches no connector and is still masked out of schemas(). Tests, the ds-bot patch mirrors and the subsystem docs move with it. Written-by: FlyBot Dev-1
|
@Birfy review request — PR #1610 at Fixes #1582 (P-109): a run Bot no longer takes the model of the Bot that started the run. As of my read: 1 comment (issue #1582 has 7), no review and no Verdict at this head. At
Written-by: FlyBot Lead |
|
Verdict: PASS at b1eae95 Ran at this tip (two checkouts, the Red first — the same two suites with only the four changed source files reverted to their The two core failures are Not run: Review points:
|
Fixes #1582
Owner: Dev-1
Written-by: FlyBot Dev-1
Reviewer: Reviewer
Base: 655aa71 (live
main; the branch was first cut fromd961774cand rebased onto the new tree with no file overlap)Tip: b1eae95 (fork head; tree
3a4bb90af1033c3e46e2263268fba42f2cbf54dd)Tree: 3a4bb90af1033c3e46e2263268fba42f2cbf54dd
What
A run Bot with no model named took the model of the Bot that started the run.
#1552wrote the caller intospec.createdBy(plugins/workflow/src/ephemeral.js), and the roster reads that field as "a Bot made this Bot": a Bot another Bot made, with no model named, starts on its maker's model (core/src/host/roster.js). A factory graph names no model at all, so the board, the canvas and the API ran it on the profile default, while one Bot'srun_workflowcall ran every node of the same graph on that Bot's model — model, cost and tool capability following whoever pressed start, with neither the graph nor the run record showing it.Three changes in this one tip:
spawnAllcarries the caller incappedByonly — it already did, throughrunCaps(caller, author)— and the plugin passescappedByonly. A run Bot carries nocreatedBy.createdByas a maker, so a record that still carries the field (an older spec, a caller that passes one) takes the profile default too. An explicit workflow/node/seated model still wins.#1558's remote-caller refusal now reads the same caps list (capsOf), so droppingcreatedBydoes not turn it into dead code: a run started by a friend's Bot is still refused and its connectors are still masked out ofschemas(). That predicate is inmainnow (#1590), so this branch carries the two-line move rather than a second PR.Why this shape
The connector ceiling never needed
createdBy:cappedByalready named the caller and the graph's author, andcapsOfreads[createdBy, ...cappedBy]. The caller is a ceiling, not a parent. The alternative — keepingcreatedByand passing the resolved model explicitly — would have left every run Bot looking like the caller's child everywhere else the roster reads that field (tasks, the kanban parent link, capability ownership).The
#1558half is not duplicated: its predicate, its tests, thepatches/ds-bot/mirrors and the docs move with the field in this tip, and its existing cases stay green.caller-offnow builds a run whose cap iscappedBy: ['writer']; acreatedBy-only case is kept so a pre-#1582 record stays locked.Red first, green after
With the sources at
655aa714and only this tip's tests applied (the latermainadded#1606and#1609, which touch none of these files):core/test/runbot-model.test.js— 1 test, 1 fail:a run Bot must not take the caller model,actual: 'deepseek/deepseek-v4-pro',expected: 'deepseek/deepseek-v4-flash'.core/test/connector-access.test.js— 24 tests, 23 pass, 1 fail:a run Bot started by a friend's Bot reaches no connector, thecappedBy: ['friend']case (the base readscreatedByonly).plugins/workflow/test/ephemeral.test.js— 214 tests, 211 pass, 3 fail: the three#1544specs that assertedspec.createdBy.At this tip, the same two core files are 25/25, the workflow suite is 214/214.
The probe from the issue is kept as a product test, with two adjustments it needs to run as one:
ephemeral: true(the shapefb.bot.createactually receives; the pasted probe passed the{ plugin }object, which the host ignores, so that Bot was only hidden), and the fourconsole.logevidence lines dropped.Verification
TZ=UTC ARCH_DOCS_BASE=655aa714 npm run verify— exit 0: core 1903, social 528, tree 125, timeline 129, workflow 214,.github549, interop 37, live-e2e 24; oxlint 0 errors (the repository's existing warnings only);arch-docs: OK (14 changed file(s));build-client --checkclean;ds-bot-patches check: pin ok, 10 patches apply,core/equalsds-bot/+ patches.node .github/scripts/changelog.mjs check— 2 fragment(s), well formed, exit 0.Not run: no live instance (no instance access), no browser,
dev/bench.sh smoke(this workspace has no DSH checkout for the bench); CI was not waited on.Docs
docs/subsystems/core-host.md(the model and cap bullets, and the#1555bullet) anddocs/subsystems/workflow.md(connector ceiling) are updated in this tip, so noDocs-not-needed:trailer is used.Files
Checkpoint
{ "pr": 1610, "claim": "Dev-1", "issue": 1582, "summary": "A run Bot no longer takes the model of the Bot that started the run: the caller travels in cappedBy, not createdBy, and the roster never reads an ephemeral Bot's createdBy as a maker. The connector ceiling is unchanged, and the remote-caller refusal now reads the same caps list, so a run started by a friend's Bot still reaches no connector and the node's tools list cannot hand it one. An explicit model still wins.", "files": [ "plugins/workflow/src/ephemeral.js", "plugins/workflow/index.js", "core/src/host/roster.js", "core/src/host/connector-access.js", "core/test/runbot-model.test.js", "core/test/connector-access.test.js", "plugins/workflow/test/ephemeral.test.js", "patches/ds-bot/0002-flybot-api-host.patch", "patches/ds-bot/files/src/host/connector-access.js", "docs/subsystems/core-host.md", "docs/subsystems/workflow.md", "changelog.d/1582.fixed.md" ], "checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=655aa714 npm run verify (core 1903, workflow 214, all suites; red first 2 core + 3 workflow failures, green 25/25 and 214/214)", "result": "green", "at": "b1eae950619265443cec45ab869fdaeb14626242" }, "next": "none — ready for review" }