Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
c668207
Add the Bloom Freeze Doctor (BL-16719)
JohnThomson Aug 24, 2026
55923b9
Fix two ways the Doctor would silently not work (BL-16719)
JohnThomson Aug 24, 2026
dbc2e1b
Fix the WebView2 port fallback and the unlocked dedup sets (BL-16719)
JohnThomson Aug 24, 2026
975f03e
Let only one outbox drain run at a time (BL-16719)
JohnThomson Aug 24, 2026
711e7e0
Count the crash dump as work in flight (BL-16719)
JohnThomson Aug 24, 2026
5e69078
Serialize outbox drains across processes, not just within one (BL-16719)
JohnThomson Aug 24, 2026
7789075
Scope the drain gate to the outbox, not to the logon session (BL-16719)
JohnThomson Aug 24, 2026
7ab7271
Make the drain gate actually block, and test that it does (BL-16719)
JohnThomson Aug 24, 2026
830f1ab
Dispose the Process objects the discovery sweep creates (BL-16719)
JohnThomson Aug 24, 2026
de35df5
Tell a person their report is queued, not that it failed (BL-16719)
JohnThomson Aug 24, 2026
994fdaf
Put every Doctor file operation through RobustFile, and drop the exem…
JohnThomson Aug 24, 2026
1ddafc7
Read the wait chain from the right offsets (BL-16719)
JohnThomson Aug 24, 2026
d610766
Give the major publishing operations extra patience (BL-16719)
JohnThomson Aug 24, 2026
75b8a8f
Rename the protocol project to BloomFreezeDoctor.Protocol (BL-16719)
JohnThomson Aug 24, 2026
ffc1081
Stop the new projects redirecting every dev build away from x64 (BL-1…
JohnThomson Aug 24, 2026
e1a71ad
Make the Doctor say what it has done, and survive doing it (BL-16719)
JohnThomson Aug 25, 2026
a49d5fc
Fix what running the Doctor for real, and reviewing it, turned up (BL…
JohnThomson Aug 25, 2026
82c384a
Fix three defects the round review found in today's own fixes (BL-16719)
JohnThomson Aug 25, 2026
497f849
Create the dump-handshake events, without which it could never fire (…
JohnThomson Aug 25, 2026
dd0a9e7
Fix seven defects the Fable review found, four of them silent (BL-16719)
JohnThomson Aug 25, 2026
63d0fbc
Judge a hard failure by the shutdown phase, not the "forced" flag (BL…
JohnThomson Aug 25, 2026
fa31f77
Stop a merge undoing a filed report, and stop two bundles fighting ov…
JohnThomson Aug 25, 2026
ae30ad7
Fix three defects from Devin's review, one of which lost reports outr…
JohnThomson Aug 25, 2026
211b47f
Act on the preflight decisions (BL-16719)
JohnThomson Aug 25, 2026
187dcc9
Do not let the new "being sent" mark strand a report (BL-16719)
JohnThomson Aug 25, 2026
a9e88db
Raise the attachment budget above a real minidump, and stop skipping …
JohnThomson Aug 25, 2026
d50797c
Send oversized artifacts to S3, and cap only what is actually expensi…
JohnThomson Aug 26, 2026
3c86100
Make --force work on the paths that run when Bloom dies (BL-16719)
JohnThomson Aug 26, 2026
a83eab6
Say so when a second Doctor discards the options it was given (BL-16719)
JohnThomson Aug 26, 2026
c9b94ed
Give the window a way to open the card it just filed (BL-16719)
JohnThomson Aug 26, 2026
176e27e
Tidy the balloon comment the previous commit left inaccurate (BL-16719)
JohnThomson Aug 26, 2026
a750cbc
Four report-readability fixes from reading a real card (BL-16719)
JohnThomson Aug 26, 2026
bdaef63
Record that YouTrack's <details> rendering is confirmed, not assumed …
JohnThomson Aug 26, 2026
c0c5a7c
Wait for a crash dump while the Doctor lives, not for three seconds (…
JohnThomson Aug 26, 2026
d187d9b
Do not withdraw crash dumps from a Doctor that predates the "started"…
JohnThomson Aug 26, 2026
6dcafe3
Release a crashing Bloom when its dump lands, not when the report is …
JohnThomson Aug 26, 2026
052a73e
Let --target-name actually narrow what the Doctor watches (BL-16719)
JohnThomson Aug 26, 2026
601dc53
Log the go.sh/--automation papercut found while testing the Doctor (B…
JohnThomson Aug 26, 2026
dffb2db
Readability pass, mainly on the code inside Bloom itself (BL-16719)
JohnThomson Aug 26, 2026
6d54401
Make the Freeze Doctor's comments read as if we got it right first ti…
JohnThomson Aug 26, 2026
6495870
Route the long-operation scope through Publish too (BL-16719)
JohnThomson Aug 26, 2026
db52623
Drop decision provenance from three comments (BL-16719)
JohnThomson Aug 26, 2026
9364ae7
Stop two classes of exit being misjudged (BL-16719)
JohnThomson Aug 26, 2026
90aab0f
Make the shutdown phase an enum, not an int (BL-16719)
JohnThomson Aug 28, 2026
d85fbca
Pin the shutdown phases so a renumber or rename cannot pass unnoticed…
JohnThomson Aug 28, 2026
48daf3b
Make server-thread trouble diagnosable from the card (BL-16719)
JohnThomson Aug 28, 2026
cc52eab
Carry the in-flight request list to the Doctor in the session file (B…
JohnThomson Aug 28, 2026
d22a6cf
Explain the 256-byte jump in the page layout where it happens (BL-16719)
JohnThomson Aug 28, 2026
49a2b56
Call an API request's URL path a urlPath, not a path (BL-16719)
JohnThomson Aug 28, 2026
84d164d
Stop using "Publish" for things that are not Publish-tab work (BL-16719)
JohnThomson Aug 28, 2026
ebd72d3
Record what the session prune actually costs (BL-16719)
JohnThomson Aug 28, 2026
dae5e72
Record how far shutdown got and who asked for it as separate facts (B…
JohnThomson Aug 28, 2026
f47ae83
Drop a misplaced comment and name the quit signal for what it is (BL-…
JohnThomson Aug 28, 2026
102abbf
Put the prune measurements on the method they describe (BL-16719)
JohnThomson Aug 28, 2026
5bb8288
Allow the freeze simulator on the internal channels (BL-16719)
JohnThomson Aug 28, 2026
22be997
Call the detector's observation an observation, not "now" (BL-16719)
JohnThomson Aug 28, 2026
427528a
Stop a late lock note from resurrecting a finished request (BL-16719)
JohnThomson Aug 28, 2026
ae28cfb
Say that Bloom is recording a video while it records one (BL-16719)
JohnThomson Aug 28, 2026
9d0c158
Name the connections.dll lines once, and have both readers use the na…
JohnThomson Aug 28, 2026
5585d1e
Sweep for every installed channel, from one list (BL-16719)
JohnThomson Aug 28, 2026
3a2b4ad
Watch the Bloom a developer actually runs: --automation is not headle…
JohnThomson Aug 28, 2026
9e0a715
Give the Doctor the same library versions Bloom uses (BL-16719)
JohnThomson Aug 28, 2026
4e180b1
Only offer to end the Bloom that is actually blocking a restart (BL-1…
JohnThomson Aug 29, 2026
5f043d4
Say what happened, not which gate stopped us: GatedOut -> AnotherProc…
JohnThomson Aug 29, 2026
0a7d6de
Drop the unused drain flag, and say what "drain" means (BL-16719)
JohnThomson Aug 29, 2026
c51653d
Move DoctorSupervisor into Core, where it can be tested (BL-16719)
JohnThomson Aug 30, 2026
c409d55
Check identity, not just the process id, before believing or ending a…
JohnThomson Aug 30, 2026
e3a622d
Re-arm the Doctor a few minutes after Bloom files its own report (BL-…
JohnThomson Aug 30, 2026
65da817
Fix four defects a Fable review found (BL-16719)
JohnThomson Aug 30, 2026
8a195de
Stop a finished operation being put back on display (BL-16719)
JohnThomson Aug 30, 2026
73af862
Report an exit on evidence, not on the absence of evidence (BL-16719)
JohnThomson Aug 31, 2026
1e17c73
Correct a false claim about why the Doctor's logic is in Core (BL-16719)
JohnThomson Aug 31, 2026
ab72d5e
Drop two exemptions that only existed to make absence-reporting safe …
JohnThomson Aug 31, 2026
63523c1
Stop overstating the exe-versus-dll distinction, and name the real du…
JohnThomson Aug 31, 2026
5ae2377
Delete DoctorSession.Channel, which nothing read (BL-16719)
JohnThomson Aug 31, 2026
dcb455c
Stop handling Blooms with older versions of Doctor support (BL-16719)
JohnThomson Aug 31, 2026
e9650d8
Watch one Bloom at a time (simplification 4) (BL-16719)
JohnThomson Aug 31, 2026
9fd8ad5
Fix three of Fable's findings: a stale port, a false recurrence, an e…
JohnThomson Aug 31, 2026
7a507b6
Work Devin's severe findings: one real, one narrowed, one already fix…
JohnThomson Aug 31, 2026
603763c
Fix Fable's last three that needed no decision (BL-16719)
JohnThomson Aug 31, 2026
b94ba40
Say what could not be attached, and for how long it will still be the…
JohnThomson Aug 31, 2026
1c94caa
Fix two regressions the one-Bloom rewrite introduced (BL-16719)
JohnThomson Aug 31, 2026
ac19c4e
Give one death one report, and say which card it went to (BL-16719)
JohnThomson Aug 31, 2026
7d184a4
Let a recurrence supply the dump a card never got (BL-16719)
JohnThomson Aug 31, 2026
117a545
Do not attach a recurrence's dump if its comment never posted (BL-16719)
JohnThomson Aug 31, 2026
a098233
Tell one crash from another (BL-16719)
JohnThomson Aug 31, 2026
1db43c9
Read the crash entry that actually names the crash (BL-16719)
JohnThomson Aug 31, 2026
cef7fed
Say why a process is not being watched (BL-16719)
JohnThomson Aug 31, 2026
c6d827a
A dump in the bucket is still a dump on the card (BL-16719)
JohnThomson Aug 31, 2026
634a811
Stop the discovery sweep failing in silence (BL-16719)
JohnThomson Aug 31, 2026
61e4675
Settle the bucket-URL question, and give the sweep its last voice (BL…
JohnThomson Aug 31, 2026
b084b3b
Make a shared doctor.log readable, and refuse to watch ourselves (BL-…
JohnThomson Aug 31, 2026
8f26760
Fix the build I broke, and stop the test wrapper hiding that (BL-16719)
JohnThomson Aug 31, 2026
19cf1b4
Found the adoption delay: a WMI query per candidate per tick (BL-16719)
JohnThomson Aug 31, 2026
43ad25a
Count the sweeps, because the WMI fix did not fix the delay (BL-16719)
JohnThomson Aug 31, 2026
ad528cb
Time the sweep in halves; the API is exonerated (BL-16719)
JohnThomson Aug 31, 2026
cd70112
Get WMI off the adoption path: 19 seconds to about a third of one (BL…
JohnThomson Aug 31, 2026
c8c485a
Bloom tells a running Doctor it has started; stop calling unknown "Re…
JohnThomson Sep 1, 2026
4f30b67
Make the startup announcement actually early, and stop it spinning (B…
JohnThomson Sep 1, 2026
7cc6988
go.mjs: stop a running Freeze Doctor, and add --nowatch (BL-16719)
JohnThomson Sep 1, 2026
2643823
Recognise a spinning UI thread, and admit when its stack is unreadabl…
JohnThomson Sep 1, 2026
25aaabd
Stop freeze reasoning appearing on a zombie report (BL-16719)
JohnThomson Sep 1, 2026
698e93b
Say what was thrown, and stop calling our own dump-wait a deadlock (B…
JohnThomson Sep 1, 2026
99079e1
Name the cause of a FailFast, and let a held-back report be sent (BL-…
JohnThomson Sep 1, 2026
2479572
Do not offer an old exception as the cause of a freeze (BL-16719)
JohnThomson Sep 1, 2026
0b7ffaf
Merge Version6.5 into BL-16719-doctor-in-repo
JohnThomson Sep 1, 2026
3153eaf
Record why the Doctor may adopt a Bloom nobody pointed it at (BL-16719)
JohnThomson Sep 1, 2026
3d4e51a
Gate the startup announcement on the Doctor setting after all (BL-16719)
JohnThomson Sep 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions Bloom.sln
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.7.34003.232
Expand Down Expand Up @@ -29,6 +30,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebView2PdfMaker", "src\WebView2PdfMaker\WebView2PdfMaker.csproj", "{F697DD7A-2D74-4850-8381-7E4ADB1E4431}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BloomFreezeDoctor.Protocol", "src\BloomFreezeDoctor.Protocol\BloomFreezeDoctor.Protocol.csproj", "{BC16B558-A063-411A-8BF0-C259CBD62E48}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BloomFreezeDoctor.Core", "src\BloomFreezeDoctor.Core\BloomFreezeDoctor.Core.csproj", "{DFDB8F7F-62BA-4019-891F-378897E56A6E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BloomFreezeDoctor", "src\BloomFreezeDoctor\BloomFreezeDoctor.csproj", "{DB569C6F-CD1A-4F6C-AD1D-5768749F2B68}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BloomFreezeDoctor.Tests", "src\BloomFreezeDoctor.Tests\BloomFreezeDoctor.Tests.csproj", "{993C2D6D-07BC-4929-90F9-67F64753AC71}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Expand Down Expand Up @@ -61,12 +72,48 @@ Global
{F697DD7A-2D74-4850-8381-7E4ADB1E4431}.Release|x64.Build.0 = Release|x64
{F697DD7A-2D74-4850-8381-7E4ADB1E4431}.Release|ARM64.ActiveCfg = Release|ARM64
{F697DD7A-2D74-4850-8381-7E4ADB1E4431}.Release|ARM64.Build.0 = Release|ARM64
{BC16B558-A063-411A-8BF0-C259CBD62E48}.Debug|x64.ActiveCfg = Debug|x64
{BC16B558-A063-411A-8BF0-C259CBD62E48}.Debug|x64.Build.0 = Debug|x64
{BC16B558-A063-411A-8BF0-C259CBD62E48}.Debug|ARM64.ActiveCfg = Debug|ARM64
{BC16B558-A063-411A-8BF0-C259CBD62E48}.Debug|ARM64.Build.0 = Debug|ARM64
{BC16B558-A063-411A-8BF0-C259CBD62E48}.Release|x64.ActiveCfg = Release|x64
{BC16B558-A063-411A-8BF0-C259CBD62E48}.Release|x64.Build.0 = Release|x64
{BC16B558-A063-411A-8BF0-C259CBD62E48}.Release|ARM64.ActiveCfg = Release|ARM64
{BC16B558-A063-411A-8BF0-C259CBD62E48}.Release|ARM64.Build.0 = Release|ARM64
{DFDB8F7F-62BA-4019-891F-378897E56A6E}.Debug|x64.ActiveCfg = Debug|Any CPU
{DFDB8F7F-62BA-4019-891F-378897E56A6E}.Debug|x64.Build.0 = Debug|Any CPU
{DFDB8F7F-62BA-4019-891F-378897E56A6E}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{DFDB8F7F-62BA-4019-891F-378897E56A6E}.Debug|ARM64.Build.0 = Debug|Any CPU
{DFDB8F7F-62BA-4019-891F-378897E56A6E}.Release|x64.ActiveCfg = Release|Any CPU
{DFDB8F7F-62BA-4019-891F-378897E56A6E}.Release|x64.Build.0 = Release|Any CPU
{DFDB8F7F-62BA-4019-891F-378897E56A6E}.Release|ARM64.ActiveCfg = Release|Any CPU
{DFDB8F7F-62BA-4019-891F-378897E56A6E}.Release|ARM64.Build.0 = Release|Any CPU
{DB569C6F-CD1A-4F6C-AD1D-5768749F2B68}.Debug|x64.ActiveCfg = Debug|x64
{DB569C6F-CD1A-4F6C-AD1D-5768749F2B68}.Debug|x64.Build.0 = Debug|x64
{DB569C6F-CD1A-4F6C-AD1D-5768749F2B68}.Debug|ARM64.ActiveCfg = Debug|ARM64
{DB569C6F-CD1A-4F6C-AD1D-5768749F2B68}.Debug|ARM64.Build.0 = Debug|ARM64
{DB569C6F-CD1A-4F6C-AD1D-5768749F2B68}.Release|x64.ActiveCfg = Release|x64
{DB569C6F-CD1A-4F6C-AD1D-5768749F2B68}.Release|x64.Build.0 = Release|x64
{DB569C6F-CD1A-4F6C-AD1D-5768749F2B68}.Release|ARM64.ActiveCfg = Release|ARM64
{DB569C6F-CD1A-4F6C-AD1D-5768749F2B68}.Release|ARM64.Build.0 = Release|ARM64
{993C2D6D-07BC-4929-90F9-67F64753AC71}.Debug|x64.ActiveCfg = Debug|Any CPU
{993C2D6D-07BC-4929-90F9-67F64753AC71}.Debug|x64.Build.0 = Debug|Any CPU
{993C2D6D-07BC-4929-90F9-67F64753AC71}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{993C2D6D-07BC-4929-90F9-67F64753AC71}.Debug|ARM64.Build.0 = Debug|Any CPU
{993C2D6D-07BC-4929-90F9-67F64753AC71}.Release|x64.ActiveCfg = Release|Any CPU
{993C2D6D-07BC-4929-90F9-67F64753AC71}.Release|x64.Build.0 = Release|Any CPU
{993C2D6D-07BC-4929-90F9-67F64753AC71}.Release|ARM64.ActiveCfg = Release|Any CPU
{993C2D6D-07BC-4929-90F9-67F64753AC71}.Release|ARM64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{D38FC737-8C27-46CB-B490-3CB2E7AB3C99} = {04AF3A87-C943-4731-BE3D-659EA705171D}
{BC16B558-A063-411A-8BF0-C259CBD62E48} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
{DFDB8F7F-62BA-4019-891F-378897E56A6E} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
{DB569C6F-CD1A-4F6C-AD1D-5768749F2B68} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
{993C2D6D-07BC-4929-90F9-67F64753AC71} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AFDCD471-511E-432D-B6B3-2F9D5AD1ACED}
Expand Down
8 changes: 7 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,13 @@
This lives here rather than as a -p: on the wrapper's command line because a
global property cannot be varied per project.
-->
<UseAppHost Condition="'$(MSBuildProjectName)' != 'WebView2PdfMaker'">false</UseAppHost>
<!--
BloomFreezeDoctor joins WebView2PdfMaker for the same reason: Bloom launches it by FILE NAME
(BloomFreezeDoctor.exe, see FreezeDoctor/DoctorLauncher.cs), so without its apphost the
"Run Freeze Doctor" menu item silently does nothing in an agent-built tree - which is an
expensive thing to debug, because nothing reports an error.
-->
<UseAppHost Condition="'$(MSBuildProjectName)' != 'WebView2PdfMaker' AND '$(MSBuildProjectName)' != 'BloomFreezeDoctor'">false</UseAppHost>
<!--
Once obj moves out of the project directory, the SDK no longer treats the
conventional in-tree obj\ and bin\ as intermediate output, so their stale
Expand Down
16 changes: 16 additions & 0 deletions PAPERCUTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ House rules:

---

## 2026-08-26 — A Bloom launched by ./go.sh cannot be watched by the Freeze Doctor

- **Cut:** `go.sh` runs Bloom with `--automation`, and the Doctor deliberately refuses to watch any
run whose command line carries that flag (such runs legitimately have no window, so watching them
would manufacture zombie reports). So the repo’s sanctioned dev launcher produces the one kind of
Bloom the Doctor ignores, and an agent following AGENTS.md cannot test the Doctor at all. Launching
the built exe directly instead dies at Velopack init when given no arguments ("Bloom Problem"
immediately), though the same binary starts fine with go.sh’s own arguments. F5 works, which is why
every successful manual test of this feature so far has been F5.
- **Idea:** either have go.sh omit `--automation` (or offer a flag to), or say in AGENTS.md that testing
the Freeze Doctor needs F5 rather than go.sh, and why. Also worth noting that go.sh builds to
`output/Debug/AnyCPU` while launch.json runs `output/Debug/x64`.
- **Context:** BL-16719, trying to run a crash test unattended. A related false start:
`build/agent-dotnet.sh` builds into `output/agent/<key>/`, so `output/Debug/x64` was eleven commits
stale and the first attempt silently exercised old code.

## 2026-07-30 — Visual regression suite reports only the first stale image per case
- **Cut:** Each case in `src/BloomVisualRegressionTests/index.spec.ts` compares the book preview
and then every bloom-player page in sequence, and every comparison throws on failure — so the
Expand Down
11 changes: 11 additions & 0 deletions build/Bloom.proj
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@
build here does NOT go to the same directory as the VS build. -->
<Exec Command='dotnet build "$(RootDir)\src\BloomExe\BloomExe.csproj" --configuration $(Configuration) --runtime win-x64 --output "$(RootDir)\output\$(Configuration)\x64" "/p:OutDir=$(RootDir)\output\$(Configuration)\x64\\" "/p:SolutionDir=$(RootDir)\"' />
<Exec Command='dotnet build "$(RootDir)\src\WebView2PdfMaker\WebView2PdfMaker.csproj" --configuration $(Configuration) --runtime win-x64 --output "$(RootDir)\output\$(Configuration)\x64" "/p:OutDir=$(RootDir)\output\$(Configuration)\x64\\" "/p:SolutionDir=$(RootDir)\"' />
<!-- The Freeze Doctor, which ships inside Bloom's installer rather than having one of its own. It
has to be named here, exactly like WebView2PdfMaker above, BECAUSE this target builds project by
project rather than building the solution: being in Bloom.sln is not enough. Leaving it out would
have failed silently and expensively - the signing step below has ContinueOnError, so an unbuilt
Doctor would simply have been missing from the installer with nothing going red anywhere. -->
<Exec Command='dotnet build "$(RootDir)\src\BloomFreezeDoctor\BloomFreezeDoctor.csproj" --configuration $(Configuration) --runtime win-x64 --output "$(RootDir)\output\$(Configuration)\x64" "/p:OutDir=$(RootDir)\output\$(Configuration)\x64\\" "/p:SolutionDir=$(RootDir)\"' />
<!--If the number of directories between output and OutDir changes, we need a corresponding
change in BloomFileLocatorTests.kRelativePathToBrowserFolder and XMatterHelperTests.OneTimeSetup-->
<Exec Command='dotnet build "$(RootDir)\src\BloomTests\BloomTests.csproj" --configuration $(Configuration) --runtime win-x64 --output "$(RootDir)\output\Tests\$(Configuration)\x64" "/p:OutDir=$(RootDir)\output\Tests\$(Configuration)\x64\\" "/p:SolutionDir=$(RootDir)\"' />
Expand Down Expand Up @@ -507,6 +513,11 @@
<!-- Sign x64 executables -->
<Exec Command='sign "$(RootDir)\output\$(Configuration)\x64\Bloom.exe"' ContinueOnError="$(ContinueOnExeSignError)"></Exec>
<Exec Command='sign "$(RootDir)\output\$(Configuration)\x64\BloomPdfMaker.exe"' ContinueOnError="$(ContinueOnExeSignError)"></Exec>
<!-- The Freeze Doctor. Signed here rather than by an installer of its own, because it no longer has
one: it ships inside Bloom's installer and is switched on from the debug menu. An unsigned exe
inside a signed installer is what behavioural antivirus objects to, and it matters more than
usual for this one, which opens handles to other processes and writes memory dumps. -->
<Exec Command='sign "$(RootDir)\output\$(Configuration)\x64\BloomFreezeDoctor.exe"' ContinueOnError="$(ContinueOnExeSignError)"></Exec>
<!-- restore to Sign ARM64 executables
<Exec Command='sign "$(RootDir)\output\$(Configuration)\arm64\Bloom.exe"' ContinueOnError="$(ContinueOnExeSignError)"></Exec>
<Exec Command='sign "$(RootDir)\output\$(Configuration)\arm64\BloomPdfMaker.exe"' ContinueOnError="$(ContinueOnExeSignError)"></Exec>
Expand Down
8 changes: 8 additions & 0 deletions build/check-csharp-ApplicationExit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ if [ -s $filesToCheck ]; then
# exemption the check fires on any commit that merely stages the file -- a merge
# of master, say -- for code the committer never touched.
src/WebView2PdfMaker/*) continue;;
# BloomFreezeDoctor is the same case as WebView2PdfMaker above, for the same reason: a separate
# executable, its own static Program, no Program.Exit to call instead. Application.Exit is simply
# how its WinForms message loop ends.
#
# Worth noting that this rule, and the RobustFile one next door, both assume every .cs file in the
# repo is Bloom. A second application needs them to know WHICH application a file belongs to, and
# the WebView2PdfMaker line above shows that was already true before the Doctor arrived.
src/BloomFreezeDoctor/*|src/BloomFreezeDoctor.Core/*|src/BloomFreezeDoctor.Tests/*) continue;;
esac
# Strip // line comments and /* ... */ single-line block comments before
# searching, so a mention of Application.Exit in a comment doesn't trip this check.
Expand Down
6 changes: 6 additions & 0 deletions build/check-csharp-robustfile.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ if [ -s $filesToCheck ]; then
case "$file" in
src/BloomExe/RobustFileIO.cs) continue;;
src/BloomTests/*) continue;;
# The Freeze Doctor's TEST project only, matching the src/BloomTests carve-out above for the same
# reason. Its production code is NOT exempt: it was converted to RobustFile/RobustIO rather than
# excused, because a diagnostic tool writing to a user's disk has no more business losing a file to
# a passing virus scanner than Bloom does - and the first thing the conversion found was a real
# one, an outbox rename that failed about one run in three and threw away a gathered report.
src/BloomFreezeDoctor.Tests/*) continue;;
esac
if awk '
# Flag ordinary banned file APIs directly.
Expand Down
29 changes: 21 additions & 8 deletions scripts/watchBloomExe.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,18 @@ const parseArgs = () => {
"..",
),
vitePort: undefined,
// Build and run once instead of running under "dotnet watch". See go.mjs --nowatch.
noWatch: false,
};

for (let i = 0; i < args.length; i++) {
const arg = args[i];

if (arg === "--nowatch" || arg === "--no-watch") {
options.noWatch = true;
continue;
}

if (arg === "--repo-root") {
options.repoRoot = requireOptionValue(args, i, "--repo-root");
i++;
Expand Down Expand Up @@ -131,14 +138,20 @@ const tryInferVitePortFromRunningBloom = async () => {
const effectiveVitePort =
options.vitePort ?? (await tryInferVitePortFromRunningBloom());

const dotnetArgs = [
"watch",
"run",
"--project",
projectPath,
"--",
"--automation",
];
// With --nowatch we run Bloom directly rather than under "dotnet watch". The watcher costs real time
// before Bloom appears - it builds, then starts its own file watching over the whole project - and buys
// nothing at all unless you are editing C#. Everything below still works: the file-change handling simply
// never fires, because nothing reports a change.
const dotnetArgs = options.noWatch
? ["run", "--project", projectPath, "--", "--automation"]
: ["watch", "run", "--project", projectPath, "--", "--automation"];

if (options.noWatch) {
console.log(
"Running without dotnet watch: C# edits will NOT rebuild by themselves. " +
"The front end is unaffected - TypeScript and LESS still update live through Vite.",
);
}

const startupLabel = getHelpfulStartupLabel(options.repoRoot);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,9 @@ describe("aiImageEditorOverlay: the live page is NOT saved after a commit", () =
commitAndReplyFromHost(postFromEditor, false);

// The overlay stays up so the user can read the error about the slot that failed.
expect(document.getElementById("ai-image-editor-overlay")).not.toBeNull();
expect(
document.getElementById("ai-image-editor-overlay"),
).not.toBeNull();
expect(postThatMightNavigate).not.toHaveBeenCalled();

closeButton.click();
Expand Down
19 changes: 19 additions & 0 deletions src/BloomBrowserUI/react_components/TopBar/TopBarContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const TopBarContextMenu: React.FunctionComponent<{
React.useState(false);
const [isMeddlingWithNewFiles, setIsMeddlingWithNewFiles] =
React.useState(false);
const [runFreezeDoctor, setRunFreezeDoctor] = React.useState(false);
const [canChooseDevBloomLibrary, setCanChooseDevBloomLibrary] =
React.useState(false);
const [useDevBloomLibrary, setUseDevBloomLibrary] = React.useState(false);
Expand All @@ -53,6 +54,11 @@ export const TopBarContextMenu: React.FunctionComponent<{
getBoolean("app/isMeddlingWithNewFiles", (value) => {
setIsMeddlingWithNewFiles(value);
});
// Persisted across runs, so the check mark has to come from the back end rather than
// from local state: it may well have been turned on during a previous session.
getBoolean("app/runFreezeDoctor", (value) => {
setRunFreezeDoctor(value);
});
// Only some builds offer the choice of web site, and only the back end knows
// which web site this run of Bloom uses.
getBoolean("app/canChooseDevBloomLibrary", (value) => {
Expand Down Expand Up @@ -163,6 +169,18 @@ export const TopBarContextMenu: React.FunctionComponent<{
setIsMeddlingWithNewFiles(newValue);
},
},
{
// The Freeze Doctor ships inside Bloom but does nothing unless switched on here.
// Turning it on starts it immediately, so you can switch it on while chasing a freeze
// rather than having to restart Bloom first.
label: "Run Freeze Doctor",
selected: runFreezeDoctor,
onClick: () => {
const newValue = !runFreezeDoctor;
postBoolean("app/runFreezeDoctor", newValue);
setRunFreezeDoctor(newValue);
},
},
];
if (canChooseDevBloomLibrary) {
items.push({ label: "-" });
Expand All @@ -183,6 +201,7 @@ export const TopBarContextMenu: React.FunctionComponent<{
alwaysMeasurePerformance,
currentlyMeasuring,
isMeddlingWithNewFiles,
runFreezeDoctor,
canChooseDevBloomLibrary,
useDevBloomLibrary,
]);
Expand Down
Loading