Skip to content

feat(frontend): choose between following and pinning in the share dialog - #8578

Draft
yangzhang75 wants to merge 5 commits into
apache:mainfrom
yangzhang75:pin/6-panel
Draft

yangzhang75 wants to merge 5 commits into
apache:mainfrom
yangzhang75:pin/6-panel

Conversation

@yangzhang75

@yangzhang75 yangzhang75 commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

What changes were proposed in this PR?

Everything so far has been machinery with nothing to drive it. This is the control: in the share
dialog, a published workflow gets a two-way switch for what the public sees.

  • Two states, so the control is a switch. Follow latest is what Texera does today — the
    public sees the author's latest, updated on every save. Pinned holds the version they froze.
    Both choices stay on screen, so the author reads what the options are instead of inferring one
    from the label of the other.
  • Everything under the switch describes the side it is on — a fact, not a warning: keeping an
    older version public is a legitimate choice. Green while the public has everything, amber once a
    pin is holding a version back.
  • Only the pinned side has more to say. Behind, that is the one decision the state leaves
    open, so it gets a card: which version is out there, what it costs, and the act that ends it.
  • The panel describes the saved copy, and the editor saves on a debounce, so it re-reads when
    a save lands. Deliberately it does not force a save first: the canvas is not always the workflow
    — it is empty while one loads, and stays empty if the collaborative model never arrives — so a
    save nobody asked for could write that emptiness over every operator the workflow had.

Behind a flag, off. gui.workflow-workspace.version-pinning-enabled defaults to false, so
merging this changes nothing anyone can see: the dialog is exactly what it is today and does not
even ask the server about a state it cannot show. The last PR of the series turns it on, so the
feature appears only once every surface that has to agree with a pin is in place.

Any related issues, documentation, discussions?

Closes #7942
Part of #7828. Design discussion: #7128. Stacked on #7853, #8575, #8576 and #8577; until those
merge this PR shows their commits too, and the review here is the last one.

How was this PR tested?

Frontend: the share-access spec covers the panel end to end — each of the three states and the
sentence it shows, the switch calling pin and unpin and doing nothing when the side already in
force is picked, "Update to current" asking for a re-pin, the date the card names, failures
surfacing as a notification, the panel staying hidden for a workflow that is not published and
for a user who cannot publish, and the re-read when a save lands. One case covers the flag being
off: nothing renders and the status endpoint is not called. The persist spec covers the new
pin/unpin/status calls and that a save is announced when it lands rather than when it is sent.
Full suite passes (6602), production (AOT) build passes, prettier clean.

Backend: GuiConfigSpec pins the flag's default to off (72 tests); the dashboard package passes
(572); scalafmt clean.

Was this PR authored or co-authored using generative AI tooling?

Generated-by: Claude Code (Opus 5)

🤖 Generated with Claude Code

@github-actions github-actions Bot added engine frontend Changes related to the frontend GUI common platform Non-amber Scala service paths labels Sep 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Automated Reviewer Suggestions

Based on the git blame history of the changed files, we recommend the following reviewers:

  • Contributors with relevant context: @aglinxinyuan, @tanishqgandhi1908, @mengw15
    You can notify them by mentioning @aglinxinyuan, @tanishqgandhi1908, @mengw15 in a comment.

@codecov-commenter

codecov-commenter commented Sep 17, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.72141% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.61%. Comparing base (be65bf8) to head (208bdd8).

Files with missing lines Patch % Lines
...shboard/user/workflow/WorkflowPublishService.scala 87.71% 4 Missing and 10 partials ⚠️
...ra/web/resource/dashboard/SearchQueryBuilder.scala 50.00% 1 Missing ⚠️
...esource/dashboard/WorkflowSearchQueryBuilder.scala 97.95% 1 Missing ⚠️
...rce/dashboard/user/workflow/WorkflowResource.scala 98.36% 0 Missing and 1 partial ⚠️
...ponent/user/share-access/share-access.component.ts 97.72% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##               main    #8578    +/-   ##
==========================================
  Coverage     92.60%   92.61%            
- Complexity     5046     5052     +6     
==========================================
  Files          1252     1253     +1     
  Lines         53586    53859   +273     
  Branches       6673     6726    +53     
==========================================
+ Hits          49625    49880   +255     
- Misses         2317     2322     +5     
- Partials       1644     1657    +13     
Flag Coverage Δ *Carryforward flag
access-control-service 77.38% <ø> (ø)
agent-service 99.16% <ø> (ø) Carriedforward from be65bf8
amber 88.15% <93.51%> (+0.04%) ⬆️
computing-unit-managing-service 60.48% <ø> (ø)
config-service 87.50% <100.00%> (+0.12%) ⬆️
file-service 81.53% <ø> (ø)
frontend 96.59% <98.71%> (+<0.01%) ⬆️
notebook-migration-service 83.73% <ø> (ø)
pyamber 98.52% <ø> (ø) Carriedforward from be65bf8
workflow-compiling-service 74.09% <ø> (ø)

*This pull request uses carry forward flags. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ Benchmark changes need a look

🟢 2 better · 🔴 6 worse · ⚪ 7 noise (<±5%) · 0 without baseline

Compared against main be65bf8 benchmarked on this same runner, so the delta is largely free of cross-runner hardware noise. The "7d avg" column still reflects the gh-pages dashboard. Treat <±5% as noise unless repeated.

Dashboard · Run

config throughput MB/s latency max Δ latest / 7d
🔴 bs=10 sw=10 sl=64 379 0.232 25,130/33,805/33,805 us 🟢 -31.3% / 🔴 +149.7%
🔴 bs=100 sw=10 sl=64 781 0.476 125,072/167,551/167,551 us 🔴 +19.1% / 🔴 +79.6%
⚪ bs=1000 sw=10 sl=64 924 0.564 1,083,758/1,167,512/1,167,512 us ⚪ within ±5% / 🔴 +29.5%
Baseline details

Latest main be65bf8 from same runner

config metric PR latest main 7d avg Δ latest Δ 7d
bs=10 sw=10 sl=64 throughput 379 tuples/sec 390 tuples/sec 983 tuples/sec -2.8% -61.4%
bs=10 sw=10 sl=64 MB/s 0.232 MB/s 0.238 MB/s 0.6 MB/s -2.5% -61.3%
bs=10 sw=10 sl=64 p50 25,130 us 23,627 us 10,903 us +6.4% +130.5%
bs=10 sw=10 sl=64 p95 33,805 us 49,184 us 13,539 us -31.3% +149.7%
bs=10 sw=10 sl=64 p99 33,805 us 49,184 us 16,174 us -31.3% +109.0%
bs=100 sw=10 sl=64 throughput 781 tuples/sec 828 tuples/sec 1,259 tuples/sec -5.7% -38.0%
bs=100 sw=10 sl=64 MB/s 0.476 MB/s 0.506 MB/s 0.769 MB/s -5.9% -38.1%
bs=100 sw=10 sl=64 p50 125,072 us 118,670 us 86,981 us +5.4% +43.8%
bs=100 sw=10 sl=64 p95 167,551 us 140,631 us 93,289 us +19.1% +79.6%
bs=100 sw=10 sl=64 p99 167,551 us 140,631 us 105,695 us +19.1% +58.5%
bs=1000 sw=10 sl=64 throughput 924 tuples/sec 920 tuples/sec 1,300 tuples/sec +0.4% -28.9%
bs=1000 sw=10 sl=64 MB/s 0.564 MB/s 0.562 MB/s 0.793 MB/s +0.4% -28.9%
bs=1000 sw=10 sl=64 p50 1,083,758 us 1,084,052 us 854,105 us -0.0% +26.9%
bs=1000 sw=10 sl=64 p95 1,167,512 us 1,140,692 us 901,686 us +2.4% +29.5%
bs=1000 sw=10 sl=64 p99 1,167,512 us 1,140,692 us 924,532 us +2.4% +26.3%
Raw CSV
config_idx,batch_size,schema_width,string_len,num_batches,total_ms,total_tuples,total_bytes,tuples_per_sec,mb_per_sec,lat_p50_us,lat_p95_us,lat_p99_us
0,10,10,64,20,527.10,200,128000,379,0.232,25129.94,33804.79,33804.79
1,100,10,64,20,2562.39,2000,1280000,781,0.476,125072.43,167550.95,167550.95
2,1000,10,64,20,21645.65,20000,12800000,924,0.564,1083758.13,1167512.08,1167512.08

@yangzhang75
yangzhang75 force-pushed the pin/6-panel branch 4 times, most recently from 831ab86 to d741134 Compare September 28, 2026 21:13
@yangzhang75
yangzhang75 marked this pull request as ready for review October 6, 2026 18:42
@yangzhang75
yangzhang75 marked this pull request as draft October 7, 2026 01:36
yangzhang75 and others added 5 commits October 8, 2026 10:55
A public workflow follows the author's latest content, as publishing has
always done. This adds the other state: the author pins the version they
have now, and the public copy stops moving until they pin again.

`is_public` stays the on/off switch; `published_content` is the pin, NULL
while following. `WorkflowPublishService` owns the two states, and three
endpoints expose them: POST and DELETE `/workflow/pin/{wid}` to pin and
unpin, GET `/workflow/publish-status/{wid}` for what the author is shown.
Publishing and unpublishing move through the same service, so unpublishing
drops the pin rather than leaving a private workflow carrying one.

Two paths are narrowed so a pin can hold. A save wrote the whole row back,
so a publish landing while a save was in flight was silently rolled back,
and a request body could set the publish columns itself; saves now write
only name, description and content. Creating a workflow clears the publish
columns for the same reason.

Nothing reads the pinned copy yet: every workflow is in the following
state it is in today, and nothing on screen changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
With a version pinned, a workflow has two copies: the author's working
copy and the frozen one on public show. This routes every read that
serves a viewer without granted access through the frozen copy, and
freezes the name and description with the graph.

`WorkflowPublishService.publicCopyOf` returns the three fields as a
group, so a surface cannot pick up the published graph under a title the
author has not published; `WorkflowAccessResource.hasGrantedAccess` is
the seam that decides which copy a caller gets. Granted access -- owner,
shared, project member -- keeps tracking the author's latest, because
sharing is not publishing.

Name and description freeze because they are as public as the graph: if
only the graph froze, a report about a title could be answered by editing
the title while the pinned copy still advertised it.

Routed through it: opening a workflow, the hub's read, Clone, Duplicate,
`/workflow_name`, `/workflow_description` and the size a listing shows.
A workflow that follows the author's latest -- every workflow today --
is served exactly what it is served now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Search and the listings it feeds were reading the author's live columns,
which for a pinned workflow is the one copy the public cannot open. A
draft would turn up in a public search under a title nobody has seen, and
the card would advertise a name the detail page does not show.

Each filter is now applied to whichever copy the caller may see:
`onVisibleCopy` builds the same filter twice -- over the live columns for
rows the caller was granted access to, over the frozen ones for rows they
reach only because the workflow is public -- and ORs the two. A
disjunction over bare columns rather than a CASE, so each side stays
eligible for its own fulltext index. Unpinned public rows fall back to
the live columns, so a following workflow searches exactly as it does now.

Listings carry two more things from the same query: the frozen name and
description to show a viewer without granted access, and whether the copy
on show is behind the author's working copy. `constructWhereClause` takes
`includePublic` for this; the other builders accept and ignore it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The author can pin a version, and their working copy then moves on. This
gives them a way back to what the public is seeing: pinning leaves an
anchor in the revision history they already use, and the panel marks it
as the one currently public, so restoring the published version is the
restore they already know.

The anchor is a version row whose delta is the identity patch, so
replaying it returns exactly what was pinned however many edits pile up
after. An existing row cannot stand in: a version row replays to the
content as it was *before* the change it records. Pinning content that is
already the pinned one reuses its anchor rather than adding a twin, and
the read that decides takes a row lock so two pins racing cannot both
insert.

Two consequences the anchor forces:

- It must not start the version panel's aggregation window. It lands
  seconds after the save it freezes, and the panel folds close-together
  versions into the newest, which would hide the author's own save behind
  a row they never made.
- The revision history is now readable only with granted access, or while
  nothing is pinned. Replaying a version folds deltas back from the
  author's current content, so listing versions of a pinned workflow would
  hand a public viewer the very edits the pin is holding back.

`publish-status` carries the pinned version's date, read from the version
row so the dialog and the panel print one value rather than two clocks'.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The share dialog said whether a workflow was public and nothing about
which copy the public was getting. With pinning that is now a choice, so
the dialog carries it: a two-option control -- Follow latest, or Pinned --
under the Public tile, plus a line naming the pinned version by the date
it went public.

Follow latest is the default and is exactly what publishing does today.
Pinning freezes the version the author has now; while a pin is in place
and their working copy has moved on, the panel says so and offers Update
to current, which is the only way those edits reach the public.

The panel re-reads its state when a save lands rather than on a timer:
`WorkflowPersistService` now emits on persist, rename and re-describe,
which is what makes "your edits are not public yet" appear as soon as the
autosave lands rather than the next time the dialog is opened. Publishing
returns the state it produced, so the panel does not have to ask again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

common engine frontend Changes related to the frontend GUI platform Non-amber Scala service paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Frontend: choose between following and pinning in the share dialog

2 participants