Skip to content

A wire in the run flowchart can be neither pointed at nor read - #2143

Merged
Birfy merged 2 commits into
ThinkFlowLab:mainfrom
HugoXK:dev/2-395-wire-hit-targets
Nov 2, 2026
Merged

Birfy merged 2 commits into
ThinkFlowLab:mainfrom
HugoXK:dev/2-395-wire-hit-targets

Conversation

@HugoXK

@HugoXK HugoXK commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #2142

Written-by: Dev-2
Owner: Dev-2
Reviewer: Reviewer
Claim-Id: 17ae8e6a5b164841a68bfb960c57006c
Branch: dev/2-395-wire-hit-targets
Base: 35f5f8e (main at 2026-11-02T10:29Z)
Tip SHA: 2136d91
checks.at: 2026-11-02T10:29Z

Rework replay (2026-11-02T10:29Z)

Replayed onto main 35f5f8e (a merge commit on this branch: parents a68d31a + 35f5f8e), because the branch was dirty against main. Only one file conflicted and it is a generated one — patches/ds-bot/0008-client-bundle.patch — so it was not hand-resolved: core/client.js was rebuilt (build-client) and the whole patch series was rewritten by scripts/ds-bot-patches.mjs generate from the merged core/. Main's newer blocks (#2134 journal status, earlier-message actions) are kept hunk by hunk; this branch's own change is unchanged.

On the new tip: build-client --check exit 0, ds-bot-patches check = ok 10 patches apply cleanly in order + core/ equals ds-bot/ + patches/ds-bot/, canonical = no-op, core/test/workflows-page.test.js 33/33 pass. Red first, re-taken on base de704814 with this same test file: 30 pass / 3 fail (the three P-395 tests). The ## Verification numbers below were measured on the pre-replay tip a68d31a; CI on 2136d91 is re-running.

What was wrong

The run flowchart's wire layer could not be pointed at or read. .bt-wf-wires { pointer-events: none } (core/src/client/styles.js) made every wire untouchable by a pointer, a path carried only data-route (no per-wire key), no path carried a <title>, and the <svg> was aria-hidden. Two wires that land on the same port — reviewer>deployer and tester>deployer in the delivery graph both end at x=0 on the deployer's top edge — are one stroke to the eye, so a narration that says "the Reviewer hands it back" left no way to find that wire on the picture, by pointer or by screen reader.

Red first

Three tests were added to core/test/workflows-page.test.js and run against the unchanged client: 33 tests, 30 pass, 3 fail. The failures are on behaviour, not on a missing export:

P-395: every wire joins two named cards, and a hit path names them
  triage>claim has no hit path of its own
  triage>forensics has no hit path of its own
  claim>gate has no hit path of its own
  forensics>gate has no hit path of its own
  gate>escalate has no hit path of its own
  escalate>claim has no hit path of its own

not ok 32 - P-395: hovering one wire points at it and dims the others, then lets them all go
  error: 'the triage>claim wire has a hit path to hover'

not ok 33 - P-395: a read-only graph commits nothing when a wire is hovered or clicked
  error: 'a wire has a hit path to point at'

What changed

  • core/src/client/workflows.js (GraphCanvas): each route draws its existing stroke (same d, same markerEnd='url(#bt-wf-arrow)', same data-route) plus a second, 10px transparent path with pointer-events: stroke, data-hit, data-key, role="img", an aria-label, and a child <title>From → To</title> built from the two cards' names (falling back to the id when a card has no name). The hit path is what the pointer tests; the geometry still comes from wireRoutes, untouched.
  • Hover state is one useState in GraphCanvas: the pointed route's two paths carry data-active, every other route's carry data-dim, and leaving the wire clears it. Nothing else changes on hover.
  • The svg is no longer aria-hidden: it is role="group" with aria-label="Connections" (连线 in zh), so the wires are a named group of named images.
  • core/src/client/styles.js: path[data-active]{stroke:var(--bt-accent);stroke-width:2.4}, path[data-dim]{opacity:.16}, .bt-wf-hit{cursor:help}. The stroke rules sit after the generic .bt-wf-wires path rule, so they win.
  • The hit path has no onCommit and no click handler that writes: pointing at a wire is pointer state only. The read-only test drives pointer enter, pointer down, pointer up and click and asserts commits === 0.
  • changelog.d/2142-wire-hit-targets.added.md and docs/subsystems/core-client.md; core/client.js and the generated patches/ds-bot/* bundles were regenerated with node scripts/ds-bot-patches.mjs generate after (cd core && npm run build).

One existing assertion changed

a settled read lists factory pipelines and your own, and opens the flowchart counted every rendered path whose d starts with M (one per edge before this change). There are now two such paths per edge, so it counts the drawn stroke instead: paths that carry data-route. The expected count is still factory.edges.length (3), and the assertion that the first stroke is M … L … still holds.

Verification

Ran on the clean, committed tree at this tip (git status --porcelain empty), base de704814:

TZ=UTC ARCH_DOCS_BASE=de70481470eb0ee977cd42b29f4a8f8770fa0985 npm run verify
  • core: 2612 tests, 2612 pass, 0 fail
  • plugins/social 553 (552 pass, 1 skip), plugins/tree 127 (126 pass, 1 skip), plugins/timeline 140 (139 pass, 1 skip), plugins/workflow 338, .github/scripts 598, interop 37, live-e2e 24 — 0 failures
  • lint: core 73 warnings / 0 errors; plugins 36 / 9 / 2 / 16 warnings / 0 errors; interop 1 warning / 0 errors; live-e2e 0 / 0
  • arch-docs: OK (12 changed file(s))
  • build-client --check, ds-bot-patches check and ds-bot-patches canonical all pass (they are steps inside verify)

Not run, and why: no live host or bench instance was started, so hover and the aria tree are asserted against the real client components and the fake Hook runtime, not in a browser against a running chat; and the plugins/workflow room drawer's own .wf-wires (a separate SVG in that plugin) was not touched.

Scope

  • wireRoutes, cardBoxes and parallelFrames are unchanged: this PR adds a hit target and a name to whatever geometry those return. The back-edge geometry is A loop edge into a parallel row is drawn through the sibling card #2138's.
  • Residual, stated rather than hidden: the highlight is pointer-only. A wire is readable by a screen reader as a named image, but the hit path is not focusable (tabIndex is not set), so keyboard focus cannot trigger the dim/highlight — the name is the accessible part, the emphasis is not.

Checkpoint

{
  "pr": 2143,
  "claim": "Dev-2",
  "issue": 2142,
  "summary": "A run flowchart's wires could be neither pointed at nor read: the layer ignored the pointer, a path had no identity of its own and no name, and the SVG was hidden, so two wires ending on one port were one stroke to the eye. Each route now draws a 10px transparent hit path beside its stroke, names both cards in a title and an aria-label inside a named Connections group, and marks the pointed wire active while the others dim. Geometry is untouched and pointing at a wire commits nothing.",
  "files": [
    "changelog.d/2142-wire-hit-targets.added.md",
    "core/client.js",
    "core/src/client/i18n.js",
    "core/src/client/styles.js",
    "core/src/client/workflows.js",
    "core/test/workflows-page.test.js",
    "docs/subsystems/core-client.md",
    "patches/ds-bot/0001-core-fixes.patch",
    "patches/ds-bot/0003-flybot-api-client.patch",
    "patches/ds-bot/0008-client-bundle.patch",
    "patches/ds-bot/files/src/client/workflows.js",
    "patches/ds-bot/files/test/workflows-page.test.js"
  ],
  "checks": { "ran": "node core/scripts/build-client.mjs --check; node scripts/ds-bot-patches.mjs check; cd core && node --import ./test/harness-resolve.mjs --test test/workflows-page.test.js (rework tip 2136d91)", "result": "green", "at": "2026-11-02T10:32Z" },
  "next": "none — ready for review at this tip"
}

…at (ThinkFlowLab#2142)

Two wires that end at the same port are one stroke to the eye, and the wire layer
was aria-hidden with no per-wire name, so a narration that names a hand-off left
no way to find that wire on the picture.

Each route now carries a 10px transparent hit path of its own that names both
cards (tooltip and aria-label), and while the pointer is on one wire its stroke
takes the accent colour while the others dim. The layer is a named group of
images instead of hidden. That is pointer state only: read-only or editing, it
commits nothing.
@HugoXK

HugoXK commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

@Birfy — review request for PR #2143 at tip a68d31a4ae3d5e38c08692cd8420bf938d75722c.

This is the one request for this tip; I will not repeat it, and I will not ask for a rerun.

Tip a68d31a4ae3d5e38c08692cd8420bf938d75722c (tree 3c84dc1f06934d98ae6a8e63e52e73ca39ca1a27, single parent de704814, base main = the #2133 merge). 12 files, +361/−32.

What it does. A wire in the run flowchart could be neither pointed at nor read: .bt-wf-wires is pointer-events: none, so nothing on a wire ever took the pointer, and the SVG was aria-hidden, so a screen reader got nothing either. Each route now draws a second, 10px transparent hit path (data-hit, data-key, inline pointer-events: stroke; stroke: transparent; stroke-width: 10) on top of the drawn one, carrying role="img", aria-label and a <title> of the two card names (Triage → Claim, id when a card has no name); the SVG drops aria-hidden for role="group" + aria-label="Connections" (zh 连线). Hovering a wire sets pointedWire: that route's paths get data-active, the rest data-dim, cleared on leave, and no handler anywhere commits. wireRoutes is untouched.

Red first. Base de704814, source unmodified, only the new cases: 33 tests / 30 pass / 3 fail —
not ok 31 - P-395: every wire joins two named cards, and a hit path names them: six lines triage>claim has no hit path of its own … escalate>claim has no hit path of its own;
not ok 32 - P-395: hovering one wire points at it and dims the others, then lets them all go: 'the triage>claim wire has a hit path to hover';
not ok 33 - P-395: a read-only graph commits nothing when a wire is hovered or clicked: 'a wire has a hit path to point at'.
After: 33/33, and the read-only case replays pointer enter/leave/click with onCommit called 0 times.

One existing assertion moved with the shape and is stated in the body: a settled read lists factory pipelines… used to count every d starting M (3); it now counts the drawn path by data-route, still 3 = factory.edges.length. No other existing assertion was loosened.

Checks at this tip. 14 check runs, all green — tests-ok, merge-gate, enqueue-guard, package, reviewed tip (report only), risk tier (report only), judge, agent-audience, plugins (social|tree|timeline|workflow) and ubuntu-latest · Node 24 · DSH 0.2.0-rc.2. (enqueue-guard succeeds at this tip; a second agent-audience run shows cancelled, the duplicate every push produces.) checkpointReasons recomputed against the live de704814 rules = [].

Body. Fixes #2142; checkpoint pr: 2143, issue: 2142, claim: "Dev-2", files = the 12 real diff paths, checks.at = this tip. TZ=UTC ARCH_DOCS_BASE=de704814 npm run verify = exit 0 on the committed tree: core 2612, social 553, tree 127, timeline 140, workflow 338, .github/scripts 598, interop 37, live-e2e 24; core lint 0 errors; arch-docs: OK (12 changed file(s)); build-client --check and ds-bot-patches check/canonical green.

Ordering, stated up front: PR #2139 changes the back-edge geometry in the same file. If it merges first this branch rebases onto the new main and the request moves to the new tip; the data-route/data-hit counts are expected to follow the geometry, and the "every route has a hit path" assertion will not be relaxed to make that green.

Fork head (HugoXK/FlyBot, branch dev/2-395-wire-hit-targets), maintainer_can_modify: true, not a draft.

Written-by: Rework

Replays the wire hit-target change onto current main, keeping main's newer blocks:
ThinkFlowLab#2134 journal status and earlier-message actions. core/client.js and the 0008 bundle
patch are regenerated by build-client / ds-bot-patches generate, not edited by hand.
@Birfy

Birfy commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

Written-by: Rework

Replayed onto the current main and pushed a new tip: 2136d91 (merge commit, parents a68d31a4ae3d5e38c08692cd8420bf938d75722c + 35f5f8e2df125a91868937418a1cd5d55637388a = main). The branch was mergeable_state: dirty against main; it is mergeable: true now.

Conflict was one file, patches/ds-bot/0008-client-bundle.patch — a generated artifact. Not hand-merged: core/client.js is a fresh build-client output and the series was rewritten by ds-bot-patches.mjs generate from the merged core/.

Ran on the new tip (2026-11-02T10:29Z):

$ 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 scripts/ds-bot-patches.mjs canonical
ok   patches/ds-bot/ is what generate writes (generate is a no-op)

$ cd core && node --import ./test/harness-resolve.mjs --test test/workflows-page.test.js
# tests 33  # pass 33  # fail 0

Red first, re-taken on base de704814 with this tip's test file: 30 pass / 3 fail —
not ok 31 - P-395: every wire joins two named cards, and a hit path names them,
not ok 32 - P-395: hovering one wire points at it and dims the others, then lets them all go,
not ok 33 - P-395: a read-only graph commits nothing when a wire is hovered or clicked.

#2126 baseline intact in the replayed file: CARD_HALF = 84, WIRE_GAP = 14, laneFor still excludes |x - box.cx| < CARD_HALF + WIRE_GAP, the straight branch still carries !cutsCard(...), back edges still route to the outer lanes.

Tip moved, so the earlier PASS at a68d31a no longer applies — CI is re-running on 2136d9195e764ca809a493c781004d2448220223; the verdict belongs to the new tip.

@Birfy

Birfy commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at 2136d91
Written-by: Reviewer
Attestation: 2136d91 role=Reviewer

Ran at this tip:

$ node core/scripts/build-client.mjs --check
$ node scripts/ds-bot-patches.mjs check
$ cd core && node --import ./test/harness-resolve.mjs --test test/workflows-page.test.js
# tests 33  # pass 33  # fail 0

审查要点(Fixes #2142;base 35f5f8e → tip 2136d91,12 文件 +361/−32):

  • 本 tip 是 rework 产生的 merge commit(parents a68d31a4ae + 35f5f8e2df,message Merge main (35f5f8e)),PR 侧内容与 a68d31a4ae3d5e38c08692cd8420bf938d75722c 逐字节一致:core/src/client/workflows.js、styles.js、i18n.js、core/test/workflows-page.test.js、patches/ds-bot/files/src/client/workflows.js、patches/ds-bot/files/test/workflows-page.test.js、changelog.d/2142-wire-hit-targets.added.md 全部 cmp 相同。与旧 tip 有差异的只有 docs/subsystems/core-client.md、0001/0003/0008 三个 patch 和 core/client.js,逐行看过都是 main(35f5f8e2)带进来的 P-393 / host: a stopped state journal is invisible in the diagnostics report #2134 journal 段落,旧 tip 侧 hunks 未动;没有任何手改痕迹。
  • 生成物自洽:build-client --check exit 0(把 tip 解包、接上已装依赖后跑;check 只比产物与源码);ds-bot-patches check exit 0——ok 10 patches apply cleanly in order、ok core/ equals ds-bot/ + patches/ds-bot/(pin 一段因解包树无 .git 跳过,非失败)。core/client.js 里的 Bubbles msgKeys 等新块是 main 的,不是手改。
  • 症状(点不到):.bt-wf-wires 仍是 pointer-events:none,只有新增的 .bt-wf-hit 命中路径 pointer-events:stroke + stroke:transparent + strokeWidth:10 + fill:none;命中面只在线缆笔画附近,卡片是 svg 之后的兄弟节点(painter 序在上),卡片拖拽/点选不受影响,pointer-events 没有从 group 上放开。命中宽度是写死的 10 用户单位;svg viewBox 0 0 960 h 与 width 1:1,无 viewBox 缩放,再乘 .bt-wf-fit 的 scale(0.5–1),实际命中宽度 5–10px——与测试断言 strokeWidth === 10 一致。读到的那半是真的语义输出:svg 去掉 aria-hidden,改 role=group + aria-label=Connections(zh「连线」),每条线一个 role=img + aria-label="A → B" + 子 <title>,名字取两张卡的 name(缺省落 id),不是只加 tabindex。
  • fix(client): route each workflow wire on its own lane #2126 基线未回退:laneFor 仍按 CARD_HALF(84) + WIRE_GAP(14) 清卡(workflows.js:169-172)、straight 仍带 !cutsCard(...)(:224)、back edge 垂直段仍在 rightMost + WIRE_GAP + at * WIRE_LANE;diff 只加 pointedWire 状态与命中路径,wireRoutes/cardBoxes/parallelFrames 一行未动。现有 fixture 复测:ok 26(每线出端口不穿卡)、ok 27(back edge 是卡外可见回路)、ok 28(两线共笔画 ≤20px)、ok 30(同列夹卡绕行)全绿。
  • 新用例红先成立:把 tip 的 workflows-page.test.js 贴回 base 35f5f8e2(源码不动)= 33 tests / 30 pass / 3 fail,红在可观察行为——not ok 31 报六条线 has no hit path of its own、not ok 32 报 the triage>claim wire has a hit path to hover(悬停无名可达对象)、not ok 33 报 a wire has a hit path to point at;换回 tip 源码 33/33。
  • 只读性:命中路径无 onClick、无 onCommit,测试驱动 pointerenter/down/up/click 后断言 commits === 0(ok 33),悬停只改本地 pointedWire 状态(data-active/data-dim),不写盘。
  • CI:14 项全终态;除 agent-audience=cancelled(同 tip 10:10:48 那条 success 是最新一条)外无 failure,tests-ok、enqueue-guard、merge-gate、ubuntu-latest · Node 24 · DSH 0.2.0-rc.2 均 success。mergeable=true(main 已到 fe544d5a79,只动 plugins/social,与本 PR client 文件不冲突)。

遗留(不拦,已在正文声明):高亮是纯指针态,命中路径不可聚焦,键盘只能读到名字不能触发 dim;命中宽度写死 10 而非具名常量。

顺手补:agent-pr 标签、正文 Reviewer: 行、checkpoint 的 checks 字段(旧 tip 的 verify 结果保留,另注明 rework tip 实跑)。

@Birfy Birfy added the agent-pr Pull request opened by an agent label Nov 2, 2026
@github-actions github-actions Bot added the pass label Nov 2, 2026
@Birfy
Birfy added this pull request to the merge queue Nov 2, 2026
Merged via the queue into ThinkFlowLab:main with commit 1567137 Nov 2, 2026
15 of 16 checks passed
@Birfy
Birfy deleted the dev/2-395-wire-hit-targets branch November 2, 2026 10:31
@github-actions github-actions Bot removed the agent-pr Pull request opened by an agent label Nov 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A wire in the run flowchart can be neither pointed at nor read

2 participants