Skip to content

fix: 144 watcher tails the right task and survives an empty one - #397

Open
s205109 wants to merge 27 commits into
mainfrom
fix/wt-watch-task-picks-the-wrong-sess-03536c53
Open

fix: 144 watcher tails the right task and survives an empty one#397
s205109 wants to merge 27 commits into
mainfrom
fix/wt-watch-task-picks-the-wrong-sess-03536c53

Conversation

@s205109

@s205109 s205109 commented Sep 8, 2026

Copy link
Copy Markdown
Owner

Backlog 144. The watcher tailed a task from another session, then stopped with a
parameter-binding error instead of printing output. Either fault alone made the watcher
unusable during a long run.

Closes backlog 144 (moved to backlog/done/ in this PR).

The two original defects

A zero-byte output file crashed the tail. PowerShell unrolls an array when it captures a
statement's value, and a zero-length array unrolls to nothing. Three else branches stored
$null instead of an empty array, and Set-TailReaderCheckpoint rejected it. The comma
operator (return , [byte[]]::new(0)) fixes it. A zero-byte file was also why the watcher
chose that file: an empty file has no terminal marker, so the old rule counted it as running,
and a fresh file has the newest write time.

A file nobody was writing counted as running. The old rule called a task running when its
text did not end with [exited with code N] or [killed]. Nothing checked whether the session
that wrote it still existed. Measured on one machine: 489 output files matched this repository,
41 counted as running, 4 were actually being written, and the oldest "running" file had not
changed for 15 days.

What changed

  • Liveness replaces the marker rule for "is it running". Test-TaskFileHeldOpen asks the
    operating system whether a writer holds the output file open. The marker rule stays for the
    terminal state (exit code, killed) and for ending the follow loop.
  • A third terminal state. A file with no marker that nobody holds is "stopped without a
    terminal marker". Under the old rule a missing exit code could only mean killed.
  • Selection prefers, in order: the caller's own session (from CLAUDE_CODE_SESSION_ID),
    then the checkout the script runs from, then the newest by write time. Each preference is
    skipped when it matches no running task. This narrows the half of backlog 123's "newest
    across everything" rule that ADR 0014 supersedes; the search still reaches every worktree.
  • -List shows every running task, then fills the rest to twenty rows with the newest
    stopped tasks. -Index addresses that same list, so the count line names only tasks the
    reader can reach.
  • The tail names its session and checkout under Tailing <path>.
  • The follow loop ends on liveness, not only on the marker rule, so a writer that closes
    without a marker no longer keeps the watcher polling a dead file for ever.

Review rounds

Five review rounds after the first implementation:

  • A race where Get-TaskState and Test-TaskFileHeldOpen observed the file at two instants: a
    marker written between them produced a wrong "stopped without a terminal marker" verdict and
    dropped the marker line. Fixed by probing liveness before the state read at all three sites,
    with a settle-loop reconciliation on the now-stable file.
  • The reconciliation then had to re-probe liveness, because a replacement run could open the
    file during the first probe and be wrongly reported stopped.
  • The checkout-preference end-to-end test only worked from a worktree; rebuilt on an isolated
    two-checkout git repo.
  • Doc corrections: the probe is not an exact writer test (a read handle with no sharing trips
    the same error); header text still described "newest running".
  • The settle-loop reconciliation was a parallel catch-up path that had lost the bounded
    read-failure retry and the deferred-read check, and its final liveness re-probe still ran
    after the state read (the marker race again). Consolidated: the separate drain is removed;
    the settle loop does up to three quiet confirming rounds through the one catch-up path, so a
    late marker or a replacement writer is seen by the next round. Select-WatchTaskRecord also
    dropped its OwnCheckoutPath parameter — its value was never compared.
  • The discovery path had the same probe-before-state gap as the settle loop, at a different
    site. Get-WatchTaskRecord stored the first liveness probe as Running, so a replacement
    writer that opened before the state read was recorded stopped and Select-WatchTaskRecord
    dropped it. It now re-probes once when the state says running and the first probe said not
    held. The CONTEXT.md "Stale task file" entry, which still said such a file "reads as a
    Running task", now names that as the old rule.

Verification

Repository tooling, no compiled surface, so the artifact is the PowerShell suite.

  • pwsh ./scripts/run-powershell-suites.ps1 — 56/56 pass, including WatchTask.Tests.ps1
    (17 new cases), CitationFreshness.Tests.ps1, BacklogStaleOpen.Tests.ps1,
    BacklogPlanPointer.Tests.ps1.
  • Full pre-PR gate: dotnet build (Release), dotnet format --verify-no-changes, PowerShell
    suites, coverage slice (all per-assembly thresholds met), git diff --check — all green.
  • The fifth review round changed only scripts/watch-task.ps1 and CONTEXT.md, both outside
    the C# gate per .github/code-paths-filter.yml; its gate was the PowerShell suites, re-run
    green (56/56).
  • Every behavioral fix has a regression test that was red for the stated reason before the fix.

Records

  • ADR 0014 — a running task is one holding its output file open.
  • Plan and spec (docs/superpowers/) frozen at ship.
  • Item 144 moved to backlog/done/, all acceptance boxes ticked.

🤖 Generated with Claude Code

Zero-byte output file: PowerShell unrolls the empty else branch to $null,
so Set-TailReaderCheckpoint gets $null. Reproduced end to end. Also
measured the inflated running count (39 running, -List shows 20, 4 of
those running) and found CLAUDE_CODE_SESSION_ID names the session folder.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Draft PR #397 exists, so the Pickup exit condition is met.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Running task = output file held open for writing, matched on HResult
0x80070020 only. Marker rule keeps exit code and killed state. Terms
pinned in CONTEXT.md Process; spec and ADR pointers in the item.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ADR 0014 said 123's rule was still true; session and checkout preference
means newest is now the last resort, so say superseded. Adds the third
terminal state and follow-stop to Consequences. Item's out-of-scope no
longer excludes -List, since criterion 5 cannot hold with a fixed window.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Also add AllowEmptyCollection to the New-FakeTaskOutput test helper so the
new zero-byte fixture can pass -Lines @().
Watch-Record gains the Session/Checkout/OwnSessionId parameters and their
two print lines here rather than in a later task, because the session
preference test asserts the 'Session: <id> (this session)' line.
The new list-window test binds its result to a local variable that is not
named $list: the dot-sourced script leaves a [switch] $List in scope and a
PSCustomObject cannot be stored there.
The nine follow child cases pin Test-TaskFileHeldOpen to held. Eight of
them run inside Start-Job, where a bare $heldStub reference is empty, so
those pass the stub text through -ArgumentList and a param() entry.
Watch-Record's parameters and print lines landed in the session-preference
task; this adds the -Index path wiring and the checkout assertions.
The watch-task.ps1 reorg moved lines cited by 144's Background section, ADR
0014, and shipped item 123. All three are pre-fix records, so freeze each
file with citation-check:ignore-file rather than repoint the line numbers.
Get-TaskState and Test-TaskFileHeldOpen observed the file at two instants.
A writer that appended its marker and closed between them left a stale
"no marker" state next to a fresh "not held" probe, and the watcher then
reported "stopped without a terminal marker" and dropped the marker line.

Probe liveness before the state read at all three sites (discovery, the
follow loop's end-of-file check, the settle loop), so the state read is
the newer observation. In the settle loop, when the probe says the writer
is gone but the state still says running, drain to the end and read the
state once more on the now-stable file.

Also from review:
- add an end-to-end case for the checkout preference (own-checkout task
  older than a task from another checkout)
- tighten the zero-byte checkpoint assertion, which passed vacuously
- correct the header text that still described "newest running" selection
From review: a read handle opened with no sharing also trips the sharing
violation, so the probe cannot single out a writer. Soften the ADR and the
Test-TaskFileHeldOpen docstring to say so; nothing but the harness opens
these files, so in practice the violation is still the writer's handle.

Also replace the brittle line numbers in 144's acceptance evidence with
identifiers, since the code edits kept moving them.
Swapping the settle probe and state read (previous review round) opened a
new gap: a replacement run that opens the file between the probe and the
read leaves "not held" stale. The reconciliation drained its output and
then reported it stopped. It now probes liveness once more, on the drained
file, and keeps following when something holds it again.

Also from review: the checkout-preference end-to-end case only worked from
a worktree, because the script derives its own checkout from its own path.
It now builds an isolated repo with a real main and worktree, drops a copy
of the script in the worktree, and runs that, so it is checkout-independent.
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

Test Results

62 tests   61 ✅  3m 29s ⏱️
 1 suites   0 💤
 1 files     1 ❌

For more details on these failures, see this check.

Results for commit bc29b28.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

Summary

Summary
Generated on: 09/09/2026 - 21:26:01
Coverage date: 09/09/2026 - 21:20:56 - 09/09/2026 - 21:25:53
Parser: MultiReport (15x Cobertura)
Assemblies: 8
Classes: 428
Files: 327
Line coverage: 94.7% (7285 of 7690)
Covered lines: 7285
Uncovered lines: 405
Coverable lines: 7690
Total lines: 27547
Branch coverage: 82.8% (2157 of 2605)
Covered branches: 2157
Total branches: 2605
Method coverage: Feature is only available for sponsors
Tag: 879_34406127602

Coverage

ahkflow - 96.6%
Name Line Branch
ahkflow 96.6% 87.3%
AHKFlowApp.CLI.Commands.Auth.LoginCommand 100%
AHKFlowApp.CLI.Commands.Auth.LogoutCommand 100%
AHKFlowApp.CLI.Commands.Downloads.AhkDownloadCommand 100%
AHKFlowApp.CLI.Commands.Downloads.DownloadCommand 100%
AHKFlowApp.CLI.Commands.Downloads.ZipDownloadCommand 100%
AHKFlowApp.CLI.Commands.Hotstrings.HotstringCommand 100%
AHKFlowApp.CLI.Commands.Hotstrings.ListHotstringCommand 100%
AHKFlowApp.CLI.Commands.Hotstrings.NewHotstringCommand 100% 100%
AHKFlowApp.CLI.Commands.RootCli 0%
AHKFlowApp.CLI.Exceptions.AuthConfigurationException 100%
AHKFlowApp.CLI.Exceptions.NotAuthenticatedException 100%
AHKFlowApp.CLI.Exceptions.ProfileNotFoundException 100%
AHKFlowApp.CLI.Output.DownloadDestination 100% 100%
AHKFlowApp.CLI.Output.DownloadTarget 100%
AHKFlowApp.CLI.Output.HotstringJsonFormatter 100%
AHKFlowApp.CLI.Output.HotstringTableFormatter 98.6% 94%
AHKFlowApp.CLI.Services.ApiException 100%
AHKFlowApp.CLI.Services.BearerTokenHandler 100%
AHKFlowApp.CLI.Services.BinaryStdout 100% 100%
AHKFlowApp.CLI.Services.CliApiFailureDetector 100% 85%
AHKFlowApp.CLI.Services.CliHttpClientBuilderExtensions 100% 75%
AHKFlowApp.CLI.Services.ConsoleErrorDeviceCodePromptWriter 100% 50%
AHKFlowApp.CLI.Services.ConsoleErrorHttpRetryStatusWriter 100% 50%
AHKFlowApp.CLI.Services.CreateHotstringDto 100%
AHKFlowApp.CLI.Services.DeviceCodePrompt 100%
AHKFlowApp.CLI.Services.DownloadsApiClient 100% 87.5%
AHKFlowApp.CLI.Services.HotstringDto 100%
AHKFlowApp.CLI.Services.HotstringsApiClient 100%
AHKFlowApp.CLI.Services.HttpRetryStatusMessages 100%
AHKFlowApp.CLI.Services.LocalAppDataAuthCachePathProvider 0% 0%
AHKFlowApp.CLI.Services.MsalDeviceCodeTokenProvider 78.5% 83.3%
AHKFlowApp.CLI.Services.PagedList`1 100% 50%
AHKFlowApp.CLI.Services.ProfilesApiClient 100%
AHKFlowApp.CLI.Services.WorkingDirectory 100% 100%
AHKFlowApp.API - 84.1%
Name Line Branch
AHKFlowApp.API 84.1% 60%
AHKFlowApp.API.Auth.HttpContextCurrentUser 100% 46.1%
AHKFlowApp.API.Auth.TestAuthenticationHandler 100%
AHKFlowApp.API.Controllers.CategoriesController 100%
AHKFlowApp.API.Controllers.DashboardController 100%
AHKFlowApp.API.Controllers.DevController 100%
AHKFlowApp.API.Controllers.DownloadsController 100%
AHKFlowApp.API.Controllers.HealthController 100%
AHKFlowApp.API.Controllers.HotkeysController 100%
AHKFlowApp.API.Controllers.HotstringsController 100%
AHKFlowApp.API.Controllers.KnownShortcutsController 100%
AHKFlowApp.API.Controllers.PreferencesController 100%
AHKFlowApp.API.Controllers.ProfilesController 100%
AHKFlowApp.API.Controllers.VersionController 100%
AHKFlowApp.API.Controllers.WhoAmIController 100%
AHKFlowApp.API.DevDatabaseReadiness 100% 100%
AHKFlowApp.API.DevDockerSqlServer 0% 0%
AHKFlowApp.API.Extensions.ApiExtensions 100% 80%
AHKFlowApp.API.Extensions.ProblemDetailsResultExtensions 100% 100%
AHKFlowApp.API.Middleware.GlobalExceptionMiddleware 100%
AHKFlowApp.API.Models.HealthResponse 100%
AHKFlowApp.API.OpenApi.Examples.CreateHotstringDtoExample 100%
AHKFlowApp.API.OpenApi.Examples.HotstringDtoExample 100%
AHKFlowApp.API.OpenApi.Examples.HotstringHistoryVersionDtoExample 100%
AHKFlowApp.API.OpenApi.Examples.HotstringPreviewDtoExample 100%
AHKFlowApp.API.OpenApi.Examples.HotstringPreviewRequestDtoExample 100%
AHKFlowApp.API.OpenApi.Examples.PagedHotstringsExample 100%
AHKFlowApp.API.OpenApi.Examples.UpdateHotstringDtoExample 100%
AHKFlowApp.Application - 98.3%
Name Line Branch
AHKFlowApp.Application 98.3% 90.2%
AHKFlowApp.Application.Behaviors.ValidatingUseCase`2 100%
AHKFlowApp.Application.Commands.Categories.CreateCategoryCommandHandler 100%
AHKFlowApp.Application.Commands.Categories.CreateCategoryCommandValidator 100%
AHKFlowApp.Application.Commands.Categories.DeleteCategoryCommandHandler 100%
AHKFlowApp.Application.Commands.Categories.UpdateCategoryCommandHandler 100%
AHKFlowApp.Application.Commands.Categories.UpdateCategoryCommandValidator 100%
AHKFlowApp.Application.Commands.Dev.HotstringSeedSamples 100%
AHKFlowApp.Application.Commands.Dev.SeedAllCommandHandler 68.7% 33.3%
AHKFlowApp.Application.Commands.Dev.SeedCategoriesCommandHandler 100%
AHKFlowApp.Application.Commands.Dev.SeedHotkeysCommandHandler 100%
AHKFlowApp.Application.Commands.Dev.SeedHotstringsCommandHandler 100%
AHKFlowApp.Application.Commands.Hotkeys.BulkDeleteHotkeysCommandHandler 100%
AHKFlowApp.Application.Commands.Hotkeys.BulkDeleteHotkeysCommandValidator 100%
AHKFlowApp.Application.Commands.Hotkeys.CreateHotkeyCommandHandler 100%
AHKFlowApp.Application.Commands.Hotkeys.CreateHotkeyCommandValidator 100%
AHKFlowApp.Application.Commands.Hotkeys.DeleteHotkeyCommandHandler 100%
AHKFlowApp.Application.Commands.Hotkeys.HotkeyConflictMessages 100% 100%
AHKFlowApp.Application.Commands.Hotkeys.PurgeDeletedHotkeyCommandHandler 100%
AHKFlowApp.Application.Commands.Hotkeys.RestoreHotkeyCommandHandler 100%
AHKFlowApp.Application.Commands.Hotkeys.RevertHotkeyCommandHandler 100%
AHKFlowApp.Application.Commands.Hotkeys.UpdateHotkeyCommandHandler 100%
AHKFlowApp.Application.Commands.Hotkeys.UpdateHotkeyCommandValidator 100%
AHKFlowApp.Application.Commands.Hotstrings.BulkDeleteHotstringsCommandHandl
er
100%
AHKFlowApp.Application.Commands.Hotstrings.BulkDeleteHotstringsCommandValid
ator
100%
AHKFlowApp.Application.Commands.Hotstrings.CreateHotstringCommandHandler 100%
AHKFlowApp.Application.Commands.Hotstrings.CreateHotstringCommandValidator 100%
AHKFlowApp.Application.Commands.Hotstrings.DeleteHotstringCommandHandler 100%
AHKFlowApp.Application.Commands.Hotstrings.HotstringImportClassifier 100% 100%
AHKFlowApp.Application.Commands.Hotstrings.ImportHotstringsCommandHandler 100%
AHKFlowApp.Application.Commands.Hotstrings.ImportHotstringsCommandValidator 100%
AHKFlowApp.Application.Commands.Hotstrings.PreviewHotstringImportCommandHan
dler
100%
AHKFlowApp.Application.Commands.Hotstrings.PreviewHotstringImportCommandVal
idator
100%
AHKFlowApp.Application.Commands.Hotstrings.PurgeDeletedHotstringCommandHand
ler
100%
AHKFlowApp.Application.Commands.Hotstrings.RestoreHotstringCommandHandler 100%
AHKFlowApp.Application.Commands.Hotstrings.RevertHotstringCommandHandler 100%
AHKFlowApp.Application.Commands.Hotstrings.UpdateHotstringCommandHandler 100%
AHKFlowApp.Application.Commands.Hotstrings.UpdateHotstringCommandValidator 100%
AHKFlowApp.Application.Commands.KnownShortcuts.CreateCustomKnownShortcutCom
mandHandler
100%
AHKFlowApp.Application.Commands.KnownShortcuts.CreateCustomKnownShortcutCom
mandValidator
100% 62.5%
AHKFlowApp.Application.Commands.KnownShortcuts.DeleteCustomKnownShortcutCom
mandHandler
100%
AHKFlowApp.Application.Commands.KnownShortcuts.IgnoreKnownShortcutCommandHa
ndler
100%
AHKFlowApp.Application.Commands.KnownShortcuts.RestoreKnownShortcutCommandH
andler
100%
AHKFlowApp.Application.Commands.Preferences.UpdateUserPreferenceCommandHand
ler
100%
AHKFlowApp.Application.Commands.Preferences.UpdateUserPreferenceCommandVali
dator
100%
AHKFlowApp.Application.Commands.Profiles.CreateProfileCommandHandler 100%
AHKFlowApp.Application.Commands.Profiles.CreateProfileCommandValidator 100%
AHKFlowApp.Application.Commands.Profiles.DeleteProfileCommandHandler 100%
AHKFlowApp.Application.Commands.Profiles.UpdateProfileCommandHandler 100%
AHKFlowApp.Application.Commands.Profiles.UpdateProfileCommandValidator 100%
AHKFlowApp.Application.Common.HistorySaveRetry 100% 50%
AHKFlowApp.Application.Constants.DefaultHotkey 100%
AHKFlowApp.Application.Constants.DefaultHotkeyCatalog 100%
AHKFlowApp.Application.Constants.HeaderPreset 100%
AHKFlowApp.Application.Constants.HeaderPresetCatalog 100%
AHKFlowApp.Application.Constants.HotkeyKeyEntry 100%
AHKFlowApp.Application.Constants.HotkeyKeys 100% 100%
AHKFlowApp.Application.Constants.KnownShortcut 100%
AHKFlowApp.Application.Constants.KnownShortcutCatalog 100%
AHKFlowApp.Application.Constants.ShortcutUse 100%
AHKFlowApp.Application.Constants.ShortcutWording 100%
AHKFlowApp.Application.DependencyInjection 100%
AHKFlowApp.Application.DTOs.BulkDeleteResultDto 100%
AHKFlowApp.Application.DTOs.CategoryDto 100%
AHKFlowApp.Application.DTOs.CreateCustomKnownShortcutDto 100%
AHKFlowApp.Application.DTOs.CreateHotkeyDto 100%
AHKFlowApp.Application.DTOs.CreateHotstringDto 100%
AHKFlowApp.Application.DTOs.CreateProfileDto 100%
AHKFlowApp.Application.DTOs.DashboardStatsDto 100%
AHKFlowApp.Application.DTOs.DeletedHotkeyDto 100%
AHKFlowApp.Application.DTOs.DeletedHotstringDto 100%
AHKFlowApp.Application.DTOs.EntityStatsDto 100%
AHKFlowApp.Application.DTOs.HeaderPresetDto 100%
AHKFlowApp.Application.DTOs.HotkeyDto 100%
AHKFlowApp.Application.DTOs.HotkeyHistoryVersionDto 100%
AHKFlowApp.Application.DTOs.HotkeyKeyCatalogDto 100%
AHKFlowApp.Application.DTOs.HotkeyKeyDto 100%
AHKFlowApp.Application.DTOs.HotkeyPreviewRequestDto 100%
AHKFlowApp.Application.DTOs.HotkeySnapshot 100%
AHKFlowApp.Application.DTOs.HotstringDto 100%
AHKFlowApp.Application.DTOs.HotstringHistoryVersionDto 100%
AHKFlowApp.Application.DTOs.HotstringImportPreviewDto 100%
AHKFlowApp.Application.DTOs.HotstringImportResultDto 100%
AHKFlowApp.Application.DTOs.HotstringImportRowDto 100%
AHKFlowApp.Application.DTOs.HotstringPreviewDto 100%
AHKFlowApp.Application.DTOs.HotstringPreviewRequestDto 100%
AHKFlowApp.Application.DTOs.HotstringSnapshot 100%
AHKFlowApp.Application.DTOs.ImportHotstringsRequestDto 100%
AHKFlowApp.Application.DTOs.KnownShortcutDto 100%
AHKFlowApp.Application.DTOs.ManagedKnownShortcutDto 100%
AHKFlowApp.Application.DTOs.ManagedShortcutUseDto 100%
AHKFlowApp.Application.DTOs.PagedList`1 100% 100%
AHKFlowApp.Application.DTOs.ProfileDto 100%
AHKFlowApp.Application.DTOs.ProfileScriptPreviewDto 100%
AHKFlowApp.Application.DTOs.ProfileStatsDto 100%
AHKFlowApp.Application.DTOs.RawSummaryDto 100%
AHKFlowApp.Application.DTOs.RecentActivityItemDto 100%
AHKFlowApp.Application.DTOs.SeedAllResultDto 100%
AHKFlowApp.Application.DTOs.ShortcutUseDto 100%
AHKFlowApp.Application.DTOs.UpdateHotkeyDto 100%
AHKFlowApp.Application.DTOs.UpdateHotstringDto 100%
AHKFlowApp.Application.DTOs.UpdateProfileDto 100%
AHKFlowApp.Application.Mapping.CategoryMapping 100%
AHKFlowApp.Application.Mapping.HotkeyMappings 100%
AHKFlowApp.Application.Mapping.HotstringMappings 100%
AHKFlowApp.Application.Mapping.ProfileMappings 100%
AHKFlowApp.Application.Mapping.UserPreferenceMappings 100%
AHKFlowApp.Application.Queries.Categories.GetCategoryQueryHandler 100%
AHKFlowApp.Application.Queries.Categories.ListCategoriesQuery 100%
AHKFlowApp.Application.Queries.Categories.ListCategoriesQueryHandler 100%
AHKFlowApp.Application.Queries.Categories.ListCategoriesQueryValidator 100%
AHKFlowApp.Application.Queries.Dashboard.GetDashboardStatsQueryHandler 100% 100%
AHKFlowApp.Application.Queries.Downloads.GenerateAllProfileScriptsQueryHand
ler
100% 100%
AHKFlowApp.Application.Queries.Downloads.GenerateAllProfileScriptsZipQueryH
andler
100%
AHKFlowApp.Application.Queries.Downloads.GenerateProfileScriptQueryHandler 100%
AHKFlowApp.Application.Queries.Downloads.GetProfileScriptPreviewQueryHandle
r
100%
AHKFlowApp.Application.Queries.Hotkeys.GetHotkeyHistoryQueryHandler 100%
AHKFlowApp.Application.Queries.Hotkeys.GetHotkeyHistoryVersionQueryHandler 100%
AHKFlowApp.Application.Queries.Hotkeys.GetHotkeyPreviewQueryHandler 100% 100%
AHKFlowApp.Application.Queries.Hotkeys.GetHotkeyPreviewQueryValidator 100%
AHKFlowApp.Application.Queries.Hotkeys.GetHotkeyQueryHandler 100%
AHKFlowApp.Application.Queries.Hotkeys.ListDeletedHotkeysQuery 100%
AHKFlowApp.Application.Queries.Hotkeys.ListDeletedHotkeysQueryHandler 100%
AHKFlowApp.Application.Queries.Hotkeys.ListHotkeyKeysQuery 100%
AHKFlowApp.Application.Queries.Hotkeys.ListHotkeyKeysQueryHandler 100%
AHKFlowApp.Application.Queries.Hotkeys.ListHotkeysQuery 100%
AHKFlowApp.Application.Queries.Hotkeys.ListHotkeysQueryHandler 44.4% 51.7%
AHKFlowApp.Application.Queries.Hotkeys.ListHotkeysQueryValidator 100%
AHKFlowApp.Application.Queries.Hotkeys.ListKnownShortcutsQuery 100%
AHKFlowApp.Application.Queries.Hotkeys.ListKnownShortcutsQueryHandler 100%
AHKFlowApp.Application.Queries.Hotstrings.GetHotstringHistoryQueryHandler 100%
AHKFlowApp.Application.Queries.Hotstrings.GetHotstringHistoryVersionQueryHa
ndler
100%
AHKFlowApp.Application.Queries.Hotstrings.GetHotstringPreviewQueryHandler 100% 100%
AHKFlowApp.Application.Queries.Hotstrings.GetHotstringPreviewQueryValidator 100%
AHKFlowApp.Application.Queries.Hotstrings.GetHotstringQueryHandler 100%
AHKFlowApp.Application.Queries.Hotstrings.ListDeletedHotstringsQuery 100%
AHKFlowApp.Application.Queries.Hotstrings.ListDeletedHotstringsQueryHandler 100%
AHKFlowApp.Application.Queries.Hotstrings.ListHotstringsQuery 100%
AHKFlowApp.Application.Queries.Hotstrings.ListHotstringsQueryHandler 90.9% 66%
AHKFlowApp.Application.Queries.Hotstrings.ListHotstringsQueryValidator 100%
AHKFlowApp.Application.Queries.KnownShortcuts.ListManagedKnownShortcutsQuer
y
100%
AHKFlowApp.Application.Queries.KnownShortcuts.ListManagedKnownShortcutsQuer
yHandler
100%
AHKFlowApp.Application.Queries.Preferences.GetUserPreferenceQueryHandler 100%
AHKFlowApp.Application.Queries.Profiles.GetProfileQueryHandler 100%
AHKFlowApp.Application.Queries.Profiles.ListHeaderPresetsQuery 100%
AHKFlowApp.Application.Queries.Profiles.ListHeaderPresetsQueryHandler 100%
AHKFlowApp.Application.Queries.Profiles.ListProfilesQueryHandler 100%
AHKFlowApp.Application.Services.AhkEscaping 100%
AHKFlowApp.Application.Services.AhkFileNaming 100% 100%
AHKFlowApp.Application.Services.AhkHotstringParser 96.6% 90.8%
AHKFlowApp.Application.Services.AhkScriptGenerator 100% 100%
AHKFlowApp.Application.Services.EntityHistoryRecorder 100%
AHKFlowApp.Application.Services.HeaderTokenRenderer 95.3% 97.3%
AHKFlowApp.Application.Services.HotkeyEmitter 100% 97.3%
AHKFlowApp.Application.Services.HotstringEmitter 98.7% 98.3%
AHKFlowApp.Application.Services.KnownShortcutMerge 100% 91.6%
AHKFlowApp.Application.Services.LegacyHotkeyDefinitionConverter 100% 100%
AHKFlowApp.Application.Services.LegacyHotkeySnapshotConverter 100% 75%
AHKFlowApp.Application.Services.MacroToken 100%
AHKFlowApp.Application.Services.MacroTokenParser 100% 100%
AHKFlowApp.Application.Services.ProfileScriptLoader 100%
AHKFlowApp.Application.Services.RawCommentLift 100% 100%
AHKFlowApp.Application.Services.RawHotkeyBodyScanner 94.3% 94.5%
AHKFlowApp.Application.Services.RawHotstringDefinitionParser 95.3% 92%
AHKFlowApp.Application.Services.RawParseResult 100%
AHKFlowApp.Application.Services.ScriptToRawComposer 100% 100%
AHKFlowApp.Application.Validation.CategoryRules 100%
AHKFlowApp.Application.Validation.HotkeyRules 100% 97.2%
AHKFlowApp.Application.Validation.HotstringRules 100% 100%
AHKFlowApp.Application.Validation.ProfileRules 100% 100%
AHKFlowApp.Application.Validation.WindowContextRules 100% 100%
AHKFlowApp.Domain - 100%
Name Line Branch
AHKFlowApp.Domain 100% 100%
AHKFlowApp.Domain.Constants.DefaultCategories 100%
AHKFlowApp.Domain.Entities.Category 100%
AHKFlowApp.Domain.Entities.CustomKnownShortcut 100%
AHKFlowApp.Domain.Entities.EntityHistory 100%
AHKFlowApp.Domain.Entities.Hotkey 100%
AHKFlowApp.Domain.Entities.HotkeyCategory 100%
AHKFlowApp.Domain.Entities.HotkeyDefinition 100%
AHKFlowApp.Domain.Entities.HotkeyProfile 100%
AHKFlowApp.Domain.Entities.Hotstring 100%
AHKFlowApp.Domain.Entities.HotstringCategory 100%
AHKFlowApp.Domain.Entities.HotstringDefinition 100%
AHKFlowApp.Domain.Entities.HotstringProfile 100%
AHKFlowApp.Domain.Entities.IgnoredKnownShortcut 100%
AHKFlowApp.Domain.Entities.Profile 100%
AHKFlowApp.Domain.Entities.UserPreference 100% 100%
AHKFlowApp.Infrastructure - 100%
Name Line Branch
AHKFlowApp.Infrastructure 100% 60%
AHKFlowApp.Infrastructure.DependencyInjection 100%
AHKFlowApp.Infrastructure.Persistence.AppDbContext 100%
AHKFlowApp.Infrastructure.Persistence.Configurations.CategoryConfiguration 100%
AHKFlowApp.Infrastructure.Persistence.Configurations.CustomKnownShortcutCon
figuration
100%
AHKFlowApp.Infrastructure.Persistence.Configurations.EntityHistoryConfigura
tion
100%
AHKFlowApp.Infrastructure.Persistence.Configurations.HotkeyCategoryConfigur
ation
100%
AHKFlowApp.Infrastructure.Persistence.Configurations.HotkeyConfiguration 100%
AHKFlowApp.Infrastructure.Persistence.Configurations.HotkeyProfileConfigura
tion
100%
AHKFlowApp.Infrastructure.Persistence.Configurations.HotstringCategoryConfi
guration
100%
AHKFlowApp.Infrastructure.Persistence.Configurations.HotstringConfiguration 100%
AHKFlowApp.Infrastructure.Persistence.Configurations.HotstringProfileConfig
uration
100%
AHKFlowApp.Infrastructure.Persistence.Configurations.IgnoredKnownShortcutCo
nfiguration
100%
AHKFlowApp.Infrastructure.Persistence.Configurations.ProfileConfiguration 100%
AHKFlowApp.Infrastructure.Persistence.Configurations.UserPreferenceConfigur
ation
100%
AHKFlowApp.Infrastructure.Services.AssemblyAppVersionProvider 100% 64.2%
AHKFlowApp.Infrastructure.Services.VersionService 100% 50%
AHKFlowApp.Launcher - 71.7%
Name Line Branch
AHKFlowApp.Launcher 71.7% 73.3%
AHKFlowApp.Launcher.DockerSqlControl 22.5% 0%
AHKFlowApp.Launcher.LauncherPlan 100%
AHKFlowApp.Launcher.ProjectLaunch 100%
AHKFlowApp.Launcher.WorktreeLocalDevManifest 100% 91.6%
AHKFlowApp.TestUtilities - 93.6%
Name Line Branch
AHKFlowApp.TestUtilities 93.6% 81.7%
AHKFlowApp.TestUtilities.Auth.TestAuthHandler 100% 88.8%
AHKFlowApp.TestUtilities.Auth.TestUserBuilder 80.9%
AHKFlowApp.TestUtilities.Builders.CategoryBuilder 100%
AHKFlowApp.TestUtilities.Builders.CustomKnownShortcutBuilder 89.4%
AHKFlowApp.TestUtilities.Builders.HealthResponseBuilder 0%
AHKFlowApp.TestUtilities.Builders.HotkeyBuilder 95.6% 100%
AHKFlowApp.TestUtilities.Builders.HotstringBuilder 95.4% 100%
AHKFlowApp.TestUtilities.Builders.IgnoredKnownShortcutBuilder 88.8%
AHKFlowApp.TestUtilities.Builders.ProfileBuilder 100%
AHKFlowApp.TestUtilities.Fixtures.ApiTestFixture 100% 50%
AHKFlowApp.TestUtilities.Fixtures.CustomWebApplicationFactory 100% 100%
AHKFlowApp.TestUtilities.Fixtures.HotkeyKeyFixtures 100%
AHKFlowApp.TestUtilities.Fixtures.LegacyHotkeyFixture 100%
AHKFlowApp.TestUtilities.Fixtures.LegacyHotkeyFixtures 100%
AHKFlowApp.TestUtilities.Fixtures.MigratedDbFixture 100% 50%
AHKFlowApp.TestUtilities.Fixtures.ScriptToRawFixture 100%
AHKFlowApp.TestUtilities.Fixtures.ScriptToRawFixtures 100%
AHKFlowApp.TestUtilities.Fixtures.SharedSqlContainer 100%
AHKFlowApp.TestUtilities.Fixtures.SharedSqlServerFixture 100% 50%
AHKFlowApp.TestUtilities.Fixtures.SqlContainerFixture 100% 50%
AHKFlowApp.TestUtilities.Fixtures.SqlTestDatabase 100% 85%
AHKFlowApp.TestUtilities.Fixtures.TestTimingRecorder 95.2% 62.5%
AHKFlowApp.TestUtilities.Fixtures.TypedHotkeyActionCase 100%
AHKFlowApp.TestUtilities.Fixtures.TypedHotkeyActionFixtures 100%
AutoGeneratedProgram 0%
AHKFlowApp.UI.Blazor - 91.2%
Name Line Branch
AHKFlowApp.UI.Blazor 91.2% 77%
AHKFlowApp.UI.Blazor.Auth.ApiAuthorizationMessageHandler 0%
AHKFlowApp.UI.Blazor.Auth.AzureAdSettings 100% 100%
AHKFlowApp.UI.Blazor.Auth.TestAuthenticationProvider 100%
AHKFlowApp.UI.Blazor.Components.Common.BlockedIconButton 100%
AHKFlowApp.UI.Blazor.Components.Common.CategoryFilterChips 100% 100%
AHKFlowApp.UI.Blazor.Components.Common.EntityChips 100% 100%
AHKFlowApp.UI.Blazor.Components.Common.EntityMultiSelect 84.6% 56.2%
AHKFlowApp.UI.Blazor.Components.Common.KeyPicker 0%
AHKFlowApp.UI.Blazor.Components.Common.MacroReplacementDisplay 83.3% 80%
AHKFlowApp.UI.Blazor.Components.Home.CliQuickstartCard 100%
AHKFlowApp.UI.Blazor.Components.Home.RecentActivityCard 66.6% 53.8%
AHKFlowApp.UI.Blazor.Components.Home.StatCard 100%
AHKFlowApp.UI.Blazor.Components.Hotkeys.HotkeyActionChip 100% 100%
AHKFlowApp.UI.Blazor.Components.Hotkeys.HotkeyEditDialog 89.5% 75.6%
AHKFlowApp.UI.Blazor.Components.Hotkeys.HotkeyHistoryDialog 87.5% 50%
AHKFlowApp.UI.Blazor.Components.Hotkeys.HotkeyMobileList 75.8% 45.8%
AHKFlowApp.UI.Blazor.Components.Hotkeys.ShortcutWarning 100% 88.4%
AHKFlowApp.UI.Blazor.Components.Hotstrings.HotstringEditDialog 89.2% 73.4%
AHKFlowApp.UI.Blazor.Components.Hotstrings.HotstringHistoryDialog 93.3% 50%
AHKFlowApp.UI.Blazor.Components.Hotstrings.HotstringImportDialog 73% 40%
AHKFlowApp.UI.Blazor.Components.Hotstrings.HotstringKindChip 100% 100%
AHKFlowApp.UI.Blazor.Components.Hotstrings.HotstringMobileList 89.3% 100%
AHKFlowApp.UI.Blazor.Components.KnownShortcuts.KnownShortcutMobileList 100% 100%
AHKFlowApp.UI.Blazor.Components.KnownShortcuts.KnownShortcutSourceChip 100% 100%
AHKFlowApp.UI.Blazor.Components.KnownShortcuts.KnownShortcutUseActions 100% 100%
AHKFlowApp.UI.Blazor.Components.Profiles.HeaderPresetPickerDialog 75%
AHKFlowApp.UI.Blazor.DTOs.ApiProblemDetails 100%
AHKFlowApp.UI.Blazor.DTOs.BulkDeleteResultDto 100%
AHKFlowApp.UI.Blazor.DTOs.CategoryDto 100%
AHKFlowApp.UI.Blazor.DTOs.ChangelogDocumentDto 100%
AHKFlowApp.UI.Blazor.DTOs.ChangelogEntryDto 100%
AHKFlowApp.UI.Blazor.DTOs.ChangelogSectionDto 100%
AHKFlowApp.UI.Blazor.DTOs.CreateCustomKnownShortcutDto 100%
AHKFlowApp.UI.Blazor.DTOs.CreateHotkeyDto 100%
AHKFlowApp.UI.Blazor.DTOs.CreateHotstringDto 100%
AHKFlowApp.UI.Blazor.DTOs.CreateProfileDto 100%
AHKFlowApp.UI.Blazor.DTOs.DashboardStatsDto 100%
AHKFlowApp.UI.Blazor.DTOs.DeletedHotkeyDto 100%
AHKFlowApp.UI.Blazor.DTOs.DeletedHotstringDto 100%
AHKFlowApp.UI.Blazor.DTOs.EntityStatsDto 100%
AHKFlowApp.UI.Blazor.DTOs.HeaderPresetDto 100%
AHKFlowApp.UI.Blazor.DTOs.HealthResponse 100%
AHKFlowApp.UI.Blazor.DTOs.HotkeyDto 100%
AHKFlowApp.UI.Blazor.DTOs.HotkeyHistoryVersionDto 100%
AHKFlowApp.UI.Blazor.DTOs.HotkeyKeyCatalogDto 100%
AHKFlowApp.UI.Blazor.DTOs.HotkeyKeyDto 100%
AHKFlowApp.UI.Blazor.DTOs.HotkeyListRequest 100%
AHKFlowApp.UI.Blazor.DTOs.HotkeyPreviewRequestDto 100%
AHKFlowApp.UI.Blazor.DTOs.HotkeySnapshot 100%
AHKFlowApp.UI.Blazor.DTOs.HotstringDto 100%
AHKFlowApp.UI.Blazor.DTOs.HotstringHistoryVersionDto 100%
AHKFlowApp.UI.Blazor.DTOs.HotstringImportPreviewDto 100%
AHKFlowApp.UI.Blazor.DTOs.HotstringImportResultDto 100%
AHKFlowApp.UI.Blazor.DTOs.HotstringImportRowDto 100%
AHKFlowApp.UI.Blazor.DTOs.HotstringListRequest 100%
AHKFlowApp.UI.Blazor.DTOs.HotstringPreviewDto 100%
AHKFlowApp.UI.Blazor.DTOs.HotstringPreviewRequestDto 100%
AHKFlowApp.UI.Blazor.DTOs.HotstringSnapshot 100%
AHKFlowApp.UI.Blazor.DTOs.ImportHotstringsRequestDto 100%
AHKFlowApp.UI.Blazor.DTOs.KnownShortcutDto 100%
AHKFlowApp.UI.Blazor.DTOs.ManagedKnownShortcutDto 100%
AHKFlowApp.UI.Blazor.DTOs.ManagedShortcutUseDto 100%
AHKFlowApp.UI.Blazor.DTOs.PagedList`1 100%
AHKFlowApp.UI.Blazor.DTOs.ProfileDto 100%
AHKFlowApp.UI.Blazor.DTOs.ProfileScriptPreviewDto 100%
AHKFlowApp.UI.Blazor.DTOs.ProfileStatsDto 100%
AHKFlowApp.UI.Blazor.DTOs.RawSummaryDto 100%
AHKFlowApp.UI.Blazor.DTOs.RecentActivityItemDto 100%
AHKFlowApp.UI.Blazor.DTOs.ShortcutUseDto 100%
AHKFlowApp.UI.Blazor.DTOs.UpdateHotkeyDto 100%
AHKFlowApp.UI.Blazor.DTOs.UpdateHotstringDto 100%
AHKFlowApp.UI.Blazor.DTOs.UpdateProfileDto 100%
AHKFlowApp.UI.Blazor.Helpers.DeliveryDisplay 88.8% 83.3%
AHKFlowApp.UI.Blazor.Helpers.DownloadFileNames 100%
AHKFlowApp.UI.Blazor.Helpers.Greeting 100% 100%
AHKFlowApp.UI.Blazor.Helpers.HomeTimeFormat 100% 100%
AHKFlowApp.UI.Blazor.Helpers.HotkeyActionDisplay 84% 75%
AHKFlowApp.UI.Blazor.Helpers.HotstringKindDisplay 93.3% 81.8%
AHKFlowApp.UI.Blazor.Helpers.KnownShortcutWarning 96.8% 93.1%
AHKFlowApp.UI.Blazor.Helpers.MacroTextPiece 100%
AHKFlowApp.UI.Blazor.Helpers.MacroTokens 100% 100%
AHKFlowApp.UI.Blazor.Helpers.ProfileTemplateUse 100%
AHKFlowApp.UI.Blazor.Helpers.RawDecomposition 100%
AHKFlowApp.UI.Blazor.Helpers.RawDefinition 77.6% 61.2%
AHKFlowApp.UI.Blazor.Helpers.RowCombination 100%
AHKFlowApp.UI.Blazor.Helpers.ShortcutScopeDisplay 100% 100%
AHKFlowApp.UI.Blazor.Helpers.TemplateHotkey 100%
AHKFlowApp.UI.Blazor.Helpers.TemplateKeyUses 95.5% 95.4%
AHKFlowApp.UI.Blazor.Helpers.TemplateUseText 98.1% 91.6%
AHKFlowApp.UI.Blazor.Layout.MainLayout 0%
AHKFlowApp.UI.Blazor.Pages.Categories 100% 50%
AHKFlowApp.UI.Blazor.Pages.Changelog 100%
AHKFlowApp.UI.Blazor.Pages.Downloads 100%
AHKFlowApp.UI.Blazor.Pages.Health 100% 100%
AHKFlowApp.UI.Blazor.Pages.Home 100%
AHKFlowApp.UI.Blazor.Pages.Hotkeys 80% 48.3%
AHKFlowApp.UI.Blazor.Pages.Hotstrings 79.1% 65.6%
AHKFlowApp.UI.Blazor.Pages.KnownShortcuts 90.9% 100%
AHKFlowApp.UI.Blazor.Pages.Profiles 100% 78.5%
AHKFlowApp.UI.Blazor.Pages.RecycleBin 100%
AHKFlowApp.UI.Blazor.Pages.Settings 100% 50%
AHKFlowApp.UI.Blazor.Services.AhkFlowAppApiHttpClient 100%
AHKFlowApp.UI.Blazor.Services.ApiClientBase 81.8% 80%
AHKFlowApp.UI.Blazor.Services.ApiErrorMessageFactory 81.2% 61.7%
AHKFlowApp.UI.Blazor.Services.ApiResult 100%
AHKFlowApp.UI.Blazor.Services.ApiResult`1 100%
AHKFlowApp.UI.Blazor.Services.CategoriesApiClient 100% 100%
AHKFlowApp.UI.Blazor.Services.ChangelogClient 100%
AHKFlowApp.UI.Blazor.Services.DashboardApiClient 100%
AHKFlowApp.UI.Blazor.Services.DownloadOutcome 100%
AHKFlowApp.UI.Blazor.Services.DownloadsApiClient 100%
AHKFlowApp.UI.Blazor.Services.FieldErrorMapper 100% 100%
AHKFlowApp.UI.Blazor.Services.HeaderPresetInserter 100% 80%
AHKFlowApp.UI.Blazor.Services.HotkeyKeyCatalog 96% 95.4%
AHKFlowApp.UI.Blazor.Services.HotkeysApiClient 83.7% 66.6%
AHKFlowApp.UI.Blazor.Services.HotstringsApiClient 83% 61.1%
AHKFlowApp.UI.Blazor.Services.HybridUserPreferencesService 90.9%
AHKFlowApp.UI.Blazor.Services.JsFileSaver 0%
AHKFlowApp.UI.Blazor.Services.KnownShortcutCatalog 100%
AHKFlowApp.UI.Blazor.Services.KnownShortcutsApiClient 100%
AHKFlowApp.UI.Blazor.Services.LocalStorageUserPreferencesService 100%
AHKFlowApp.UI.Blazor.Services.PreferencesApiClient 100%
AHKFlowApp.UI.Blazor.Services.PreviewScheduler`2 100% 100%
AHKFlowApp.UI.Blazor.Services.ProfilesApiClient 85.7%
AHKFlowApp.UI.Blazor.Services.ProfileScriptDownloader 100% 100%
AHKFlowApp.UI.Blazor.Shared.LoginDisplay 100%
AHKFlowApp.UI.Blazor.Shared.RedirectToLogin 0%
AHKFlowApp.UI.Blazor.Startup.DevConfig 100%
AHKFlowApp.UI.Blazor.Startup.RequireApiConnectivity 100% 75%
AHKFlowApp.UI.Blazor.Startup.StartupConfigValidator 100% 100%
AHKFlowApp.UI.Blazor.Startup.StartupError 98.3% 90%
AHKFlowApp.UI.Blazor.Startup.StartupErrorRoot 100% 100%
AHKFlowApp.UI.Blazor.Startup.StartupErrorState 100%
AHKFlowApp.UI.Blazor.Validation.CategoryEditModel 100%
AHKFlowApp.UI.Blazor.Validation.HotkeyEditModel 98.9% 95%
AHKFlowApp.UI.Blazor.Validation.HotstringEditModel 95.5% 89.2%
AHKFlowApp.UI.Blazor.Validation.ProfileEditModel 100%

Per-assembly thresholds: Domain line>=85% br>=70% ; Application line>=85% br>=45% ; Infrastructure line>=70% br>=50% ; API line>=57% br>=50% ; UI.Blazor line>=65% br>=28%
Local reproduction: pwsh .\scripts\run-coverage.ps1
Threshold-only rerun: python .\scripts\ci\check-coverage-thresholds.py
Coverage policy: docs\development\coverage.md

@s205109 s205109 changed the title fix: 144 watch-task wrong session and null checkpoint fix: 144 watcher tails the right task and survives an empty one Sep 9, 2026
@s205109
s205109 marked this pull request as ready for review September 9, 2026 15:46
Round-3 review found two P2 gaps in the extra drain added last round: a
read that failed there broke out with no retry and no deferred-read check,
and its liveness re-probe ran after the state read, so a marker written in
that gap was still missed and dropped from the output.

Removed the separate drain. The settle loop now does up to three extra
"quiet" rounds -- nothing read, no marker, no writer -- before it reports a
markerless stop. A marker or a replacement writer that appears during one
round's probe is seen by the next round's read, through the same catch-up
path that already carries the bounded retry and the deferred-read handling.

Also from review: Select-WatchTaskRecord dropped its OwnCheckoutPath
parameter. The value was never compared -- only its emptiness gated a
filter that reads the record's own OwnCheckout flag. The preference now
always applies and self-skips when no running task belongs to the checkout.
Get-WatchTaskRecord probed liveness, then read the file text, then stored
the earlier probe as Running. A replacement writer that opened in the gap
was missed: the record read stopped while a writer held the file, and
Select-WatchTaskRecord dropped it, so the caller could follow another
session. Re-probe once when the state says running and the first probe
said not held, matching the follow loop's reconciliation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The Stale task file glossary entry said such a file reads as a Running
task. That is the old marker-only rule. The Watcher now checks the write
handle, so it reads a held-by-nobody file as stopped. Say so.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.

2 participants