Skip to content

Keep PublishHelper.InPublishTab in step with the active tab (BL-16174) - #8183

Draft
andrew-polk wants to merge 3 commits into
masterfrom
BL-16174
Draft

Keep PublishHelper.InPublishTab in step with the active tab (BL-16174)#8183
andrew-polk wants to merge 3 commits into
masterfrom
BL-16174

Conversation

@andrew-polk

@andrew-polk andrew-polk commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Switching from the Edit tab to Publish and asking for a BloomPUB preview sometimes threw
InvalidOperationException: Should not be creating bloom book while not in publish tab from
PublishHelper's constructor: the "Bloom had a problem" dialog appeared, the preview stayed
blank, and the Collection and Edit tabs were left greyed out until Bloom was restarted.

Cause

Bloom kept two records of "we are in the Publish tab", updated at different times:

  • WorkspaceTabSelection.ActiveTab — the authoritative one, assigned in WorkspaceView.ChangeTab's PostponedWork.
  • PublishHelper.InPublishTab — the static flag the book-staging code actually checks, maintained separately by PublishView.Activate()/Deactivate() from the SelectedTabChangedEvent subscriber.

The second is always updated later — after ActiveTab has already changed, and after however many
other subscribers have run. The log attached to the card shows the two disagreeing at the moment of
failure: PublishApi.MakeBloompubPreview's own guard (ActiveTab != publish → abort) let the request
through, so ActiveTab said "publish"; PublishHelper's guard fired, so the flag still said "not publish".

The same desync explains the second symptom. PublishView.Activate() is also what calls
SetTabsEnabled(true), the safety net that releases the Edit tab's save lock — a skipped activation
loses that too.

Fix

  • WorkspaceTabSelection.ActiveTab becomes a property whose setter also sets PublishHelper.InPublishTab. One assignment, no window, one owner. This also clears the flag when a new collection's WorkspaceTabSelection initializes to the Collection tab — the static previously survived a collection switch.
  • PublishView no longer writes the flag (comments at both sites point at the new owner).
  • PublishView._isActive is now set before Activate()/Deactivate(), so a throw inside either cannot leave the latch stale and silently skip the next activation. Exceptions still propagate; nothing is swallowed.

Tests

New src/BloomTests/Workspace/WorkspaceTabSelectionTests.cs — 4 tests covering both directions and
the stale-static-across-collections case, each with a sanity check on the starting state.

Ref: https://issues.bloomlibrary.org/youtrack/issue/BL-16174

Devin review


This change is Reviewable

andrew-polk and others added 3 commits August 10, 2026 11:19
…ailed with status code 503

https://issues.bloomlibrary.org/youtrack/issue/BL-16174

Switching from the Edit tab to Publish and asking for a BloomPUB preview
sometimes threw "Should not be creating bloom book while not in publish tab"
(InvalidOperationException from PublishHelper's constructor), showed the
"Bloom had a problem" dialog, left the preview blank, and left the Collection
and Edit tabs greyed out until Bloom was restarted.

Bloom kept two separate records of "we are in the Publish tab":

  * WorkspaceTabSelection.ActiveTab, the authoritative one, assigned in
    WorkspaceView.ChangeTab's PostponedWork; and
  * the static PublishHelper.InPublishTab, which the book-staging code checks,
    maintained separately by PublishView.Activate/Deactivate from its
    SelectedTabChangedEvent subscriber.

The second is updated strictly later than the first: after ActiveTab has already
changed and after however many other subscribers have run. The reported log
proves the two disagreed - PublishApi.MakeBloompubPreview's own guard
(ActiveTab != publish) let the request through, so ActiveTab said "publish",
while PublishHelper's guard fired, so the flag still said "not publish".

Fix: give WorkspaceTabSelection.ActiveTab a setter that updates
PublishHelper.InPublishTab in the same assignment, and stop PublishView from
setting the flag at all. The two can no longer be observed disagreeing, and it
also clears the flag when a new collection's WorkspaceTabSelection initializes
to the Collection tab (the static previously survived that).

Also stop PublishView's _isActive latch going stale: it is now set before
Activate()/Deactivate() rather than after, so a throw inside either cannot leave
us believing we are still in the state we just left. That stale belief silently
skipped the next Activate(), and with it the SetTabsEnabled(true) that releases
the Edit tab's save lock - which is what left the tabs greyed out. Exceptions
still propagate; nothing is swallowed.

Tests: added src/BloomTests/Workspace/WorkspaceTabSelectionTests.cs (4 tests,
covering both directions and the stale-static-across-collections case). Ran the
full C# suite through build/agent-dotnet.sh: 3067 passed, 0 failed, 12 skipped.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Two documentation/robustness points Devin raised on
#8183 (both Informational, no bugs):

- The fixture's summary comment implied that constructing a new WorkspaceTabSelection is
  what clears the stale static after a collection switch. It isn't: what clears it is the
  new WorkspaceView constructor's `_tabSelection.ActiveTab = WorkspaceTab.collection`,
  which the test stands in for. Say so, and say that the invariant depends on that line
  continuing to exist.
- Mark the fixture [NonParallelizable]. PublishHelper.InPublishTab is process-wide;
  Setup/TearDown restoring it is enough while fixtures run one at a time, but stating the
  constraint means turning parallel execution on later cannot quietly let this fixture and
  the publish tests perturb each other. Matches the existing use of the attribute in
  BloomTests/Publish/Rab/RabRealBuildTests.cs.

Comments and a test attribute only; no production code touched.

Tests: full C# suite green at the parent commit (3067 passed, 0 failed, 12 skipped);
WorkspaceTabSelectionTests re-run green (4 passed) after these edits.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Devin flagged (informational) that moving PublishHelper.InPublishTab into the ActiveTab
setter weakened a guarantee that the comment on StageBookForBloomPubPreviewForTest still
asserts. The flag now goes true when the tab becomes active, which is ahead of the
SelectedTabChangedEvent subscribers -- so it no longer implies "PublishView.Activate has
finished the real publish-tab setup", which is what the comment said it meant.

The e2e flow is still correct, but for a different reason than the comment gave: selectTab
runs the whole tab switch synchronously inside its own API call, which the caller awaits.
Say that, so nobody later reasons from a guarantee the guard no longer provides.

Comment only; no behavior change.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
@andrew-polk

Copy link
Copy Markdown
Contributor Author

[Claude Opus 5 (1M context)] Consulted Devin on 2026-08-10, up to commit e2c4649515f8808dd0ed364217c6fea540929d69.

Devin ran three times, once per commit on this branch. Every run came back with no bugs and nothing flagged for investigation — the seven items on the final run are all Informational, so there are no findings mirrored as review threads here.

Two of the informational points were worth acting on, and did become commits:

  • The new test fixture's summary comment implied that merely constructing a WorkspaceTabSelection is what clears the stale static after a collection switch. It isn't — the new WorkspaceView constructor's assignment is. Corrected, and the fixture is now [NonParallelizable] (2ce2afc).
  • The comment on StageBookForBloomPubPreviewForTest claimed the staging guard means the publish tab has finished its setup. Since the flag now flips ahead of the SelectedTabChangedEvent subscribers, that is no longer what it proves; the e2e flow is safe because selectTab runs the whole switch synchronously inside its own awaited API call. Comment corrected (e2c4649).

The rest were noted and deliberately not acted on; they are in the preflight report for the developer, chiefly the observation that Bloom.Workspace now has a compile-time dependency on Bloom.Publish, with the alternative of making PublishHelper.InPublishTab a read-only property over a registered WorkspaceTabSelection.

CI (pr-automation) is green, and CodeRabbit does not review this repo (.coderabbit.yml sets auto_review.enabled: false).

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