Skip to content

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

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

feat(frontend): choose between following and pinning in the share dialog#7859
yangzhang75 wants to merge 6 commits into
apache:mainfrom
yangzhang75:pin/6-panel

Conversation

@yangzhang75

Copy link
Copy Markdown
Contributor

Part of #7828. Stacked on #7858 — the review here is the last commit, feat(frontend): choose between following and pinning in the share dialog. This is the first PR in the series a user can see.

The share dialog said whether a workflow was public and nothing about which copy the public was getting. With pinning that is a choice, so the dialog carries it.

What the author sees

Under the Public tile, a two-option control:

Follow latest the default, and exactly what publishing does today — the public sees your latest, updated on every save
Pinned the version you have now is frozen; your later edits stay private until you pin again

While a pin is in place and the working copy has moved on, the panel says The public is on an older version, names the pinned one by the date it went public, and offers Update to current — which is the only way those edits reach the public.

Private workflows show none of this. A user without write access cannot reach it: the status endpoint is guarded on write access, because whether edits are being held back is nobody else's business.

Why the panel re-reads on save rather than on open

WorkflowPersistService now emits when a save, a rename or a re-description lands. That is what makes "your edits are not public yet" appear as soon as the autosave does, rather than the next time the dialog is opened — the write is what changes the saved copy, not the keystroke.

Publishing returns the state it produced, so the panel does not have to ask again.

Forcing a save when the dialog opens was tried and reverted: the canvas is not always the workflow — it is empty while one loads, and stays empty if the collaborative model never arrives — so that save could write the emptiness over every operator the workflow had.

Tests

+24 frontend cases: the three states and their wording, the control switching between them, pinning and unpinning through the service, the panel re-reading when a save lands, the panel staying away on a private workflow and for a user without write access, and the dialog's existing access list untouched.

yangzhang75 and others added 6 commits August 22, 2026 14:36
A public workflow follows the author's latest content today: every save reaches
the Hub immediately. Pinning a version as the public copy needs somewhere to keep
that copy, which is what these columns are.

`is_public` stays the on/off switch. `published_content` is the pin: NULL means
the workflow follows the author's latest, which is what every workflow does today,
so the migration changes nothing anyone can see. `published_name` and
`published_description` travel with it because a pin has to hold everything on
show, and `workflow_version` stores no metadata at all -- only content deltas.
`published_version_id` names the version row holding that copy, so the revision
panel can mark it and the author can restore it.

The copy is materialized rather than replayed from `workflow_version` because
those rows are reverse JSON-Patch deltas: serving a pinned workflow would mean
folding every newer patch back from the author's current content on each public
read, and a computed value is something the fulltext index cannot cover.

A CHECK constraint makes "private but pinned" unrepresentable, and a PGroonga
index mirrors the latest-content one so public search can match the frozen copy.

Adding columns changes the arity of the generated positional constructor, so the
three copy-producing paths (clone, duplicate, restore-a-version) now build their
POJO with setters -- which is also what stops a later column from silently
shifting a null into the wrong field.

Part of apache#7828.
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>
@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, @Mrudhulraj
    You can notify them by mentioning @aglinxinyuan, @tanishqgandhi1908, @Mrudhulraj in a comment.

@codecov-commenter

codecov-commenter commented Aug 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.33962% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.99%. Comparing base (b7c33b0) to head (9d5f3a1).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
...shboard/user/workflow/WorkflowPublishService.scala 84.09% 4 Missing and 10 partials ⚠️
...ra/web/resource/dashboard/SearchQueryBuilder.scala 50.00% 1 Missing ⚠️
...esource/dashboard/WorkflowSearchQueryBuilder.scala 97.87% 1 Missing ⚠️
...rce/dashboard/user/workflow/WorkflowResource.scala 98.43% 0 Missing and 1 partial ⚠️
...ponent/user/share-access/share-access.component.ts 97.87% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #7859      +/-   ##
============================================
+ Coverage     91.87%   91.99%   +0.12%     
- Complexity     4510     4518       +8     
============================================
  Files          1173     1174       +1     
  Lines         47350    47599     +249     
  Branches       5306     5352      +46     
============================================
+ Hits          43502    43789     +287     
+ Misses         2204     2160      -44     
- Partials       1644     1650       +6     
Flag Coverage Δ *Carryforward flag
access-control-service 81.00% <ø> (ø)
agent-service 98.62% <ø> (ø) Carriedforward from 8f56d11
amber 88.49% <92.73%> (+0.11%) ⬆️
computing-unit-managing-service 73.67% <ø> (ø)
config-service 86.73% <ø> (ø)
file-service 75.74% <ø> (ø)
frontend 94.00% <98.80%> (+0.20%) ⬆️
notebook-migration-service 79.13% <ø> (ø)
pyamber 97.57% <ø> (ø) Carriedforward from 8f56d11
workflow-compiling-service 77.19% <ø> (ø)

*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

Copy link
Copy Markdown
Contributor

⚠️ Benchmark changes need a look

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

CI benchmark results are noisy; treat <±5% as noise unless repeated.

Dashboard · Run

config throughput MB/s latency max Δ latest / 7d
🔴 bs=10 sw=10 sl=64 453 0.277 21,738/28,653/28,653 us 🔴 +141.8% / 🔴 +99.2%
🔴 bs=100 sw=10 sl=64 1,098 0.67 90,769/117,375/117,375 us 🔴 +56.2% / 🔴 +17.9%
🔴 bs=1000 sw=10 sl=64 1,302 0.795 764,921/886,855/886,855 us 🔴 +21.9% / 🟢 +16.4%
Baseline details

Latest main b7c33b0 from 2026-08-22T12:41:18.215Z

config metric PR latest main 7d avg Δ latest Δ 7d
bs=10 sw=10 sl=64 throughput 453 tuples/sec 1,070 tuples/sec 833.79 tuples/sec -57.6% -45.7%
bs=10 sw=10 sl=64 MB/s 0.277 MB/s 0.653 MB/s 0.509 MB/s -57.6% -45.6%
bs=10 sw=10 sl=64 p50 21,738 us 8,990 us 11,864 us +141.8% +83.2%
bs=10 sw=10 sl=64 p95 28,653 us 12,633 us 14,381 us +126.8% +99.2%
bs=10 sw=10 sl=64 p99 28,653 us 13,861 us 18,035 us +106.7% +58.9%
bs=100 sw=10 sl=64 throughput 1,098 tuples/sec 1,420 tuples/sec 1,083 tuples/sec -22.7% +1.3%
bs=100 sw=10 sl=64 MB/s 0.67 MB/s 0.867 MB/s 0.661 MB/s -22.7% +1.3%
bs=100 sw=10 sl=64 p50 90,769 us 69,686 us 93,077 us +30.3% -2.5%
bs=100 sw=10 sl=64 p95 117,375 us 75,137 us 99,553 us +56.2% +17.9%
bs=100 sw=10 sl=64 p99 117,375 us 89,601 us 108,604 us +31.0% +8.1%
bs=1000 sw=10 sl=64 throughput 1,302 tuples/sec 1,455 tuples/sec 1,119 tuples/sec -10.5% +16.4%
bs=1000 sw=10 sl=64 MB/s 0.795 MB/s 0.888 MB/s 0.683 MB/s -10.5% +16.4%
bs=1000 sw=10 sl=64 p50 764,921 us 685,976 us 909,247 us +11.5% -15.9%
bs=1000 sw=10 sl=64 p95 886,855 us 727,444 us 952,561 us +21.9% -6.9%
bs=1000 sw=10 sl=64 p99 886,855 us 767,738 us 985,186 us +15.5% -10.0%
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,441.24,200,128000,453,0.277,21738.34,28653.22,28653.22
1,100,10,64,20,1822.16,2000,1280000,1098,0.670,90768.75,117374.83,117374.83
2,1000,10,64,20,15362.30,20000,12800000,1302,0.795,764920.73,886855.12,886855.12

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

Labels

ddl-change Changes to the TexeraDB DDL engine frontend Changes related to the frontend GUI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants