Repository navigation
Workflow stage drawer Edit plane: parallel Reviewer/Tester cards overlap and frame label clipped - #1747
Merged
Birfy merged 1 commit intoOct 30, 2026
Conversation
Contributor
Author
|
@Birfy please review this tip State at the tip as of my read:
One request per tip: this is the only one on |
Collaborator
|
Verdict: PASS at 6b917a9 Ran at this tip:
|
At the 364px stage drawer the fitted Edit plane left the delivery template's Reviewer and Tester cards 4px apart, so each card's out-port and remove button sat under its neighbour, and `parallelFrameStyle` ended the frame 10px above the cards it names. A parallel row that the fit squeezes is now drawn at that row's `flowLayout` column width on its fitted centres, so it keeps the 12px row gap and stays a fitted plane whose drags still commit world coordinates; the frame adds the 20px label band above the cards and a 10px pad below them. Fixes ThinkFlowLab#1738
HugoXK
force-pushed
the
dev/1738-workflow-stage-drawer-edit-plane-paralle
branch
from
October 30, 2026 02:37
6b917a9 to
bddf374
Compare
Collaborator
|
Verdict: PASS at bddf374 Ran at this tip:
|
HugoXK
added a commit
to HugoXK/FlyBot
that referenced
this pull request
Oct 30, 2026
…) outside its frame and off the canvas (ThinkFlowLab#1762) Fixes ThinkFlowLab#1752 Owner: Dev2 Reviewer: Birfy Written-by: Dev-2 ## What the bug was Settings → Workflows drew the authored 960px plane at 1:1 inside a canvas that is far narrower, with the plane centred as a zero-width line, and it drew a parallel layer as one row whatever the stored `y` said. A workflow whose branch card was stored lower and near the right edge therefore reproduced all three reported symptoms: - the 取证 / 真机取证 card was cut by the canvas's right edge, its status chip reduced to `Q…` (view) / `Que…` (Edit); - the dashed "Parallel branch, then join" frame ran off the same edge; - the Claim → 取证 wire ended with its arrowhead on the clip line, and the horizontal scrollbar was the only way to see the card. ## Red first Three tests in `core/test/workflows-page.test.js` at the base `7f4aa2a7`, run against the base sources with only the branch's test file in place (`node --import ./test/harness-resolve.mjs --test --test-name-pattern="1752" test/workflows-page.test.js`): 3 tests, 0 pass, 3 fail. ``` not ok 1 - a parallel frame encloses a member stored lower than its partner (\ThinkFlowLab#1752) error: 'the frame 90..182 leaves forensics (159..231) outside' not ok 2 - the Settings graph fits its plane into the canvas width (\ThinkFlowLab#1752) error: 'the canvas wraps the plane in a fit layer' not ok 3 - a fitted graph keeps the right-hand card and the wire into it inside the canvas (\ThinkFlowLab#1752) error: |- + actual - expected + 'undefined' - 'function' ``` After the fix the same pattern is 3/3, and the whole file is 14/14. ## Fix - `fitScale(nodes, width)` (exported pure helper): one `scale()` from the widest content reach (`|x| + 108`, so the dashed frame counts), capped at 1 and floored at 0.5 when the canvas is too narrow for half size — below the floor the canvas scrolls rather than shrinking text past readable. - `GraphCanvas` measures its canvas (`getBoundingClientRect` plus a `ResizeObserver`, the same shape the drawer's fitted plane uses) and wraps the plane in `.bt-wf-fit` (`transform: scale(...)`, `transform-origin: 50% 0`; `.bt-wf-stage` takes the scaled height). Cards, dashed frames and the wire SVG all sit inside that one wrapper, so all three share the same transform. - The parallel frame is the union of its members' boxes: `left = min(x) - 108`, `right = max(x) + 108`, `top = min(y) - 18`, `bottom = max(y) + 72 + 10`. A member the editor stored lower is enclosed; a same-`y` pair gets a 100px band (was 92). - Dragging still commits authored plane coordinates: the pointer delta and the drop offset are divided by the current `scale`. The display transform never becomes the stored value. This is display-only scaling, not a room-style per-layer re-layout, so positions the user dragged stay put (the issue's Expected allows "or the graph scales to fit"). ## Scope and lines Real changes, excluding the generated `core/client.js`, the two `patches/ds-bot/files/` mirrors and `patches/ds-bot/0008` (all produced by the one `generate` step): | file | lines | | --- | --- | | `core/src/client/workflows.js` | +47/−12 | | `core/src/client/styles.js` | +3/−0 | | `core/test/workflows-page.test.js` | +72/−2 | | `docs/subsystems/core-client.md` | +1/−1 | | `changelog.d/1752-settings-graph-fit.fixed.md` | +1/−0 | 139 changed lines (136 code + 3 docs/changelog), under the 150 stop-line. Generated at this base (not counted above): `core/client.js` +128/−89; `patches/ds-bot/0008-client-bundle.patch` +1359/−1496 (whole-patch regeneration); `patches/ds-bot/0003-flybot-api-client.patch` +27/−24 — of which **3** lines are the real `styles.js` rules and **24** are hunk-header drift, kept because this patch's application result does change; `patches/ds-bot/files/src/client/workflows.js` +47/−12; `patches/ds-bot/files/test/workflows-page.test.js` +72/−2. **Drift:** `generate` is not byte-idempotent on this box (git 2.34.1 writes hunk headers with function context). It rewrote `0001-core-fixes.patch` (44/44), `0003` and `0014-flybot-branding.patch` (2/2) for header reasons alone. `0001` and `0014` were restored to the base bytes; their application result was unchanged, and `check` stays green after the restore. `0003` keeps the generated form because it carries the 3 real CSS lines, so its remaining 24 header pairs are environment drift, not hand edits. No hunk was hand-edited, and `0008` is the whole-chain regeneration. `check`, twice on this tree, prints the same three lines: ``` 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/ ``` `arch-docs` runs against the rebase base `7f4aa2a70766a7292cf5474ec8a7bbc364f5d1ea`; the subsystem doc line for the Settings graph is in this diff, so no `Docs-not-needed:` trailer is needed. ## What I ran ``` node scripts/ds-bot-patches.mjs check # twice, same three lines node core/scripts/build-client.mjs --check # silent, exit 0 TZ=UTC ARCH_DOCS_BASE=7f4aa2a7 npm run verify # green at the tip ``` ## What I did not run - No browser screenshot: jsdom cannot measure text, so the chip is pinned by geometry (the chip is inside the card, and the third test keeps the card's right edge inside the scaled half-width). - No `dev/bench.sh smoke`: client-only change. - No `ThinkFlowLab#1745`, `ThinkFlowLab#1747`, `ThinkFlowLab#1752` sibling branches touched; this diff carries no `patches/ds-bot/0002-*` and no `patches/ds-bot/files/src/host/*` (the parallel batch's generation is untouched). ## Gates at this head Head: `575774521864af328cd68e7dbe2557f83e2cbab3` (tree `8a3c9f43862ad36b1f82b0a43f165ddb2ec26cae`), base `7f4aa2a70766a7292cf5474ec8a7bbc364f5d1ea`. `enqueue-guard` on this head, verbatim: ``` enqueue ThinkFlowLab#1762: no `hold` and the gate passes ``` The check's own decision line is the same one (`enqueue ThinkFlowLab#1762: no `hold` and the gate passes`), with **no PASS needed: computed tier T1**, and the fork exemption for the issue reference and `agent-pr` — this pull request lives on a fork. The full suite at this head (`TZ=UTC ARCH_DOCS_BASE=7f4aa2a7 npm run verify`, exit 0): core **2099**, social **539**, tree **125**, timeline **129**, workflow **289**, `.github` **549**, interop **37**, live-e2e **24**, every suite 0 fail; `arch-docs: OK (10 changed file(s))`; `10 patches apply cleanly in order`; `build-client --check` silent. ## Checkpoint <!-- flybot:checkpoint --> {"pr": 1762, "claim": "Dev2", "issue": 1752, "summary": "The Settings Workflows canvas now fits the authored graph into the canvas with one scale on a wrapper, so cards, the dashed parallel frame and the wires share the transform and nothing is clipped at the right edge; the frame is the union of its members' boxes, so a member stored lower is enclosed. Dragging divides the pointer delta by the scale, so authored coordinates are unchanged. Tests: three new cases in core/test/workflows-page.test.js (red first), 14/14 in that file; full verify green at the tip.", "files": ["core/src/client/workflows.js", "core/src/client/styles.js", "core/test/workflows-page.test.js", "docs/subsystems/core-client.md", "changelog.d/1752-settings-graph-fit.fixed.md"], "checks": {"ran": "TZ=UTC ARCH_DOCS_BASE=7f4aa2a7 npm run verify", "result": "green", "at": "575774521864af328cd68e7dbe2557f83e2cbab3"}, "next": "none — ready for review"} <!-- /flybot:checkpoint -->
HugoXK
added a commit
to HugoXK/FlyBot
that referenced
this pull request
Oct 30, 2026
…y cards that wait (ThinkFlowLab#1761) Fixes ThinkFlowLab#1758 Three defects in the digest text a Project owner receives, all in `core/src/host/project-assistant.js`: a quiet window compared as wall-clock text against a due instant, silent truncation past five waiting decisions, and cards that wait on nobody listed as waiting on the owner. Base: `7f4aa2a70766a7292cf5474ec8a7bbc364f5d1ea` (the tip after ThinkFlowLab#1747 merged; that merge touches no file this PR touches). Head: `f01a17c6c9a52c0e3e047447292960f22a12f9de` (tree `8031b5b95085ead0d199dea0b5d52e93e9b72e18`; the locally verified commit `8a67f9a0e80e6c556810dd944c5ec09d3384dc4f` has the same tree — the push helper rebuilt the commit). ## What changed - **P-194 — quiet window vs. due instant.** The due time is 09:00 Asia/Shanghai, an *instant*; a quiet window is wall clock in the owner's own zone. Comparing the string `09:00` with `inQuietHours` compared two different zones' wall clocks, so an owner outside +08:00 was dropped on the wrong day. The due instant is now computed (`Date.parse(day + 'T09:00:00+08:00')`, read back with `clockIn(quiet.zone, …)`) and compared as a clock in the owner's zone. The held label uses the instant the digest was actually held at, and the drop reason names the due clock rather than the literal `09:00`. - **P-197 — overflow line.** The personal block stays capped at five; when more than five cards wait, a sixth line says `… and N more waiting on you`. Five or fewer print exactly the bytes they printed before. - **P-195 — only what waits on the reader.** The personal block now holds `blocked` ("needs your call") and `result-ready` ("ready for your acceptance") cards, read from the card's own status. A card still `open`, `claimed` or `working` waits on nobody and appears only in the shared in-progress section. A row that carries no status keeps the older answer — absence is not evidence that the card is moving. ## Red first Red first drives the digest through `rt.projectAssistant.setOwner(...)` directly: that endpoint has no channel case (`core/src/host/store.js:608` handles `project-assistant-owner`, but `core/src/host/channel.js:670-677` lists only the eight `project-*` endpoints, so the request falls to `default` and fails `Unknown endpoint` / `bot/not-found` at `:716`). No tool, hook, CLI or schedule path reaches it either, so no product-level path exists today; the window matters once P-198 wires it. What is visible today, in two classes: - **P-194 is a P-198 prerequisite:** without a `quiet` record, `quietOf` (`project-assistant.js:294`) yields `{on:false}`, so the `coversDue` / label / reason paths cannot fire. - **P-197 and P-195 write the digest text today but have no live producer:** the running instance has `projects: 0`, and `project-create` has no client caller and no UI path, so no Project exists to produce a digest. On base `7f4aa2a7` with only the source reverted (`# tests 22 / # pass 16 / # fail 6`), the six failures are: ``` not ok 10 - a quiet window holds the digest and delivers it once, labelled, when it ends not ok 12 - #P-194: a quiet window in the owner's zone is compared with the due instant, not with 09:00 not ok 13 - #P-194: a window that really covers the due instant still drops the digest not ok 14 - #P-197: more than five decisions say how many are still waiting not ok 16 - #P-195: only the cards that wait on the reader reach the personal block not ok 20 - a card lists for the owner of the Bot that owns it, through the real task and board path ``` With the fix: `22/22` at `TZ=UTC` and `22/22` at `TZ=Asia/Shanghai`. The process time zone changes nothing; the owner's zone does. Same probe, base source vs. this branch, owner quiet `09:00–11:00` @ `Asia/Shanghai` — the reason is byte-identical in both process zones: `quiet hours 09:00–11:00 cover 09:00, so nothing is delivered — the expected outcome`. Owner quiet `08:00–10:00` @ UTC, `01:00Z`: base drops the digest with `cover 09:00`; this branch delivers once and carries no reason. ## Two existing tests changed - **Test 10** now expects `held from 08:00` instead of `09:00`. The old string was `held from 09:00 to 08:45` (base `:325`) — it starts *after* it ends, because `09:00` is exactly the `DIGEST_TIME` constant (`project-assistant.js:24`) the old code printed in the label (`:375`) and compared in `inQuietHours(quiet, DIGEST_TIME)` (`:365`): the old expectation asserted the defect itself. The label now starts at the instant the digest was actually held (`clockIn(quiet.zone, held.at ?? at)`), which is `08:00` in UTC. - **Test 20's** old fixture used only `task-create` (default `status: 'open'`) and still expected two cards in the personal block, which P-195 no longer allows. The new fixture drives both cards through `task-accept → task-start → task-wait-input` to `blocked`, and adds a `working` card with the `a card the owner's Bot is still working on waits on nobody` negative assertion. The four original title assertions are byte-identical; the assertions got stronger, not looser. Everything else is new tests, red as written. ## Generated files `core/src/host/project-assistant.js` and its test are mirrored whole, so this diff carries exactly two generated paths: `patches/ds-bot/files/src/host/project-assistant.js` and `patches/ds-bot/files/test/project-assistant.test.js`. No change to `patches/ds-bot/0002-flybot-api-host.patch` and none to `patches/ds-bot/files/src/client/*`. `generate` is not byte-idempotent in this repository: on this clean base it also rewrites `0001-core-fixes.patch`, `0003-flybot-api-client.patch` and `0014-flybot-branding.patch` with 44 / 23 / 2 lines of pure hunk-header and count drift, and `0008-client-bundle.patch` (192 content lines differ in the patch, but the applied bytes are unchanged — `check` is green with the regenerated `0008` as well as with the base bytes). All four are restored to the base bytes, so the diff carries only the two mirrors; the applied bytes are unchanged, and `check` stays green either way. ``` $ 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/ ``` ## What I ran At this tip, `TZ=UTC`, `ARCH_DOCS_BASE=7f4aa2a70766a7292cf5474ec8a7bbc364f5d1ea`: - `npm run verify` — exit 0. Suites: core **2101**, social **539**, tree **125**, timeline **129**, workflow **289**, `.github` **549**, interop **37**, live-e2e **24** — every suite fail 0. - `node core/scripts/build-client.mjs --check` — silent, exit 0 (`core/client.js` untouched; the bundle does not carry `project-assistant.js`). - `node scripts/ds-bot-patches.mjs check` — the three lines above. - `arch-docs: OK (5 changed file(s))` against the base ref above. - focused file: `22/22` at `TZ=UTC` and `22/22` at `TZ=Asia/Shanghai`. Not run: the real-model `bench.mjs` (minutes, spends tokens, not part of `verify`). No live instance was touched. ## Size Measured as add + delete, generated files and mirrors excluded: **190 lines of change (+164/−26)** — source +48/−17 (65 changed lines), tests +101/−4, docs +15/−5. ## Gates at this head At `f01a17c6c9a52c0e3e047447292960f22a12f9de`, 13 of 14 check runs are green: `tests-ok`, `merge-gate`, `judge`, `package`, `agent-audience`, `reviewed tip (report only)`, `risk tier (report only)`, `plugins (social/tree/timeline/workflow)`, `Node 24`. `enqueue-guard` is red only for the missing Verifier report: ``` do not enqueue ThinkFlowLab#1761: no Verifier report names the tip f01a17c ``` Its summary states the same fork rule the earlier PRs saw: `the head lives in a fork: the issue reference and agent-pr rules are not asked of it`. Nothing else refuses this head.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1738
Owner: Dev2
Reviewer: Reviewer
Written-by: Dev-2
What changed
The stage drawer's Edit plane draws the
deliverytemplate's Reviewer ‖ Tester pair, and at a 364px plane the fitted plane put the two 168px cards 4px apart. Each card's out-port dot hangs 5px past its right edge and its remove button 8px, so both landed under the neighbour card — a click on Reviewer's × selected Tester.parallelFrameStylealso drew the frametop - 20withheight = cards + 10, so it ended 10px above the cards it names, and its right edge sat 2px from the panel edge.Two changes in
plugins/workflow/client.js, no generated file touched:editPlane: a parallel row the fit squeezes (fitted gap below the row's 12pxFLOW_GAP_X) is drawn at that row'sflowLayoutcolumn width — 154px at a 364px drawer, 2px narrower than the 156px column the Read-only Flow tab already paints this pair at — on its fitted centres, its fitted y and its existingwirePathfold. The row keeps a 18px gap, and because the plane stays a fitted plane (wrapped: false), a drag still commits world coordinates and a dropped Bot still stores the panel point, exactly as before. A row that cannot keep the gap even at that width (3+ branches) keeps the existing laid-out fallback from workflow: the drawer's Edit plane overlaps a 3+ way parallel step's cards, so a branch's out-port cannot be clicked #1677/workflow: the drawer's Edit plane draws the parallel frame and wires from the old 168x72 card size, so the frame is clipped and wires cross cards #1693.parallelFrameStyle: the frame adds the 20px label band above the cards and a 10px pad below them (FRAME_LABEL/FRAME_PAD), so the box encloses every card it names.Rebased onto
ed5410c2(keeps#1756)maingained#1756— the room Flow's idle-run states, in this same file — while this PR waited, so the branch was rebased ontoed5410c2with one conflict inplugins/workflow/client.js. It is resolved by keeping#1756'snodeState(status, node.id)for the card's class and layering this PR's fitted width onto the style line.#1756's two#1753tests still pass unchanged, and nothing of it was modified.Measured at the 400px drawer (plane 364px),
deliverytemplatecalc(100% - 16px)cap resolves to 328px inside a 360px frameThe label itself was not cut at 364 in the before state — its cap (328px) already exceeded its one-line width; what the report saw as "tight against the right edge" is the frame's 362px right edge. The new test pins the cap at ≥ 205px so a later narrowing cannot force the label to wrap, and pins the frame inside the panel.
Red first
The two new tests fail on
ed5410c2with the fix reverted and pass at the tip; the two pins this branch also updates show the same base behaviour. Output at the base, run with only the test file from this branch:The two #1677/#1693 pins that still hold are kept: the four Read-only planes at 368 stay byte-identical, and the 2-way Edit plane keeps its centres, its wire fold and every non-parallel card at 168px; the two entries that change are the two parallel cards (168 → 154, x +7) and the #1693 frame-height unit test, which now asserts the frame encloses the cards (96 + 20 + 10).
What I ran
At the pushed tip
bddf374b8ee5534b73d2fea90d0a7fde8ab99d06(tree77dae91beb, baseed5410c2):cd plugins/workflow && npm test→ 289 tests, 289 pass, 0 fail;test/client.test.jsalone → 101, 101 pass.TZ=UTC ARCH_DOCS_BASE=ed5410c2 npm run verify→ exit 0; core 2096, social 539, tree 125, timeline 129, workflow 289,.github549, interop 37, live-e2e 24, every suite 0 fail;arch-docs: OK (4 changed file(s));10 patches apply cleanly in order;build-client --checksilent.ed5410c2with onlyplugins/workflow/test/client.test.jstaken from this branch (no source change), same worktree base.What I did not run
dev/bench.sh smoke(noindex.jsorsrc/host/change, and the drawer is client-only).wf-parframe style,wf-plabelstyle), not from a screenshot.Docs: yes —
docs/subsystems/workflow.md(theplugins/workflow/client.jsrow) andchangelog.d/1738-workflow-drawer-parallel-pair.fixed.md. Nopatches/ds-bot/**and nocore/client.js: this plugin has nofiles/mirror and is not part of the core bundle.Known residual (not fixed here, not verifiable in CI)
parallelFrameStyle, whose bottom comes fromnode.h ?? NODE_H, and the fitted branch hands its nodes no measuredh, so the frame always seesNODE_H = 72(plugins/workflow/client.js:278, the same line on the base;flowLayoutdefaults to it at:767). A card taller than 72px in a real browser leaves the frame's bottom edge short..wf-nodeplugins/workflow/client.js:2286(before this branch,4916bd5f::2262) has no fixed height and nooverflow: hidden, and its width is overridden by the inlinespot.w;.wf-nm:2293(base:2269) has nowhite-space: nowrap/text-overflow: ellipsis(the role line.wf-rlbeside it does) and carries a 9.5px inline status chip.wf-chip-inline:2245(base:2221); the text column is.wf-nt:2292(base:2268). Inside a 154px card that column is about 96px (154 − 20 padding − 2 border − 28 avatar − 8 flex gap), so "name + chip" can run past one line. jsdom has no layout engine, so no test here can see it..wf-nman ellipsis, which is a separate issue outside Workflow stage drawer Edit plane: parallel Reviewer/Tester cards overlap and frame label clipped #1738.Checkpoint
{
"pr": 1747,
"claim": "Dev2",
"issue": 1738,
"summary": "The stage drawer's Edit plane no longer squeezes a two-Bot parallel step. A row whose fitted cards would sit closer than the 12px row gap is drawn at that row's flowLayout column width (154px at a 364px drawer) on its fitted centres, so the delivery template's Reviewer and Tester cards keep an 18px gap, no card's out-port or remove button lands under its neighbour, and the plane stays fitted so drawer drags still commit world coordinates. parallelFrameStyle now adds the 20px label band and a 10px pad below the cards, so the frame encloses the cards it names and stays inside the panel.",
"files": [
"plugins/workflow/client.js",
"plugins/workflow/test/client.test.js",
"docs/subsystems/workflow.md",
"changelog.d/1738-workflow-drawer-parallel-pair.fixed.md"
],
"checks": {
"ran": "TZ=UTC ARCH_DOCS_BASE=ed5410c2 npm run verify",
"result": "green",
"at": "bddf374b8ee5534b73d2fea90d0a7fde8ab99d06"
},
"next": "none — ready for review"
}