Releases: CosmicCEO/BoloKit
Release list
v1.6.9 — Guest builder resource accounting
v1.6.9 — Guest builder resource accounting
Tagged 2026-09-28 on merge commit 7a4f3d6 (PR #177).
Summary
Patch on top of v1.6.8. Two guest-only accounting defects, both found while reproducing the
v1.6.8 pill desync with a real host and a real join client in one test process: a guest's
builder work cost it nothing, because the host's stale copy of the guest's resources was
pushed back onto the guest. Both were reproduced in that harness before anything was changed.
Fixed since v1.6.8
- Guest builder spends were refunded (#171,
PR #173). A guest deducts trees locally
when its builder launches (repair, road, wall, boat, pill) and carries them out. The host
never learns of the spend, so its own count for the guest stays at the old value, and any
laterSRTankStatus(a fired shell, a hit, a refuel) put that count back on the guest while
the builder still carried the spent trees. Repairs and builds were free, and harvested trees
were wiped the same way. Wider than the issue's repair case: a road reproduced it too.- Fix:
recvSrTankStatusapplies the host'streesonly with a respawn teleport, the one
time the host really resets them (Spawn.swift). Trees belong to the guest, as in the
reference, where the server keeps no tree count. No wire change.
- Fix:
- Guest builder mine placement never cost the host a mine
(#174,
PR #176). Same shape, different fix. A
guest's mine count is the host's (SRTankStatus, key-drop, base refuel), but its builder
spends one locally at launch and the host never did: after one placement the guest read 39
and the host 40, and any later status handed the mine back.- Fix:
HostSessionspends one of a host-simulated player's mines onCLPlaceMine,
clamped at zero, mirroring the existing key-drop path. Spent whether or not the tile takes
it; the reference never refunds a builder-placed mine. Without host simulation the guest
still owns its count. Mines cannot be handed back to the guest the way trees were, since the
host is authoritative for them. The reference server keeps no mine count either, so the
deduction is port-only.
- Fix:
Testing
BoloKitTests: 650 to 652 (two newrecvSrTankStatustests; one existing test no longer
pins the removed behaviour).DifferentialTests: four newHostSessionTestsdispatch tests.Bolo 2026Tests:PillDesyncReproTests2 to 5 tests. Three are new and parameterised (a
guest pill repair, road, and mine placement, each with and without a shot mid-trip), real
host and real guest. Without the fixes they fail in 5 of 16 case-runs (trees) and 2 of 2
(mines); with them all six cases passed in each of 4 repeated iterations.makeHostin that suite gained ahiddenMinesparameter (default unchanged): the builder
tests turn Hidden Mines off, because fog-redacted tiles read as solid sea to a builder.
Live-verified by Jerod on two Macs, 2026-09-28: the #171 fix on a Desktop build of that fix
(the tree cost stays paid after shots mid-trip, harvested trees survive), and the #174 fix on
the 1.6.9 build (a placed mine costs one and stays spent).
Known, not resolved in this release:
- The two original
PillDesyncReproTestsand the older
guestRequestingAndLeavingAnAllianceUpdatesItsOwnStatepass when their suite runs alone and
can fail inside the full app run. Cause not found. swift testhangs at 0% CPU on real-socket dispatch tests and had to be stopped, twice this
cycle.dispatchBuildRoadTerrainByteMatchesTheNewTerrainD40hangs when run alone, with and
without this release's changes, and fails in a broader run.
Found, not fixed
- A guest builder killed en route keeps its local mine or tree deduction until the next status
(from code reading, not reproduced). - A guest builder killed while carrying a pill drops it locally only (from code reading).
- No tank-follow scrolling (#172); the
reference re-centres near the view edge (GSXBoloController.m:2600-2619).
Docs
The v1.5.1 two-Mac fog test artifacts (.docx and .pdf copies) and docs/Small.map were
removed (PR #175); still in git history.
v1.6.8
v1.6.8 — Guest spawn, camera, and pill sync
Tagged 2026-09-27 on merge commit 30c39a5 (PR #168).
Summary
Patch on top of v1.6.7. Two live-play reports from Jerod, both guest-only: the guest's screen
stayed on the wrong part of the map after joining, with base icons showing in unexplored fog;
and a guest that died carrying pills could not pick them back up, then died at once driving onto
the centre one, which the host showed as a damaged live pillbox. Both were reproduced with a
real host and a real join client in one test process before anything was changed.
Fixed since v1.6.7
- Guest spawned twice.
applyBoloPreamblespawns the guest locally at a random start; the
host, which simulates guest tanks, then picks its own start and teleports the guest there
(SRTankStatus). The camera centred once on the first pick and never moved, and the first
pick counted as a fog vision source.- Fix: the join path starts the local player dead until the host places it
(joinPathInitialState,GameSession.swift).GameRenderViewre-centres whenever the
local player goes dead -> alive, matchingrefresh:'sclient.spawnedcheck in the
reference (GSXBoloController.m:2574-2598). Host and solo respawns re-centre too.
- Fix: the join path starts the local player dead until the host places it
- Own-pill pickups were never reported. The host's per-tick capture broadcast was keyed on
a pill's owner changing. A guest re-collecting pills it already owned changed only
ground -> onboard, so the guest never saw them leave the ground.- Fix: the diff in
HostGameEngine.tick()also reports ground -> onboard.
- Fix: the diff in
- Guest counted pill damage twice. A join client's dead-reckoning ran
shellTickfor
remote shells against its own world, damaging pills locally on top of the host'sSRDamage.
The guest's armour ran low, so a pill the host held at armour 1 looked destroyed; driving
onto it made the hostsuperboomthe guest.- Fix:
applyRemotePlayerUpdatetakesextrapolationMutatesWorld;UDPSessionpasses
false, keeping player state and discarding pill, base, and terrain changes.
- Fix:
- Stale fog test.
aLivePlayerWithinItsOwnVisionRectResolvesLiveleft its player dead and
had been failing sincecb1f456. The player is now alive.
Testing
Nine new tests in Bolo 2026Tests: SpawnRecenterTests (4), JoinPathSpawnTests (2),
PillDesyncReproTests (2), and one camera test in GameViewFocusRoutingTests. App suite is
162 tests. Each new test fails with its fix removed.
Live-verified by Jerod on the 1.6.8 Desktop build, 2026-09-27: every previously observed
issue is gone and nothing new appeared.
Known, not resolved in this release:
- The two
PillDesyncReproTestsand the older
guestRequestingAndLeavingAnAllianceUpdatesItsOwnStatepass when their suite runs alone and
fail inside the full app run, parallel or serial. The alliance test fails the same way
without this release's changes. Cause not found. swift testhung twice during this work and was stopped. Of the package tests that ran,
dispatchBuildRoadTerrainByteMatchesTheNewTerrainD40failed, including before any package
source was changed.
Found, not fixed
- A guest builder's pill repair costs no trees: the host never deducts them.
- A guest builder killed while carrying a pill drops it locally only (from code reading).
- No tank-follow scrolling; the reference re-centres near the view edge
(GSXBoloController.m:2600-2619).
Docs
HOSTMODELS.md moved out of docs/notes/; the two-Mac fog script, its run notes, and
U.S.A.map removed, with references updated.
v1.6.7 — Guest fog undraw + status-panel label scramble
v1.6.7 — Guest fog undraw + status-panel label scramble
Tagged 2026-09-27.
Summary
Patch on top of v1.6.6. After the prior join-time fog fix (#163), Jerod's continued live play surfaced three further ways the guest client's view diverged from the host's: stray pill/base icons in unexplored territory, the trailing edge of explored terrain reverting to plain ocean while moving, and the Status panel's Pillbox/Base rows showing each other's content (confirmed live via a side-by-side host/client screenshot). All three root-caused and fixed — see #164.
Fixed since v1.6.6
- Fog "undraw" race (also explains the residual pill/base leak).
#159's join/solo client computes its own local fog (updateFogVisionTracker) the instant its vision rect covers a cell, but the host separately redacts and streams terrain over the wire (SRRevealTerrain) — a second, uncoordinated fog computation the C oracle never needed (one fog tracker per process, always full ground truth). If the local 0→1 snapshot raced ahead of the matching reveal packet, a stale placeholder got permanently baked intoseenTiles; invisible while the cell stayed visible (the live-render branch masks it), it surfaced later as a bogus revert to ocean, or a stuck pill/base icon, once vision moved on.- Fix:
decreaseVis(Sources/BoloKit/FogState.swift) now re-samplesseenTilesfrom live ground truth at the exact moment a cell'sfogcount reaches 0 — a last-visible-moment refresh instead of trusting the (possibly stale) first-sight snapshot. Additive to the oracle'sdecreasevis()(which never touchedseentiles); a no-op whenever there was no race. Threaded through all 8 call sites inFogState.swiftandSources/BoloNet/HostGameEngine.swift.
- Fix:
- Scrambled Pillbox/Base status labels.
PlayerStatusGrid.list(snapshot:)(Bolo 2026/Bolo 2026/PlayerStatusView.swift) built one SwiftUIListwith threeSections (Players, Pillboxes, Bases), eachForEachusing plain, overlapping small-integer ids —Listrow-diffing operates over the whole list's identity space, not scoped perSection, so a pill at offset 0 and a base at offset 0 were indistinguishable to it. Confirmed live: the host's Pillboxes section showed Base names/status, the client's Bases section showed Pillbox names/status, each device scrambled differently.- Fix: every row now carries a globally-unique id (
"player-\(index)"/"pillbox-\(offset)"/"base-\(offset)").
- Fix: every row now carries a globally-unique id (
Testing
New regression test testDecreaseVisRefreshesTheSnapshotFromLiveGroundTruthWhenVisionIsLost (Tests/DifferentialTests/FogDifferentialTests.swift). FogDifferentialTests 43/43 green (was 42/42 — one net-new test). HostGameEngineTests 44/45 green plus 1 pre-existing, already-documented flaky timing test (hostGameEngineBroadcastsExactlyAtTheTimeLimitBoundaryTickThenNeverAgain, see docs/STATUS.md's "Tests" line), confirmed passing in isolation and unrelated to this change. Live re-verified by Jerod on real host/join windows: trailing-edge fog draw and Status panel both confirmed fixed.
See docs/STATUS.md for full detail.
v1.6.6 — Visibility Parity
v1.6.6 — Visibility Parity
Tagged 2026-09-24.
Summary
Fifth theme in the 1.6.x visual + play parity arc (man/tank/boat/sound, v1.6.5) that Jerod's
original roadmap ruling missed: forest concealment. Live-observed and reported: a tank entering
forested terrain should become progressively less visible with distance and cover -- never
fully invisible up close, fading in as an observer approaches.
Fixed since v1.6.5
- #153 -- forest concealment (
forestvis()/
calcvis(),bolo.c:174/217-321) was already a faithful, differentially-tested port
(Sources/BoloKit/CalcVis.swift,Sources/BoloKit/PillTick.swift) and already wired into
rendering (GameRenderView.swift'svisFraction) -- but gated behindstate.hiddenMines
being on and afogStateexisting. Forest concealment is core, always-on oracle gameplay,
unrelated to the separate Hidden Mines fog-of-war feature (#1); the default (hiddenMines
off) meant it never fired at all. - Host/client asymmetry, confirmed live: the host got concealment (when Hidden Mines was
on), but a joined guest never did, in any mode --GameRenderView.render(_:fogState:)defaults
fogStatetonil, and only the host render path ever supplied a real one. Forest
concealment only ever neededstate.terrain(already synced to guests), neverfogState
itself -- only the fog term did. - Fix:
calcVisnow acceptsfogState: FogState?; a missing one means "no fog
contribution," not "skip forest concealment."visFraction's tank/walking-builder path
(matching the oracle's own per-sprite-kindcalcvis()choice) always callscalcVisnow.
Fixes host, solo, and join uniformly through the same render call sites -- no join-path-
specific change needed. The parachuting-builder state deliberately stays on plainfogVis
(GSBoloView.m:389usesfogvis, notcalcvis, for an airborne builder -- correct oracle
behavior, not a gap). - Softened the sea/river water textures (
applySeaShading/applyRiverFlow,
GlyphSource.swift) -- reported as too bold/high-contrast; halved each texture's color delta
from its base fill.
Testing
4 new differential unit tests (FogDifferentialTests.swift, calcVis with fogState: nil)
plus 2 new offscreen pixel-diff render tests (VisibilityParityTests.swift) confirming the
actual reported symptom: a forest-buried enemy tank renders differently than one in the open
with no fogState at all, and more visibly the closer the observer gets. 148/148 Bolo 2026Tests and 956/956 SwiftPM tests green (known pre-existing timing/port-contention flakes,
confirmed unrelated and passing in isolation).
See docs/STATUS.md for full detail.
v1.6.5 — Visual & Play Parity Arc
v1.6.5 — Visual & Play Parity Arc
Tagged 2026-09-24.
Summary
Bundles everything that landed on main since v1.6.0: the visual-polish milestone (v1.6.1,
#137/#140/#110) and the full four-theme visual/play parity arc Jerod scoped on 2026-09-23 --
one theme per release in name (v1.6.2 man, v1.6.3 tank, v1.6.4 boat, v1.6.5 sound) but
none tagged individually as they landed, so all four ship together here -- plus #139, a real
host-engine crash fix found via a live crash-log audit, not a parity item.
Fixed / added since v1.6.0
Visual polish (v1.6.1, milestone 21):
- #137 -- black seam lines in the live Metal terrain overlay, fixed at the geometry level
(quad padding inMetalTileRenderer.swift). - #140 -- a joined guest's
connectionAge(for:)had no data for other players, so lag-tint
staleness coloring silently disappeared on the client's own HUD; host/guest panels now match. - #110 -- all 7 procedural terrain families (sea, grass, swamp, forest, river, boat, crater,
road) now have distinct autotiled textures instead of flat fills; still 100% procedural, no
imported art (hard constraint).
Man parity (v1.6.2, milestone 23):
- #146 -- the builder ("little green man") had no visual identity at all, a placeholder growing
square for every frame; now a walking-figure silhouette (BUILD0/BUILD1) and a parachute-canopy
silhouette (BUILD2). - #145 -- tank-crush-builder, a disclosed new mechanic (real memory of the original, not shown
in theReference/coracle): a tank overlapping an enemy builder's tile now kills it, reusing
the existingkillBuilderrespawn-as-parachute path.
Tank parity (v1.6.3, milestone 24):
- #147 -- a boated tank rendered pixel-identical to a land tank;
GlyphRole.tankhad noboat
parameter, so the already-correct sprite-row selection never actually changed anything on
screen. Added a distinct hull silhouette.
Boat parity (v1.6.4, milestone 25):
- #148 -- boarding a boat never set
player.boat = trueon the host's authoritative state,
under any connection topology -- the root cause of a live "boats don't look like boats"
report, and the reason #147's new hull sprite had no live trigger until this fixed it.
Sound parity (v1.6.5, milestone 26):
- #149 -- a real networked host and a joined guest played zero gameplay sound;
SoundPlayer
was only ever reachable from the single-process/solo tick loop. Added
HostGameEngine.onShouldPlaySound, wired the same way the existing render/message callbacks
are, plus the join path's local-prediction call sites. - #150 -- sound played "near" unconditionally regardless of distance. Fixed for the host path
whenever Hidden Mines is on, reusing the existingFogState/isFogmachinery. The larger,
always-on vision-system extension needed for full parity on every mode/path is a real, known
gap but was explicitly ruled out of scope for solo play (this port's primary mode has no
second listener for a far-sound distinction to matter to) -- parked in theDecide: Oracle parking lotmilestone, not silently dropped.
Host-engine crash fix (#139, not a parity item, milestone 20):
- A hosted session left running for hours crashed with
SIGABRT-- corruptedArrayrefcounts
onGameState.local. Root cause:GameSession'sadminState/liveStateread
HostGameEngine.statedirectly, a plain stored property mutated off-main by the engine's own
tick-loopTask, racing that mutation from the@MainActorHUD/admin-panel read path. Fixed
by caching the already-safe value-type snapshotHostGameEnginehands toonTickRendered
(the one sanctioned way to getstateoff the tick loop) instead of reading the live property.
Testing
Every fix above has its own regression test. Full swift test (956 SwiftPM tests) and
xcodebuild test -scheme "Bolo 2026" -destination "platform=macOS" (146 tests) both green at
merge, modulo the documented pre-existing real-clock/port-contention flakes under full parallel
runs (hostGameEngineSubmitPauseResumeServerTogglesPauseState,
hostGameEngineBroadcastsExactlyAtTheTimeLimitBoundaryTickThenNeverAgain,
aSimulatedGuestDrivingOntoAMineDetonatesItAndLosesArmour), each individually confirmed
unrelated and passing in isolation.
See docs/STATUS.md for the full milestone-by-milestone detail this summary condenses.
v1.6.0 — Metal-tastic
v1.6.0 — Metal-tastic
Tagged 2026-09-23.
Summary
Terrain and sprite rendering moved from CPU CGContext blits to a live Metal overlay, removing
the render-cost ceiling that capped the tile budget at 9,000 (roughly a third of the map) —
Metal is now the default renderer, and there's no longer a visible tile-count ceiling at max
zoom-out. Along the way, live two-Mac testing surfaced and fixed a real, unrelated multiplayer
desync bug: guest-side pill/base capture, combat damage, base refuel, and builder-completion
actions weren't reaching the other player, a side effect of the earlier #59/#62
host-simulates-guest-tanks redesign moving those actions off the network-broadcasting code path.
Every fix in this release has been confirmed on real two-Mac hardware.
Fixed / added since v1.5.1
Metal renderer (#25):
- Terrain and sprites render via a
CAMetalLayer-backedMTKViewfloating behind the scroll
view, live camera tracking, pixel-exact parity with the CPU path (offscreen pixel-diff harness
covers both). - Fixed a real live resize/scale bug found on first evaluation: the overlay's
MTKViewwas
sized off the whole scroll view (HUD chrome included) instead of the inset-excluded visible
region — terrain and sprites disagreed on scale as the window resized, worse on a narrow
window. Fixed by sizing off the clip view's frame instead, with explicit resize/scroll
observers. - Switched the overlay from a continuous ~60 Hz draw loop to on-demand rendering (redraw only on
camera movement or an actual tile-grid change) — the continuous loop was competing with the
network/tick consumer for the same thread, the root cause of a guest-side rendering-adjacent
lag report. - Raised the tile-count budget for the Metal path (effectively uncapped, matching the original
game, which never had this ceiling); the CPU fallback keeps its original, measured budget. - Live-confirmed clean at every window size, zoom level, and during active pan/resize; no
tile-count ceiling visible at max zoom-out.
Guest desync fixes (found live, unrelated to #25):
- Pillbox pickup, build, and repair now sync to the guest — previously the guest's own client
never learned a pill's ownership/armour had changed, matching "Mac B ran over the pillbox
according to the host screen; Mac B doesn't think it was collected." - Base capture (neutral and hostile takeover) now syncs — including the capture-war/regen
mechanic: a base under 5 armour is freely re-capturable by anyone, matching the original
game's own collision rule exactly, so a freshly-captured base can be immediately retaken until
it regenerates. - Combat damage to pill/base armour, and base refuel depletion, now sync — previously a guest
watching a base they'd shot would see it silently regenerate with no visibility into its real
armour. - A builder's walked-to-completion pill build/repair now syncs (the instant click-command path
already worked).
Networking (found and fixed alongside the render work):
- Guest-side TCP connection now sets
TCP_NODELAY, matching the host's own listener — the
guest's outbound actions (mine placement, etc.) were exposed to Nagle-algorithm coalescing
delays the host side never had. - Guest-side
TCPSessionmoved off the main dispatch queue, removing contention with the
render/tick loop.
Known limitations
- Intermittent black seam lines in the live Metal terrain overlay, sea-only, hard to reproduce
(#137) — cosmetic, not a blocker, tracked for v1.6.1. - Builder movement after death can look unrouted before snapping to a direct line (#138) — seed
issue from a live observation, not yet root-caused. - A host-engine crash signature (SIGABRT, data-race shape in
GameState.localmutation during
tankLocalTick) was found via a crash log from an older (v1.5.1) build left running
overnight (#139) — not yet reproduced live or confirmed against this build; flagged for
investigation, not blocking this release. - Whether a host-placed pillbox is visible to a guest (the reverse direction of the
guest-builds-a-pillbox case, which is confirmed working) has not been directly tested.
v1.5.1 — Live play fixes
v1.5.1 — Live play fixes
Tagged 2026-09-22.
Summary
Two-player LAN play with Hidden Mines works end to end on two Macs: the guest sees the map, the host's
name and tank, and can fire, lay mines and form alliances. The host now simulates the guest's tank, shells and
mines, so combat is host-authoritative. A guest that leaves and rejoins is the one known break; see below.
Every tracked v1.5.1 issue is closed, its code is merged to main, and every live-play fix in this release
(#79, #105, #107, #114) has been confirmed on real two-Mac hardware, build v151-test6 496999e.
Fixed since v1.5.0
- Guest map no longer renders all black with Hidden Mines on (#75); host-laid mines and host builder edits reach
guests (#76, #84); host name shows on the guest (#85). - Guest alliances work from both sides (#92); the guest sees the host's respawn at its new position (#61).
- Host simulates guest tanks (#59/#62): firing, shells, explosions, mine triggers and a host-owned mine count.
- Hidden Mines no longer announces a remote-laid mine at range (#106).
- Host draws never-seen tiles as plain sea, matching the guest, instead of black.
- A remote tank's name label now fades and hides with distance instead of always showing (#79, live-confirmed 2026-09-22).
- The host quitting to the menu now shows the guest a disconnected message instead of leaving its session hanging (#107, live-confirmed 2026-09-22).
- A guest's key-down mine no longer detonates under its own tank when the drop lands on the tile it already occupies (#105, PR #121, live-confirmed 2026-09-22 driving and continuously laying mines as the guest).
- Shells no longer vanish for their whole flight at roughly 10 of 16 firing headings (#114, PR #123, live-confirmed 2026-09-22 firing stationary, moving, rotating in place, and moving+firing) — the sprite sheet only had directional art for 6 of the 16 headings
headingColumncan produce; the other 10 rendered a transparent cell for the shot's entire flight.
Known limitations
- Leaving and rejoining a hosted game breaks the rejoined guest (#113, fix planned for v1.5.2). After a guest
quits to the menu and rejoins, its screen does not refresh and its tank stops responding — pinned down
2026-09-22 to losing control once it reaches the map border, not a fixed distance from spawn. A rejoin under
a new player name does not avoid this: it renders the map correctly but spawns the tank at wherever the
previous session was frozen, not a fresh spawn point (corrects this note's earlier claim that a new name
"worked in testing"). Everything else in a two-player game was fine in testing as long as nobody left. - The guest is not fogged for tanks or pillboxes/bases: it can see the host tank and other structures outside its own
vision (#86/#90, closed as not planned for this release). - A first join can fail once and succeed on the second try, and a real Mac joining a VM host was refused once (#93).
- Breaking an alliance removes the ally's tank sprite but does not re-fog the terrain that alliance had revealed —
the map stays exactly as it was while allied (#120, found 2026-09-22, tracked and the focus of v1.5.2). - A builder-laid mine may not render as visible to the host until the tank moves (a render-refresh gap, #77) —
closed 2026-09-22 per triage direction without a dedicated re-test; flag if it resurfaces. - Pill/base capture, build and deploy are still not fog vision sources (#72).
v1.5.0 — Hidden-mines fog
Hidden-mines fog-of-war (#1), opt-in; fully visible remains the default. Host-authoritative fog with wire redaction of the initial map and terrain broadcasts (new SRRevealTerrain). Known gap: pill/base transitions are not yet vision sources. See docs/STATUS.md and docs/CONSTRAINTS.md.
v1.4.0 — Controls, HUD, and sound
Play-feel milestone: Game Controller, join-side lag tint, remaining near sounds, Observable HUD snapshot, App Intents, OSLog/signposts.
Closed issues
- #16 OSLog / OSSignposter intervals on tick, draw, and net send (PR #48)
- #23 Observable HUD snapshot (PR #49)
- #3 Join-side connection lag tint (PR #50)
- #22 App Intents for Host a Game / Join Last Host (PR #51)
- #17 Game Controller mapping to InputFlags (PR #53)
- #8 Remaining procedural sounds (PR #54)
Tests
824/824 SwiftPM (230 BoloKitTests + 594 DifferentialTests) + 86/86 Bolo 2026Tests pass. One pre-existing flaky timing test is documented; isolated rerun is the check.
v1.3.0 — Find and share games
LAN/WAN discovery without a paid Apple Developer Program: bolo:// join URLs, host tracker announce + UPnP port mapping, Quick Look map previews.
Closed issues
- #20
bolo://join URLs (PR #47) - #26 Quick Look map previews (PR #47)
- #24 Host tracker announce + UPnP port mapping (PR #55)
Deferred
#14 (Bonjour), #21 (AWDL), and #6 (dedicated headless host) all require binding a Network.framework listener locally to write or verify, and are blocked on the same NWListener EINVAL environment issue. Moved to the new v1.8.0 LAN/WAN discovery (env-blocked) milestone rather than holding this release open indefinitely.
Tests
826/826 SwiftPM (232 BoloKitTests + 594 DifferentialTests) + 107/107 Bolo 2026Tests pass.