Skip to content

Overnight/test harness 2026 04 20#4

Merged
claudenstein merged 58 commits into
mainfrom
overnight/test-harness-2026-04-20
Apr 26, 2026
Merged

Overnight/test harness 2026 04 20#4
claudenstein merged 58 commits into
mainfrom
overnight/test-harness-2026-04-20

Conversation

@claudenstein

Copy link
Copy Markdown
Owner

No description provided.

Frank Dosk and others added 30 commits April 25, 2026 15:51
Existing tests hit individual API endpoints but never the root
"/" path that serves the embedded index.html. The HandleFunc
registration alone doesn't trip Go coverage; only invoking the
closure does. Add a test that GETs / and asserts a 200 with an
HTML body. Server.Start 93% → 95.3%; package 97.7% → 97.9%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The pre-existing TestEncodeDecodeRoundTrip and
TestEncodeRejectsOversized both populate v.Hits before
encoding, so the `if v.Hits == nil { v.Hits = []KeywordHit{} }`
default-fill arm was never exercised. Pass a fully-zero
KeywordValue and verify the round trip works. EncodeValue 80%
→ 90%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Existing happy-path / non-200 / bad-URL tests covered most of
httpGetClient.Get, but the `resp, err := g.c.Do(req)` transport-
error arm stayed uncovered. Pre-cancel the context so http.Do
returns context.Canceled immediately, exercising the wrapped
return without depending on actual network state.
Get 90% → 100%; package 96.4% → 96.7%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
All pre-existing tests passed an explicit fakeHTTPSClient, so
the `if client == nil { client = NewHTTPSFallbackClient(0) }`
arm stayed uncovered. New test passes nil + a pre-canceled
context so the substituted real client errors out immediately
without making a network call. FallbackToHTTPS 97.1% → 100%;
package 96.7% → 97.0%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The pre-existing tests covered the bad-pubkey early return and
the happy-path round trip, but the `if err != nil` arm after
MineRecordPoW was untested. Pass bits=41 so MineRecordPoW's
"cost prohibitive" guard fires; verify the wrapped error
surfaces and the Sig stays zero (signing skipped).
SignAndMineRecord 88.9% → 100%; package 93.5% → 93.7%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The pre-existing tampered-leaf test triggers either the
DecodeLeaf error path or the fingerprint-bytes mismatch — but
the explicit `count != trailer.NumRecords` guard runs first
and could mask either of those depending on the tampered byte.
Add a test that inflates only the in-memory trailer's
NumRecords claim (post-OpenBTree, no leaf mutation) so the
fingerprint hash still matches — leaving the count guard as
the only catch. VerifyFingerprint 81.5% → 85.2%; package
93.7% → 93.8%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Build a tree whose records carry tiny nonces (0..9) that don't
satisfy any meaningful PoW threshold, then post-OpenBTree set
the in-memory trailer's MinPoWBits to 20. Find must silently
drop every record via VerifyRecordPoW's continue arm and
return zero hits — verifies that the spam-defence threshold is
actually enforced at query time, not just at trailer-decode.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Build a tree where one record's signature is corrupted before
BuildBTree (the trailer signs the resulting fingerprint, so
OpenBTree still succeeds). Find must drop the bad-sig record
via the VerifyRecordSig continue arm rather than surface a
bogus hit. Find 80.6% → 83.9%; package 93.8% → 93.9%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pass an opts.Root that doesn't exist so the inner CreateTorrent
fails on os.Stat; the wrapping CreateTorrentFile must surface
the error without writing any file. Closes the propagation arm
no other test reached. CreateTorrentFile 88.9% → 94.4%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Plant a 0o000 subdirectory under opts.Root so the pre-walk
size pass's filepath.WalkDir receives a permission-denied
ReadDir error. CreateTorrent must surface "stat tree:" without
proceeding to BuildFromFilePath. Skipped when running as root.
CreateTorrent 87.8% → 92.7%; engine package 84.1% → 84.4%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pre-cancel the context so getput.Get aborts immediately
without contacting any peers, exercising the
"dhtindex: get pointer ... %w" error wrap that no existing
test reached. GetInfohashPointer 80% → 86.7%; package 94.4% →
94.7%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Apply the canceled-context pattern that closed
GetInfohashPointer's traversal-error arm to every other
DHT-bound entry point: PutInfohashPointer, AnacrolixPutter.Put,
GetPPMI, and PutPPMI. Each of these wraps a getput.Put or
getput.Get call whose error path was unreachable in CI without
a real network. Pre-canceling the context aborts the traversal
deterministically.

Coverage shifts:
  - PutInfohashPointer: 85.0% → 90.0%
  - Put:                80.0% → 86.7%
  - GetPPMI:            80.0% → 100%
  - PutPPMI:            82.4% → 88.2%
Package coverage: 94.7% → 95.1%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The function comment promises that passing reply=nil lets the
responder run through search without trying to send back a
result ("decode-only mode used in unit tests"), but no test
exercised that path. Add a test that drives a successful query
with nil reply and asserts the call completes without panic.
handleQuery 82.9% → 85.4%; package 96.1% → 96.2%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pass a reply closure that returns an error and verify
handleQuery completes without panic — the function debug-logs
the send failure and returns rather than retrying. handleQuery
85.4% → 90.2%; package 96.2% → 96.4%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…y_err

The pre-existing test passed an empty Records slice, which makes
ApplyRecords succeed (no records to validate) and silently
returns through the success path — never exercising the
apply_err arm the comment claims. Replace with a record whose
Pk is 5 bytes instead of 32 so ApplyRecords' "bad sizes" check
fires and onSyncRecords takes the err return. onSyncRecords
84.6% → 100%; package 96.4% → 96.5%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a small pieceErrSource / leafFailSource pair of test
fakes that wrap the real BytesPageSource and synthetically
fail one piece. After OpenBTree completes (which only reads
the trailer), swap the reader's source for the wrapper so
Find's piece-fetch error arms fire deterministically:
  - TestFindRootPieceFetchError: piece 0 fetch fails
  - TestFindLeafPieceFetchError: a mid-walk leaf piece fails

Find 83.9% → 87.1%; package 93.9% → 94.0%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
walkToLeaves and Find both fetch the same leaf piece; the
existing TestFindLeafDecodeError corrupts the leaf bytes
before any read so the walk's decodeHeader trips first and
Find's outer DecodeLeaf arm stays uncovered. Same problem
applied to TestFindLeafPieceFetchError this iteration —
use a stateful source that returns the real bytes on the
first call (walkToLeaves) and garbage on the second
(Find's re-fetch). Find 87.1% → 93.5%; package 94.0% → 94.2%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two more fakes in the source-stub family — constPieceSource
returns custom bytes for piece 0 — let the remaining root-
validation arms in Find fire deterministically:
  - TestFindRootHeaderDecodeError: piece 0 is zero-bytes so
    decodeHeader's magic check fails.
  - TestFindRootKindNotRoot: piece 0 is a syntactically valid
    leaf-kind page so decodeHeader succeeds but the kind check
    rejects.

Find 93.5% → 100%; package 94.2% → 94.4%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Submit a .txt file whose OpenReader returns an empty stream;
the plaintext extractor produces no chunks, so handle takes
the `len(chunks) == 0 → skipped++ + return` branch without
calling IndexContent. handle 82.4% → 88.2%; package 94.2% →
94.7%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Build a tree, then substitute piece 1 (the leaf) with a valid
trailer-kind page so the recursive walkToLeaves call decodes
the header successfully but the kind check rejects since
trailer is neither Interior nor Root nor Leaf. walkToLeaves
85.7% → 96.8%; package 94.4% → 94.5%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Close the index between Start and Submit so every IndexContent
call returns "indexer: closed". Pipeline.handle still
extracts text from the input but every chunk's index attempt
fails, taking the `writeErrors == len(chunks) → Failed++`
counter arm rather than `Extracted++`. handle 88.2% → 100%;
package 94.7% → 95.7%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pre-plant a non-empty directory at companion.torrent.tmp so
the second atomicWrite call (after the JSON payload writes
successfully) fails on truncate-open. WriteCompanionFiles
80% → 85%; package 94.5% → 94.6%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Set EndorsementThreshold=10 so countStrongEndorsers with one
endorser falls short, forcing the policy fallback. With bloom
+ tracker wired, the default unknown score of 0.5 clears the
0.3 threshold check inside bloomPolicy and admit fires via
the "endorsed-bloom" label. IngestEndorsement 94.4% → 100%;
package 97.0% → 97.3%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A single indexer returns two hits for the same infohash where
the first has empty Name and the second has a non-empty Name.
The merge loop processes hits in slice order, so the second
hit's name deterministically fills the initially-empty merged
entry — exercises the
`if lh.Name == "" && h.N != "" { lh.Name = h.N }` arm without
relying on cross-indexer goroutine ordering. legacyQuery
92.2% → 93.8%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extend the existing same-indexer two-hit test so the second
hit also carries non-zero Size and Files, exercising the
matching `lh.Size == 0 && h.Sz > 0` and
`lh.Files == 0 && h.F > 0` fill arms that no other test
reached. legacyQuery 93.8% → 96.9%; package 95.1% → 95.4%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A malformed indexer can publish a KeywordHit whose IH is not
exactly 20 bytes (the field is a plain []byte with no wire-
level length validation). The merge must skip such hits via
the `if len(ih) != 40 { continue }` guard. Add a test that
mixes a 19-byte IH with a valid 20-byte IH and asserts only
the latter survives. legacyQuery 96.9% → 98.4%; package 95.4%
→ 95.5%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NewTracker() returns an in-memory tracker with empty path;
Save's `if t.path == "" { return nil }` early-return arm let
callers construct ephemeral trackers for tests / experiments
without needing a backing file. No existing test exercised
this path. Save 85.7% → 92.9%; package 95.0% → 95.4%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two new tests use a counting failingWriter that succeeds for
the first N bytes then errors out:
  - TestWriteBloomHeaderWriteError fails at byte 0 so the
    header write returns immediately.
  - TestWriteBloomBitsWriteError accepts the 24-byte header
    then fails on the first bits-buffer write.

writeBloom 81.2% → 93.8%; package 95.4% → 96.0%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two new tests use hand-crafted bloom file headers:
  - TestReadBloomBitsLenInconsistent declares bitsLen huge
    relative to m so the size-consistency guard fires before
    a billion-entry slice would be allocated.
  - TestReadBloomBitsReadError truncates the body mid-bits
    so io.ReadFull returns unexpected EOF on the second
    iteration of the bits loop.

readBloom 90% → 100%; package 96.0% → 96.6%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Saturate every bit in a small filter (set bits to all-ones)
so popcount equals m and the `if x >= m { return Inf }`
guard fires. Without this test the +Inf saturation path of
the cardinality estimator was unexercised. EstimatedItems
90.9% → 100%; package 96.6% → 96.9%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Frank Dosk and others added 27 commits April 26, 2026 08:55
Pass a 100-byte pieceSize so even a normally-sized record
(~270 bytes encoded) overflows the page on its own. packLeaves
must surface the "record too large" error rather than
silently truncating. packLeaves 85.7% → 95.2%; package 94.7%
→ 94.9%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A record with Kw at the 64-byte keyword limit plus max-width
Pow (uint64-max → 20 digits) and T (int64-max → 19 digits)
encodes to ~258 bytes once you sum the bencoded fixed fields
(pk:38, ih:26, sig:70 etc.) — just over the 256-byte
MaxRecordBytes ceiling. Verifies that the post-marshal size
guard catches such records before they reach the leaf
encoder. EncodeRecord 81.8% → 90.9%; package 94.9% → 95.0%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three new tests round out the VerifyFingerprint error paths:
  - TestVerifyFingerprintFetchError: piece-fetch fails
    mid-walk via the pieceErrSource wrapper.
  - TestVerifyFingerprintHeaderDecodeError: piece 1 returns
    zero-bytes via constPieceSource so decodeHeader rejects
    on bad magic.
  - TestVerifyFingerprintHashMismatch: tamper with the
    in-memory trailer's TreeFingerprint after OpenBTree so
    the reconstructed hash and the claim diverge — count
    guard passes, fingerprint comparison catches.

VerifyFingerprint 85.2% → 96.3%; package 95.0% → 95.3%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two new tests round out ProduceSymbols:
  - TestProduceSymbolsWrongPhase calls in PhaseIdle (fresh
    session, no Begin called) so the phase guard fires.
  - TestProduceSymbolsCountClamping exercises both
    count <= 0 (falls back to MaxSymbolsPerMessage default)
    and count > MaxSymbolsPerMessage (capped).

ProduceSymbols 86.4% → 100%; package 96.4% → 96.7%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
  - TestApplyNeedTxIDMismatch passes a SyncNeed whose TxID
    diverges from the session's so the txid guard fires.
  - TestApplyNeedTooManyIDs passes IDs of length
    MaxNeedIDsPerMessage+1 so the per-message cap rejects.

ApplyNeed 86.7% → 100%; package 96.7% → 96.9%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bencode-marshal a payload that round-trips into a valid
SyncBegin struct but carries MsgTypeSyncEnd as msg_type so
the discriminator-mismatch guard fires. DecodeSyncBegin
87.5% → 100%; package 96.9% → 97.0%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EncodeSyncRecords enforces the same MaxRecordsPerMessage cap
as DecodeSyncRecords, so the round-trip path can't exercise
the decoder-side guard. Bencode-marshal the struct directly to
bypass the publisher-side check, then verify DecodeSyncRecords
catches a 501-record payload. DecodeSyncRecords 88.9% → 100%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EncodeSyncNeed substitutes an empty slice for a nil IDs slice
before bencoding so the wire form always has an explicit
(possibly empty) list rather than nothing. No existing test
exercised that fall-through. Round trip a nil-IDs SyncNeed
and verify the encoder + decoder both succeed. EncodeSyncNeed
88.9% → 100%; package 97.0% → 97.1%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drive the session past Begin so the phase gate accepts, then
pass MaxNeedIDsPerMessage+1 ids to trip the cap. NeedFrame
92.3% → 100%; package 97.1% → 97.2%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Plug in a sender that errors every Send call so the fan-out
loop counts every error and asked stays at 0. Query must
return an empty response without waiting for replies. Query
92.5% → 98.1%; package 97.2% → 97.3%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EncodePeerAnnounce rejects oversized endorsement lists at
publish time, so a round-trip test can only exercise the
at-cap case. Hand-marshal a peer_announce payload with
MaxEndorsedPerAnnounce+5 entries to bypass that check, then
verify the decoder trims to the cap rather than blowing out
memory or surfacing an error. DecodePeerAnnounce 92.3% → 100%;
package 97.3% → 97.4%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous commit accidentally added .claude/scheduled_tasks.lock
which is local workspace state. Untrack it and add .claude/ to
.gitignore so future Claude Code sessions don't pick it up either.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two new tests:
  - TestDecodeSyncSymbolsBadDataXORLen hand-marshals a frame
    whose first symbol carries a 5-byte DataXOR (must be 32);
    the post-decode validation loop rejects.
  - TestEncodeSyncRecordsNilRecordsDefault passes a nil
    Records slice through Encode + Decode to exercise the
    "substitute empty slice" default-fill arm.

DecodeSyncSymbols 92.3% → 100%; EncodeSyncRecords 92.3% →
100%; package 97.4% → 97.6%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Existing tests covered every decode-error arm but not the
happy-path forwards for SyncRecords and SyncEnd. Add direct
tests that pass valid frames from a capability-marked peer
and assert no misbehavior is charged. handleSyncFrame 94.1%
→ 100%; package 97.6% → 97.8%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dhtindex.Lookup.Query rejects queries that tokenise to
nothing. Pass a stopword-only query "the of" — Bleve's local
search handles it (returns no hits, no error) but Tokenize
returns empty and Lookup.Query surfaces "no tokens".
handleSearch must still respond 200 with the error string in
the dht.error field. handleSearch 87.0% → 88.9%; package
97.9% → 98.2%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wire a fixedHitGetter that returns a populated KeywordValue
and register an indexer on the Lookup. Lookup.Query goes
through its happy path with one hit, and handleSearch's
`for _, h := range out.Hits { dhtResp.Hits = append(...) }`
arm fires. handleSearch 88.9% → 90.7%; package 98.2% → 98.4%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A session manifest entry with QueueOrder higher than the
engine's nextQueueOrder counter must lift that counter so
torrents added after restore get fresh slots above the
restored set. restoreEntry 89.8% → 91.8%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drive the session past Begin so the phase guard accepts, then
SetBudgets(2, 1024) and feed a 3-symbol frame so the
`symbolsIn + len(Symbols) > maxSymbols` check fires.
ApplySymbols 94.1% → 100%; package 97.7% → 97.8%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wire-level decoders (ApplyRecords + DecodeSyncRecords) already
filter out records with wrong field lengths, but
ingestSyncRecords is also called from cache-restore-style
flows that may not go through the wire path. Bypass the wire
layer by calling ingestSyncRecords directly with a 5-byte Pk
so the per-record skip arm fires; sink + observer must both
remain empty. ingestSyncRecords 95.5% → 100%; package 97.8%
→ 97.9%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When a node has Capabilities.Publisher > 0 and a 32-byte
publisher pubkey set via SetPublisherPubkey, the announce it
sends after a peer handshake must:
  - flip BitLayerDPublisher in the services bitmap, and
  - carry the 32-byte pubkey in pa.Pubkey.

A recordingHandshakeSender catches the goroutine-emitted
PeerAnnounce; the test asserts both arms fire.
OnRemoteHandshake 94.6% → 100%; package 97.9% → 98.1%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pass a record with an empty keyword so the per-record
EncodeRecord call inside EncodeLeaf's loop returns an error;
EncodeLeaf must surface it rather than producing a leaf with a
half-written payload. EncodeLeaf 88% → 92%; package 95.3%
→ 95.4%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A magnet handle has no metadata until GotInfo fires; with DHT
disabled and no peers, that never happens, so sampleRate must
take the early return rather than call t.Stats() on a half-
initialized torrent. sampleRate 90% → 93.3%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Close the engine then call restoreEntry directly (inside-
package test) so the `if e.closed { return error }` guard
fires before any torrent.Client state is touched.
restoreEntry 91.8% → 95.9%; engine package 84.5% → 84.8%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two new tests:
  - TestLoadSessionMkdirFails plants a regular file at
    <dataDir>/torrents so MkdirAll can't make it a directory.
  - TestLoadSessionReadFileFails plants a directory at
    session.json so os.ReadFile fails with EISDIR (a non-
    ErrNotExist error that the existing NoFileYetReturnsEmpty
    test deliberately doesn't cover).

loadSession 90.9% → 100%; engine package 84.5% → 84.9%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Construct an engine with BloomPath and ReputationPath set so
the spam-resistance subsystems are wired in. Close then runs
the `if e.bloom != nil { e.bloom.Save() }` and matching
tracker save arms; verify both files exist on disk afterwards.
Engine.Close 80% → 84%; package 84.9% → 85.4%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two new tests:
  - TestDecodeTrailerBadKind: hand-build a leaf-kind page and
    pass it to DecodeTrailer; the kind guard rejects.
  - TestDecodeTrailerWrongPayloadLength: build a trailer-kind
    page whose header claims a 32-byte payload (not the fixed
    TrailerPayloadSize); the size guard rejects before any
    body byte is read.

DecodeTrailer 93.8% → 100%; package 95.4% → 95.7%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pass a record with empty keyword so packLeaves rejects;
BuildBTree must propagate the error rather than continue with
a half-built tree. BuildBTree 93.4% → 94.5%; package 95.7%
→ 95.8%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@claudenstein
claudenstein merged commit b648c4f into main Apr 26, 2026
1 check failed
claudenstein pushed a commit that referenced this pull request Jul 19, 2026
Closes the round-4 adversarial backlog; each fix has a regression test that
fails when the fix is neutralized. All touched packages race-clean.

- #10 companion: put the new BEP-46 pointer BEFORE dropping the old seed, so a
  failed put keeps the still-advertised infohash fetchable.
- #7 dhtindex: TOCTOU between off-lock aggregate Refresh and a concurrent
  Retract — added a gen counter + post-distribute re-check so a retracted tree
  isn't left published for a full refresh interval.
- #9 indexer/httpapi/daemon: a malformed query string is now a typed
  ErrBadQuery → HTTP 400 (client fault), not 500; classified at the daemon
  seam via a LocalBadRequest flag (httpapi imports no indexer types).
- #5 companion: publisher GeneratedAt is now strictly monotonic across content
  changes, so a backward wall-clock step no longer trips the follower's replay
  guard and drops legitimate new content.
- #11 companion: an Unfollow racing an in-flight Sync no longer resurrects the
  dedup maps — per-publisher Forget epoch, re-checked under lock in commitDedup.
- #4 engine: persistAdd runs after Add releases e.mu; a racing RemoveTorrent
  could be resurrected — updateGuarded(abort) + Handle.isRemoved() make the
  create happens-before-correct (RemoveTorrent closes h.removed before its
  lock-guarded remove); persistSeedAdd DataPath refinement → updateExisting.
- #8 swarmsearch: initiator/responder txid collision deferred with a fix sketch
  (needs a wire direction bit + the unwired StartSync path) — see DECISIONS T3-8.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claudenstein pushed a commit that referenced this pull request Jul 19, 2026
First 0-HIGH round since the GUI surface opened. The r8-regression and
html-xml-walker finders returned zero. Three of four fixes are neutralize-to-
fail pinned; the LOW #4 fix is structurally verified (see below).

- MED (gui): the Status tab ran indexer.Stats() (full-corpus scan under the
  index lock) synchronously on the Fyne UI thread every 2s, freezing the whole
  GUI on a real corpus. Compute render() off the UI thread; only SetText on it.
- MED (swarmsearch): handlePeerAnnounce (async worker) unconditionally created a
  p.peers entry, so a frame processed after the synchronous OnPeerClosed
  resurrected a zombie -> unbounded, remote-triggerable memory leak (no reaper).
  Update-only now: NotePeerAdded already creates the entry synchronously before
  any message, so a missing entry means the peer is gone -> skip.
- LOW (gui): the Search tab gated rendering on the LIVE checkbox, so toggling
  Swarm/DHT off mid-search dropped already-fetched hits. Gate on res.Swarm/res.DHT
  (what was queried; nil when not).
- LOW (engine): SetFilePriority on a queued torrent could download outside the
  max-active-downloads cap (queued torrents were gated only by None priorities).
  Add the anacrolix-level gate pause uses: DisallowDataDownload on queue,
  AllowDataDownload on promotion. Structurally verified (the flag is unexported;
  the full queue/promote/pause suite passes with no regression).

Convergence: r2=2 HIGH, r3-4=0, r5=1, r6=0, r7=1, r8=1, r9=0. Strongest signal
yet — GUI + follow-ups swept, shared-walker + r8-regression dry. See DECISIONS.

Co-Authored-By: Claude Opus 4.8 (1M context) <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.

1 participant