Skip to content

feat(edges): reverse a connection's direction from the Ends group (v0.28.0) - #354

Merged
MerciHanrim merged 4 commits into
mainfrom
feat/reverse-connection
Oct 14, 2026
Merged

MerciHanrim merged 4 commits into
mainfrom
feat/reverse-connection

Conversation

@MerciHanrim

@MerciHanrim MerciHanrim commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

v0.28.0 — reverse a connection without drawing it again

The second line-editing unit (docs/edge-routing.md §ER19): an existing connection's direction is reversed from the Inspector's Ends group, through one shared check and one shared change. Four commits, each verified and pushed as it was done; the fourth is the fix from the code review. The investigation and Lumi's decisions are in share notes 48 and 49.

1. One check, one change (ba7775b)

src/model/reverse.ts (reverseVerdict, reversedEdge) and the store's reverseEdge.

  • A reversal keeps the id, kind, handles (the kind channel) and every data field: amount, condition, mode, timing, delay, resource type, colour, line shape. The ends swap; each end keeps the face of the node it is on, so the faces swap with the ends (canonical ports); the user's bends keep their points in reverse order. The same text is then read in the new direction.
  • Refused, judged on the RESULT, one reason shown, in this order: the new source needs an output and the new target an input (as drawing, portRefusal); the result must not repeat another connection by the drawing rule (duplicateOf); a state connection must not break its mode's or timing's engine rule in the new direction — an activator needs a Pool source, an "Always" label a Pool at both ends, a "When the source fires" label a router source and a Pool target, and a mode or timing that never has an effect is refused. The engine rules are unchanged. An expression's validity does not depend on direction and is not a reason.
  • Applied and left to the engine's existing warnings (Lumi, decision 1): a reversal that closes a loop of Gates and Converters with no Pool, or that turns an amount into a probabilistic Gate's branch weight.
  • One undo step; the run is reset on the reversal and on its undo and redo. A self-loop keeps its node and the run; with one face and at most one bend nothing changes and nothing is recorded.

2. The Inspector (ba7775b)

  • Reverse beside the folded Ends summary, on the same row, its own button: pressing it never opens or closes the group (decision 2). One press, no confirmation, no Apply (decision 3); focus stays on it.
  • A note below it, visible folded: the hint (what a reversal keeps); after it acted, three separate sentences — the amount and condition are kept and read in the new direction / the user's bends stay where they were (only when there are bends) / the automatic route, the line next to each end and the label's place may change; a refusal's reason, node names isolated; and, while a pick in the open group is not applied, "Apply or revert the change you picked first, then reverse", with Reverse waiting, so it is never unclear whether the pending or the stored ends would be reversed.
  • Waiting or refused, the button is aria-disabled, not disabled: it stays in the Tab order and the note describes it. Under the edit lock the Inspector is disabled as a whole; the phone shows no Ends group (read-only, as before).
  • Two choices made while building, approved in the screen review: the note is never in the warning colour (a refusal says why an action is unavailable, not that the connection is wrong; in red it would sit on every activator and "When the source fires" label); the wait line names no position (the pick is below it).
  • The longer summary ellipsises earlier with Reverse on its row; accepted for this release (opening the group shows both names).
  • 13 interface strings in 18 languages (12 in ba7775b, one in ef392f8); manifests: isolate obligation edge-ends-reverse-reason (er19, 3 rows; the reason-key map is a literal so the checker resolves it), ICU arguments 238.
  • Not changed: the router and label placement. The label overlaps seen in note 48 (a label under a bend marker while selected; a neighbour re-laned after a reversal) are a separate item.

3. Release (9e8bea2, 263a37b)

  • 9e8bea2: 0.28.0, .changes/reverse-connection.json (user-facing), three What's new lines in 18 languages, CHANGELOG v0.28.0, README Latest. No line says the line or the label stays; the drawn-differently line says what may change. es-ES = es-419 and pt-BR = pt-PT, written regionally neutral, so no audited region key and pt-PT's delta are added. Release date 2026-10-14, to be checked on the merge day.
  • 263a37b: the two state-ui Inspector baselines (light, dark) regenerated from the final code — the only baselines changed, approved after review (share note 49).
  • The translation contract tests pass: evidence of the catalogs' structure and terms, not a review of how the lines read. No native-speaker review was done.

4. Review fix (ef392f8)

Lumi's code review (P2): the note checked the NEXT reversal's refusal before the reversal just made, so a reversal that made a connection work (an activator from a Gate turned to start at a Pool, a "When the source fires" label from a Pool turned to start at a Gate) showed only the refusal, as if the press had failed. Now the reversal's own result stays in the note, and a line of its own below it says "It cannot be reversed back." with the reason; the button stays aria-disabled and is described by that line. One new string in 18 languages (inspector.ends.reverse.again; catalog 1134, runtime 1356). docs/edge-routing.md §ER19 and the CHANGELOG say it.

Verification of the new head ef392f8

  • e2e connection-reverse.spec.ts: 8 (the 6 below and 2 new: the activator and the "When the source fires" label made to work — the result, the separate line, the button waiting and described by it, a press recording nothing, one undo step).
  • connection-ends.spec.ts, state-ui.spec.ts, connection-reverse.spec.ts on all projects: 50 passed.
  • Unit: 3 571 passed, 1 skipped; locale and release contracts 713; lint 0 errors; the same checks as below pass.
  • The full local e2e, dist and pwa were run on 263a37b, before this fix, not again on ef392f8.
  • PR CI on ef392f8: 10 / 10, and the Cloudflare Pages preview deployed.

Verification on 263a37b (before the review fix)

  • Full local e2e, --workers=1 --retries=0: 2 189 passed, 0 failed, 10 skipped (46.1 min).
  • dist and pwa configs, run separately after the full run: dist 16 passed, pwa 19 passed.
  • Unit: 3 571 passed, 1 skipped (vitest, full), including graphStore.reverse.test.ts (16). tsc, lint (0 errors), check:change-declaration, check:i18n, check:i18n-surface, check:isolate-arguments, check:icu-arguments, check:arrow-direction, check:content-direction, check:control-bytes, check:functional-glyphs, check:locale-lists, check:mobile-query.
  • e2e connection-reverse.spec.ts: 6 (folded allow and refuse by ports and by the activator rule, the result sentences with and without bends, undo by Ctrl+Z, the run, the waiting state with Revert and Apply, the keyboard, the lock).
  • PR CI on 263a37b: 10 / 10 (checks, six e2e shards, production bundle, PWA, the e2e gate), and the Cloudflare Pages preview deployed.

Earlier runs, kept apart

  • The related e2e on ba7775b's code before two fixes (39 specs that touch the Inspector, all projects): 835 run, 831 passed, 4 failed. The 4: connection-ends.spec.ts "collapsed by default … the keyboard opens it" (Tab from the summary now reaches Reverse before the pick: the test now expects that order); state-ui.spec.ts "activator / label: inline validation" (two elements matched the warning-hint selector: the note was then red; it is now the plain hint colour); and the two state-ui Inspector baselines (the new row; regenerated in 263a37b after review).
  • After the fixes, re-verified: those two tests and connection-reverse.spec.ts pass on all projects (8 passed); state-ui.spec.ts 15 passed against the regenerated baselines; whats-new.spec.ts 96 passed once the dev server was restarted (two version-story tests first read a server left from 0.27.0, the documented stale-server case).

Records and open items

Share notes 48 (investigation, the five user-bend screen pairs) and 49 (the implementation screens and the baseline before / after). Separate later units: user-shaped curves, moving a connection's label. Separate item: the label overlaps of note 48.

…eping its amount, condition and bends

Line editing, unit 2 (docs/edge-routing.md §ER19; share note 48 and Lumi's
decisions of 2026-10-14). One check, `reverseVerdict`, and one change,
`reversedEdge` through the store's `reverseEdge` (src/model/reverse.ts).

A reversal keeps the id, kind, handles and every data field (amount,
condition, mode, timing, delay, resource type, colour, line shape). The ends
swap, the faces swap with them (canonical `ports`), the user's bends keep
their points in reverse order. One undo step; the run is reset on the
reversal and on its undo and redo; a self-loop keeps its node and the run.

Refused, judged on the result, one reason shown, in this order: the new
source needs an output and the new target an input (as drawing); the result
must not repeat another connection by the drawing rule; a state connection
must not break its mode's or timing's engine rule in the new direction (an
activator needs a Pool source; an "Always" label a Pool at both ends; a
"When the source fires" label a router source and a Pool target; a mode or
timing that never has an effect). The engine rules are unchanged. Resource
rules that are not about ports (a router-only loop, a probabilistic Gate's
branch weight) are applied and left to the existing warnings (Lumi).

The Inspector: Reverse beside the folded Ends summary, its own button (it
never opens or closes the group), one press, no confirmation; focus stays on
it. A note below it, visible folded and never in the warning colour: what a
reversal keeps; after it, that the amount and condition are kept and read
in the new direction, that the user's bends stay where they were (only with
bends), and that the automatic route, the line next to each end and the
label's place may change; a refusal's reason, node names isolated; and
"Apply or revert the change you picked first, then reverse" while a pick in
the open group is not applied. Waiting or refused, the button is
aria-disabled, so it stays in the Tab order. 12 strings in 18 languages, no
native-speaker review; manifests: isolate obligation edge-ends-reverse-reason
(er19, 3 rows), ICU arguments 238.

The router and label placement are not changed; the label overlaps seen in
note 48 are a separate item.

Tests: unit graphStore.reverse.test.ts 16; e2e connection-reverse.spec.ts 6
(folded allow and refuse, keyboard, the wait, undo, the run, the lock, bends);
connection-ends.spec.ts's keyboard test now passes Reverse between the
summary and the pick. Full vitest 3571 passed, 1 skipped; locale and release
tests 729; the related e2e (39 specs) 831 of 835 before the fixes, the 4
failures being the Tab order and a hint selector (both fixed) and the two
state-ui Inspector baselines, which show the new row and are NOT regenerated
pending review. No change declaration yet: it comes with the release.
- package.json / package-lock.json 0.28.0; `.changes/reverse-connection.json`
  (user-facing, `release:0.28.0`), date 2026-10-14, to be checked on the
  merge day.
- What's new 0.28.0, three lines in 18 languages: Reverse beside Ends in the
  Inspector, the amount, condition, colour and line shape kept and read in
  the new direction, the user's bends keeping their places; the automatic
  route, the line next to each end and the label's place may be drawn
  differently (no line says the line or the label stays); a reversal that
  cannot be made is not applied and says why. es-ES and es-419, pt-BR and
  pt-PT are written identically and regionally neutral, so no audited
  region key is added and pt-PT's delta stays as it was; French avoids the
  punctuation its spacing rule governs. The locale tests' counts follow
  (catalog 1133, runtime 1355). The e2e release list pins 0.28.0.
- CHANGELOG v0.28.0; README Latest v0.28.0 (v0.27.0 kept as a section,
  v0.25.0 moved into the CHANGELOG list).

The translation contract tests pass: evidence of the catalogs' structure and
terms, not a review of how the lines read; no native-speaker review.
Tests: locale and release 713, whats-new.spec.ts 96 (after restarting the
dev server: two version-story tests had read the old server's 0.27.0),
descriptive-copy-wrapping.spec.ts passed; full vitest 3571 passed, 1
skipped; change-declaration and the i18n, ICU, isolate, control-byte,
glyph, arrow and direction checks pass.
The two state-ui Inspector baselines (light, dark) regenerated from the
final code after Lumi's review (share note 49): the Ends row now carries
the Reverse button and, below it, the hint line, which moves the rest of
the panel down. No other baseline is changed. The rest of state-ui.spec.ts
(15 tests) passes against them.
…d back, with why on a line of its own

Lumi's review of #354 (P2): the Reverse note checked the next reversal's
refusal before the reversal just made, so a reversal that made a connection
work — an activator from a Gate turned to start at a Pool, a "When the
source fires" label from a Pool turned to start at a Gate — showed only
"Reversed, this … would start at …", as if the press had failed.

Now the reversal's own result stays in the note, and below it a line of its
own says "It cannot be reversed back." followed by the reason; the button
stays aria-disabled and describes itself with that line. One new string in
18 languages (`inspector.ends.reverse.again`); the locale counts follow
(catalog 1134, runtime 1356).

Tests: e2e connection-reverse.spec.ts +2 (the activator and the
"When the source fires" label made to work: the result, the separate line,
the button waiting and described by it, a press recording nothing, one undo
step) — 8 passed; connection-ends.spec.ts, state-ui.spec.ts and
connection-reverse.spec.ts 50 passed on all projects; locale and release
713; full vitest 3571 passed, 1 skipped; lint 0 errors; the i18n, ICU,
isolate, control-byte, glyph, arrow, direction, locale-list, mobile-query
and change-declaration checks pass. docs/edge-routing.md §ER19 and the
CHANGELOG say it; 13 interface strings.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Deploying cozy-loop-studio with  Cloudflare Pages  Cloudflare Pages

Latest commit: ef392f8
Status: ✅  Deploy successful!
Preview URL: https://85839daf.cozy-loop-studio.pages.dev
Branch Preview URL: https://feat-reverse-connection.cozy-loop-studio.pages.dev

View logs

@MerciHanrim
MerciHanrim merged commit 71dfbe2 into main Oct 14, 2026
11 checks passed
@MerciHanrim
MerciHanrim deleted the feat/reverse-connection branch October 14, 2026 01:30
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.

1 participant