Skip to content

v1.3.208

Choose a tag to compare

@topcheer topcheer released this 16 Aug 13:10
· 1031 commits to main since this release

ggcode v1.3.208

Highlights

  • See the changelog below for the user-facing updates included in this release.

Compare

Changelog

  • test(#534): observe BugB epoch stamping at the send side — fix CI flake
  • release: v1.3.208
  • fix(#545): tag_balance HTML5 optional-end-tag implicit closing
  • fix(#543,#544): session token budget no-op wired + three detector FP fixes
  • fix(#542): tool batch — worktree ownership validation, relative-path WorkingDir, whitespace required-param
  • fix(#541): TUI batch — /retry //edit producer restored, /undo-run guard, change_summary accuracy, /clear cycle fix, wheel routing
  • fix(#540): IM adapter batch — dedup rollback, Slack mention strip, Feishu retry, DingTalk reset, TG entity strip
  • fix(#536): npm prerelease exclusion rules for range constraints
  • fix(#539): relay server batch — detach guard, authority check, non-blocking sends, onAck race
  • fix(#538): delimiter regex-literal false positives + RenameSession windowed no-op
  • fix(#537): base-package batch — phantom diff lines, hunk headers, CJK truncation, ring filter, pipe stdin
  • fix(#536): pipe-mode stderr visibility + SearchSessions global sort (semver A/B in 143e8eb)
  • fix(#535): context batch — block-level stale, script-tier tokenizer, aligned estimates
  • fix(#534): tunnel batch — AuthorityEpoch stamping, async server recovery, pendingFront drain, no false-ack
  • fix(#536): SemVer prerelease ranking + caret/tilde range constraints
  • fix(#533): printf_format delta/star-width/literal-concat + nil_deref value-guard/fallback
  • fix(#532): SetEndpointLimits/SetModelLimits instance guard — no more garbage-dir writes
  • fix(#531): session store residues — unlock, O_TRUNC TOCTOU, 24h window boundary
  • fix(#530): desktop app.go batch — DeleteSession nil panic, StartShare leak, RespondAskUser hang
  • fix(#528,#529): errclass window-limit classification + cost billing-basis batch
  • fix(#522): bump runGeneration at every run start + SendHiddenText turn reset
  • fix(#521,#526,#527): hot-reload per-path watermark + detector FP/FN batch
  • fix(#518,#520): 422-string retry exclusion + PID-file/error/truncation hygiene
  • fix(#524,#525): instance-scope persistence both sides + permission downgrade consistency
  • fix(#523): MCP client concurrency — WS waiter routing + HTTP lock narrowing
  • fix(#517,#519): checkpoint UndoRun run-integrity + cron debounce busy-spin
  • fix(session): loadSession extraStart indexed postCPEntries but sliced allMessages — panic on long/cost-record sessions
  • test(#516): add registration-guard test for hardcoded-path — the detector spent multiple census rounds as unregistered dead code precisely because nothing asserted its registration; TestCheckWriteIntegrity_HardcodedPathRegistered goes through the public checkWriteIntegrity entry (old/new delta with a newly introduced /Users/... path must warn 'macOS home path'), guarding against silent deregistration in future registry refactors. Companion to 0713a6f.
  • test(#513): add persistent characterization tests for the two probe-verified bugs the issue's temporary probes covered — secondsToDuration clamp (overflow table: negative/zero fallback, 9223372037s negative wrap, 18446744074s positive wrap, 99999999999s all clamped to 86400s) and snapshot TotalLines partial-line exclusion (end-to-end via CommandJobManager: partial chunk visible as preview but TotalLines stays 1, merged chunk increments to 2, since_line=1 polling returns the merged complete line instead of the old permanent []). The Bug3 ctx test was already committed inverted in 8b409dc; wailskit chat.go fixes are covered by the package's existing regression suite (no prior assertion encoded the buggy behavior).
  • fix(#516): delete 4 dead detectors, register hardcoded-path live — R73 census batch, sa-172 probe-verified (12/13 hypotheses PASS, temp probe files removed). DEAD DELETES (10th census instance of the #328/#330/#499/#503-#510 class): duplicate_code_check.go — token-frequency Jaccard is order-blind: two functions with identical token multisets but opposite execution order report "100% similar / structurally identical bodies" (its own L124-129 concedes the frequency set can be 1.0 while order differs); header comment says min 5 stmts but const duplicateMinStmts = 3. hardcoded_host_check.go — semantic inversion across Go/JS/Python paths: host:'localhost' and 127.0.0.1 flagged as "binding to unintended interfaces" when loopback is the MOST conservative binding and the stated risk is 0.0.0.0. http_timeout_check.go — doubly broken: set-delta keys (default-client-func:http.Get) are non-multiset so delete-2-add-1-new swallows brand-new violations (0 warnings where fresh-file identical content warns twice; #171 class), AND it only inspects composite-literal fields so c := &http.Client{}; c.Timeout = 30*time.Second still warns "created without a Timeout field". loop_perf_check.go — triple-broken: double-counts nested-loop += (same line recorded twice), misses string-typed function params entirely (identifyStringVars has no param case), and stringVars is file-global-by-name so a string s in func A poisons an int s in func B. LIVE REGISTER: hardcoded-path (checkHardcodedPaths) — the only zero-known-FP/FN survivor of the sa-172 batch (already implements the #186/#171 per-instance multiset delta; .go home-path fires, .md stays silent), following the #508 empty-error-body revival precedent. DEPENDENCY DISPOSITION (#509 fate-binding precedent): identifyStringVars/isStringExpr + 3 collect helpers migrated from loop_perf_check.go to string_efficiency_check.go (itself unregistered dead code — must be re-adjudicated together when string_efficiency reaches census); qualifiedCallName stays in exit_call_check.go (multi-file shared, unaffected). Tombstone with per-detector revival preconditions recorded in write_integrity.go registry. All file-private symbols verified zero-external-reference before deletion. Full repo build + internal/agent package regression green (15.1s).
  • fix(#513,#514,#515): three verified bug batches — internal/tool, wailskit chat bridge, CJK tokenizer
  • test(agent): drop stale mcpRuntime entry from nil-fields allowlist
  • chore(agent): remove dead mcpRuntime wiring left by ecosystem-detector deletion
  • fix(#512): narrow live checkDebugStmts to unambiguous debug signals; delete i18n + time_format dead detectors — R71 census batch, sa-171 probe-verified (3/3 in-package repro tests, all passed, temp files removed). LIVE FIX: checkDebugStmts (called at agent_tool.go:551/729 on every source write) flagged languages' ONLY stdout primitives as debug leftovers and coached "Remove them before completing the task" — Python print (sa-171 A1: a normal CLI with 2 prints warned "2 x print() (Python)"; a 10-print script would report "10 x"), C printf (CLI usage output), Java System.out, Swift/Dart print, Rust println!, Ruby puts/p (which also warned on the comment # p(x)). Normal stdout and debug prints are token-indistinguishable, so the pattern table is narrowed to unambiguous debug signals: Go fmt.Print + builtin println (strong log convention; A5-verified TP), JS console.* + debugger, Python breakpoint/pprint, Rust dbg!, PHP var_dump/print_r/dump, Swift debugPrint/dump; removed: Python print, C/C++, Java/Kotlin, Swift/Dart print, Ruby, Rust println-family. Four tests asserting the old FPs rewritten as exemption assertions; all kept TPs verified still firing. DEAD DELETES (9th census instance, write_integrity.go zero refs): i18n_check.go — signature discards oldContent (identical rewrite re-reports, #509 a11y zero-delta class), warns "locale-sensitive" on ISO-8601 "YYYY-MM-DD" literals (locale-independent BY DESIGN), and its Go advice ("use locale-aware formatting" for t.Format("2006-01-02")) is unimplementable — Go stdlib has no locale-aware date API, and log/wire formats SHOULD be locale-stable. time_format_check.go — scalar delta (len(old)>=len(new)→nil) broken both directions: fix-one-add-one suppresses a NEW wrong layout (the #510 ctxkey class) while additions re-report pre-existing ones; extractTimeMethodName matches ANY .Parse receiver so strptime.Parse("%Y-%m-%d") (correct for that lib) gets "fixed" into Go tokens, breaking it. Its core signal (time.Parse("YYYY-MM-DD")→"2006-01-02") confirmed CORRECT — tombstone records it as the strongest partial-revival candidate (preconditions: per-instance multiset delta per #171 + receiver restriction via go/types or time. allowlist). All symbols verified zero-external-reference before deletion (incl. unquoteGoString). Full internal/agent package regression green (7.1s).
  • fix(#511): delete flaky_test + test_gaming dead detectors — R70 census batch, sa-170 probe-verified (12/12 in-package repro probes, all passed, temp files removed). Both were stripped by the fc5c4aa critical-only refactor and survived as fully-implemented+unit-tested dead code (8th instance of the #328/#330/#499/#503-#510 class), but revival is empirically FP/FN-confirmed. flaky-test-patterns: the regex path matches comments and string literals BY DESIGN (a comment "// time.Sleep removed, now using wg.Wait()" warns — coaching the agent for documenting the fix); the goroutine check's ±15-line proximity window has no scope awareness (wg.Wait() 16+ lines from the go stmt is invisible; probe: go@L9 + wg.Wait@L30 warned where 0 expected) and exprToString(FuncLit) returns "" so the warning literally reads "'go ' launched without WaitGroup"; isMapTypeExpr returns true for every *ast.Ident, so for _, tc := range tests over a []struct — THE table-driven idiom — warns on nearly every new table-driven test (probe: got[0] != tc.want over []struct warned as "range over map", the single largest FP source); its rand advice is self-contradictory (concedes math/rand/v2 stays non-deterministic while still warning; rand.Seed is deprecated since Go 1.20 auto-seeding — following the advice reproduces the flakiness or breaks parallel-test isolation). test-gaming: TestFoo→TestFooV2 (body unchanged) is reported as "removed test function(s)... fix the code instead of deleting tests" — a cheating accusation on legitimate refactoring; "expect " with a trailing space counts any prose string as an assertion (deleting 3 prose lines reports "removed 3 assertion(s)"); and the scalar assertion count is hedged — delete 2 real assertions, add 2 trivially-true assert.Equal(t, 1, 1), zero warnings: the anti-spec-gaming detector is bypassable by spec gaming itself (#506/#507 scalar/set-delta class). Partial-revival candidates recorded in the tombstone: checkGoTestGaming's deleted-func signal is the strongest single detector (precondition: normalized-body similarity exemption so renames don't fire); assertion removal needs a line-level multiset delta; the goroutine check needs scope-aware sync lookup + FuncLit name fix. exprToString (lock_without_unlock_check.go, shared with printf_format_check.go) and isTestFile (debug_sniffer.go) survive in registered files. All 22 file-private symbols verified zero-external-reference before deletion. Full internal/agent package regression green.
  • fix(#510): delete error_msg_quality + error_sentinel + ctxkey dead detectors — R69 census batch, sa-169 probe-verified (3/3 in-package repro tests, all passed, temp files removed). All three were registered at birth, stripped by the fc5c4aa critical-only refactor, and survived as fully-implemented+unit-tested dead code (7th instance of the #328/#330/#499/#503/#506 class), but revival is empirically FP/FN-confirmed: error-msg-quality's delta key embeds fset.Position (line:col) — any insert above a pre-existing generic error message mismatches the key and re-reports it (the #507 error_swallow defect class, violating the #186 fingerprint convention; probe: L6→L7 shift produced 1 warning where 0 expected, keys-equal:false proven); ctxkey's line-number delta is broken in BOTH directions — a shift re-reports AND a brand-new violation (changed key literal "user"→"session-id") landing on an old line number is silently suppressed (FN: the promised write-time guard goes silent in the most common manual-rewrite shape; probes: shift=1 warning expected 0, new-violation-on-old-line=0 warnings expected ≥1); error-sentinel's single-letter "e" heuristic plus bare "Canceled" sentinel name fire on non-error entities (for _, e := range events { if e == Canceled } — e is an Event, Canceled a State constant) with errors.Is advice that does not compile (#506 float-equality class), and it has no _test.go exemption — test files are where legal sentinel comparisons concentrate (probe: foo_test.go warned, msg-quality's identical path correctly silent). Its content-fingerprint delta (sentinel-cmp:X op Y) WAS correct — recorded as the strongest partial-revival candidate (preconditions: drop the "e" heuristic or go/types, _test.go exemption, fresh zero-FP probes). Census also exposed the coverage gap: the two delta-broken detectors were exactly the two without line-shift regression tests. callExprName/unquoteString/unescapeQuotedString (#509 migration) went down with their sole consumer as their fate-binding note predicted; exprText survives in suspicious_comparison_check.go; TestErrorSentinel_LineShiftDoesNotRereport removed with its host (two live empty-error-body regression tests kept). All 24 file-private symbols verified zero-external-reference before deletion. Full internal/agent package regression green.
  • fix(#509): delete a11y + error_wrap dead detectors — R68 census batch, sa-168 probe-verified (8 in-package tests, all passed, temp files removed). a11y_check.go: registered at birth (dc64b24: LangMarkup+LangJSTS "accessibility"), stripped by fc5c4aa; discards oldContent entirely (ZERO delta — identical rewrite re-reports every pre-existing issue, competing for the single maxIntegrityWarnings=1 slot each write, violating the #186 fingerprint-multiset convention every registered peer follows), regex-scans .js/.ts string literals blindly (an a11y library's own sanitizer test fixtures produce warnings — no string-context awareness), and in JSX the "=>" inside arrow handlers terminates [^>]* before role/tabIndex/onKeyDown so ALL clickable divs warn including correctly-fixed ones (coaching agents into breaking correct accessible JSX); SVG non-match and alt=""/data-alt word-boundary behavior verified correct, but advisory class (WCAG advice) is exactly what fc5c4aa removed "to eliminate context pollution". error_wrap_check.go: never registered; Pattern 2 (%v→%w) empirically 3/4 FP — API-boundary intentional %v (avoiding errors.Is coupling), multi-error aggregation where %w permits exactly ONE wrapped error so the suggested fix has no valid form, and the e-name heuristic flags non-error string vars with advice that would not compile (go vet printf/staticcheck deliberately do not enforce %v→%w; wrapcheck is opt-in); delta is a bool-set keyed by format-string/file-wide constants — masks growth (2nd concat site: 0 warnings) and fix-one-introduce-one (silent FN); Pattern 1+3 (concat + errors.New(err.Error())) confirmed the zero-FP core, recorded as the reserved partial-revival candidate. Migrated callExprName/unquoteString/unescapeQuotedString into error_msg_quality_check.go (sole remaining consumer, itself unregistered census — header note ties helpers' fate to that file's disposition). Deleted a11y tests (26) + target_scatter_bug_test.go (all 5 tests are a11y regex/behavior probes) + error_wrap tests; all symbols verified zero-external-reference before deletion. Full internal/agent package regression green.
  • fix(#508): delete ignored_error + naked_return dead detectors; register empty_error_check as zero-FP "empty-error-body" — R67 census batch, sa-167 probe-verified (11 in-package tests, all passed, temp files removed). ignored_error_check.go: method-name fallback asserted "returns an error" on zero-return-value methods (conn.Write on func (c fakeConn) Write(p []byte) {} — factually wrong, deeper than the #111 always-nil case its own comment documents), flagged the _ = deliberate-discard syntax errcheck leaves off by default (coaching class, same as #505), and ALL three-segment keys (os.File., bufio., sql., http.Server., os/exec.Command.*) were unreachable in an untyped AST — f.Close() resolves to "f.Close" — while its own marquee header example json.NewEncoder(w).Encode resolved via constructorReturns to json.Encoder.Encode against the stored json.NewEncoder.Encode key: a double mismatch, so detection rested entirely on 30 generic method names; unfixable without go/types (why errcheck is built on types.Check) → delete + tombstone, revival precondition: go/types integration. naked_return_check.go: advisory class the fc5c4aa strip explicitly removed ("eliminate context pollution"); fires on the defer+recover named-result idiom (verified, 24-line func) at threshold 20 vs revive nakedret default 30; funcName-level delta masks same-function growth (2→3 naked returns, zero warnings, verified); FuncLit non-recursion confirmed correct/conservative → delete + tombstone, revival preconditions: min-lines>=30 + instance multiset delta + closure coverage. empty_error_check.go REGISTERED as "empty-error-body": zero FP across all probes (pure-empty/comment-only/semicolon-only bodies precise, non-error vars exempt, fingerprint multiset delta handles fix-one+introduce-one), exactly the Pattern-1 revival path the #507 tombstone reserved ("the only zero-FP revivable part"), semantically distinct from nil-deref-after-error (post-check inaction vs misuse), delta-gated so steady-state rewrites are zero-noise (e2e verified: 2 new bodies reported, identical rewrite 0). 26 deleted symbols verified zero-external-reference; exprText dependency confirmed live (suspicious_comparison_check.go:269); checkEmptyErrorBody already covered by detector_delta_regression_test.go. Full internal/agent package regression green.
  • fix(#507): delete error_swallow + error_nopropagate dead detectors instead of #499-style resurrection — both were registered at birth, stripped by the fc5c4aa critical-only refactor, and survived as fully-implemented+unit-tested dead code (6th instance of the #328/#330/#499/#503/#506 class), but revival is empirically FP-confirmed by sa-166 (4/4 in-package repro tests): error-swallow Pattern 2 (bare return) fires on named-result propagation — func f() (err error) { if err != nil { return } } returns the NON-nil err (Effective Go recommended, required for recover()), yet the warning text claims "instead of returning nil" (factually wrong because funcReturnsError never checks whether results are named); error-nopropagate fires on the Go 1.20+ errors.Join accumulation idiom (errs = append(errs, err) ... return errors.Join(errs...)) where the suggested fix silently converts accumulation into fail-fast — coaching agents into breaking correct code (same class as the #505 forwarding-wrapper FP) — and on struct-field stores (s.lastErr = err) / channel handoffs (errCh <- err), deferred-propagation sinks that AST heuristics cannot distinguish from swallowing without interprocedural dataflow (the reason go vet/errcheck/staticcheck all deliberately implement no such check); its line-number delta also re-reports every pre-existing instance after any top-of-file insert while a deletion above a new true positive suppresses it (error_swallow's own header comment documents why fingerprints are required instead). The two LIVE helpers (looksLikeErrorVar/isNilIdent — sole external consumers were nil_deref_check.go:325,326,390,392) were migrated into nil_deref_check.go with the thin looksLikeError wrapper inlined; the other 18 symbols verified zero-external-reference before deletion (countErrorNoPropagate was not even referenced by its own test). Tombstone comment in write_integrity.go records the revival preconditions: named-result exemption + drop the nil claim (or revive unambiguous Pattern 1 alone); deferred-sink exemptions + fingerprint delta. Full internal/agent package regression green.
  • fix(#506): delete error_order + float_equality dead detectors instead of #499-style resurrection — both were registered at birth (3e3e2ad / ac25e08), stripped by the fc5c4aa critical-only refactor, and survived as fully-implemented+unit-tested dead code (4th/5th instance of the #328/#330/#499/#503 class), but unlike append_ignored (#499, zero-FP pure win) revival is empirically 100%-FP: sa-165 probe-verified that checkErrorOrder fires on the io.Writer contract idiom (n, err := w.Write(b); total += n — n is VALID on error by the stdlib's own contract, io.Copy does exactly this) with a factually wrong "causes a panic" claim for non-pointer results, while its core true positive (defer resp.Body.Close() before the check) is ALREADY covered by the registered nil-deref-after-error — which correctly stays silent on the writer idiom; and checkFloatEquality's feqIsMathFunc matches EVERY math.* call as float64 (IsNaN/IsInf/Signbit return bool, Float64bits returns uint64 — its epsilon fix advice does not compile), flags exact-representable sentinels (== 0.5, == 1.0) where epsilon comparison would BREAK correct code, discards oldContent entirely (no delta), and cannot in principle separate sentinels from computed values without dataflow (the reason golangci-lint keeps float-eq off by default). All 13 file-private helpers verified zero-external-reference before deletion; tombstone comment in write_integrity.go extended with both revival preconditions. Issue also records sa-165's census of the fc5c4aa batch: 93 stripped, 32 currently registered, 78 still-unregistered names of which 54 have surviving implementations (46 whole dead *_check.go files + 6 renamed variants + 4 dead funcs in live files), flagged error_swallow_check.go as the priority next batch (contains live helper looksLikeErrorVar used by nil_deref_check.go:392 — migrate before file deletion). Full internal/agent package regression green.
  • fix(#505): printf_format_check double deterministic false positive — two idiomatic Go patterns that go vet (the detector's own baseline, cited in its header) explicitly exempts both fired 100%: (1) variadic spread calls like fmt.Sprintf("%s=%v\n", kv...) / log.Printf("%s %s", parts...) counted the spread as exactly one argument (extraArgs=1 vs verbs=2 → "2 format verb(s) but 1 argument(s)") because the AST counter never reads call.Ellipsis — runtime slice length is statically unknowable, which is exactly why vet's printf checker skips spread calls; the warning's "This causes a go vet error" claim was factually wrong, and the same counting flaw hit explicit-index reuse ("%[1]s and %[1]s" with one arg). (2) forwarding wrappers — Go's most common helper shape, stdlib testing.T.Logf/slog/errors style: func logf(format string, args ...any) { log.Printf(format, args...) } and the prefix variant log.Printf("[WARN] "+format, args...) — always tripped the nonconstant-format injection warning because the format parameter is a lowercase Ident (not all-caps, not a literal, not err.Error()); the warning's fix advice (Printf("%s", format)) would BREAK the wrapper's forwarding semantics, actively coaching agents into corrupting correct code. Fix, empirically verified by sa-164 (in-package repro tests + vet contrast runs, fact chain a-g): (1) skip verb-count when call.Ellipsis.IsValid() or the literal uses %[N] explicit indexes; (2) scope-aware forwarding exemption — findPrintfFormatIssues now uses an ast.Visitor carrying the enclosing FuncDecl/FuncLit parameter names, and a call is exempt when its format expr is a parameter (or string-literal prefix + parameter) AND its final spread argument is another parameter; local-variable formats and genuine verb mismatches still warn. Characterization tests: VariadicSpread, ExplicitIndex, ForwardingWrapper (both forms) all silent; TruePositivesSurviveExemptions (local var format + no-spread mismatch) still fires; all 12 pre-existing tests unchanged and green.
  • fix(#504): emitGeneration dead field wired into per-callback emitIfCurrent guard — the #489 commit message claimed "emit() generation guard drops stale-run stream events" and the struct comment claimed protection over "persist tail, stream emit", but the diff only ever wired the PERSIST side (persistSession/persistGeneration + sessionLockMismatchLocked); emitGeneration was declared at chat.go:178 with zero uses at the fix commit and forever after (same #328/#499 dead-wiring class), leaving emit() unguarded: applySemanticToLiveHistory writes into the CURRENT session's live history (reseeding it from the new session's messages when empty), the done branch mutates desktopAssistantID/desktopTextSeq (corrupting the next real turn's message linkage), and OnStreamEvent/tunnelHost.PushStreamEvent fire regardless of run ownership. One-click trigger (verified by independent subagent sa-163, fact chain a-f, severity Medium): App.NewSession() runs Cancel → StartNewSession → ClearCurrentSession back-to-back synchronously while the agent goroutine is still draining in-flight events (agent.go emits summary/error/tool-result events after ctx cancel; Cancel() nils b.cancel synchronously so LoadSession's busy guard also passes during the drain) — the cancelled run's tail events land in the NEW session's live history and frontend stream as ghost content. Fix: each of the three RunStream*/RunStreamWithContent callback sites captures the generation under the lock before starting the run and routes through emitIfCurrent(gen, ev), which drops events whose generation no longer matches; the dead emitGeneration field is removed and the struct comment now states where each half of the protection actually lives. Characterization test: TestEmitIfCurrent_DropsStaleRunEvents (stale event dropped, current event delivered). Disk persistence was already correctly protected by the #489 snapshot + #305 tombstones — this fix covers the in-memory/UI half.
  • fix(#503): delete checkAssertionWeakening dead code instead of #499-style resurrection — the detector was unregistered by the fc5c4aa critical-only refactor but its 657 lines (impl + tests) survived as dead code, and re-registering it (the tempting fix after #499 re-wired checkAppendIgnored) would activate an empirically-confirmed false-positive class: detectExpectedValueChange compares ALL literals positionally, so rewriting a human-readable error message ("failed to fetch" → "failed to fetch user profile") in source+test sync fires a reward-hacking accusation on an everyday edit; the only exemption (literal count change) never triggers on rewording since counts stay equal. The whole file is deleted (all 17 symbols file-private, zero external references, verified by symbol-level grep) and a tombstone comment in write_integrity.go's allChecks registry records WHY there is deliberately no assertion-weakening entry — including the revival precondition (position-aware exemption for testify trailing msgAndArgs and t.Error*/t.Fatal* first-arg format strings) — so a future #499-pattern pass does not blindly resurrect it.
  • fix(#499,#500,#501,#502): four verified detector defects — checkAppendIgnored registered at last (the write-time discarded-append() detector was fully implemented and unit-tested but never wired into the write_integrity allChecks registry — third instance of the #328/#330 dead-detector class; one-line sliceCheck registration in the Go-correctness section), wt/expired/futile read tracking is batch-aware for multi_file_read (agent.go recorded only files[0] via extractToolFilePath so a 5-file batch read counted as one — suppressing the cross-file stale-read warning below minReadsBeforeWarning=2 exactly when agents rely on batch reads, and starving expired-read/futile-cycle identically; the loop now records every path from extractFilePathsFromArgs, post-edit re-read hint stays first-path to avoid spam, and the three dead helpers extractToolFilePath/extractWTReadPath/extractMultiReadPaths are removed with their tests), spec_gaming isCIRelatedTask matches 'ci' as a whole word (bare substring empirically hit 12/12 everyday prompts — efficient, special, decide, precision, pricing, sufficient, crucial all contain 'ci' — permanently disabling the CI-tampering pattern for ordinary tasks; tokenized match via FieldsFunc with substring matching kept for multi-word phrases, counter-cases added), export_guard burns its once-per-run dedup marker only after a comparison actually completes (the marker was set BEFORE gitHeadExportSymbols, so a failed git-show on an uncommitted new file silenced the guard for that file for the rest of the run even after a mid-run commit made the comparison possible — exactly the breaking changes the guard exists to catch; all four verified by independent subagents sa-160 fact chain 4/4 and sa-161 fact chain 5/5 with empirical reproduction)
  • docs(#498): UnbindAdapter doc comment now matches its idempotent implementation
  • fix(#497): MCP hot-reload watcher is scope-aware — workspace sessions no longer lose workspace-only servers on global-file edits (watcher fed the GLOBAL file's list into Reload while workspace-bound managers are built from the WORKSPACE mcp_servers.yaml — two set computations that only coincide for global-scope users — so any daily global write (another session's AddMCPServer, CLI mcp add, Claude migration, manual edit) name-diffed the session's workspace-only servers into 'removed' → tools unregistered + connections closed, silently and irrecoverably until restart, while global-only servers leaked INTO the workspace session; the watcher was also blind to manual workspace-file edits entirely. resolveScopeMCPServers now mirrors the wailskit LoadConfigForWorkspace resolution — workspace ggcode.yaml present ⇒ read the workspace mcp_servers.yaml (or .ggcode/mcp_servers.yaml), else the global file — making the reload input match the manager's initial-set computation; scope files join the watch set so workspace edits now trigger reloads; the shared mtime watermark advances across ALL watched files, fixing the reload-storm edge when the global file is absent; verified by independent subagent sa-158 fact-chain a-e, severity High; characterization tests: global edit keeps workspace servers, workspace-file edit reaches the manager, no reload storm without a global file)
  • fix(#496): AddMCPServer workspace-scoped writes now propagate to the running session (hot-reload watcher polls only the GLOBAL mcp_servers.yaml — interactive_core.go constructs MCPHotReload with config.ConfigDir() while SaveMCPServers writes filepath.Dir(workspace yaml)/mcp_servers.yaml, two paths that never intersect — so a workspace-bound session's MCP edit/add was silently dead until restart; even the UI Reconnect button was structurally useless: MCPPlugin.Connect short-circuits on the cached adapter and reconnects reuse the plugin's construction-baked m.cfg, making Reload the only effective primitive; AddMCPServer now snapshots the bridge once per the #458 pattern and calls reloadSessionMCPServers — the same MergeStartupServers+manager.Reload computation a watcher-triggered reload performs, idempotent for persisted Claude-migrated servers — symmetric with RemoveMCPServer's #408 explicit Disconnect; verified by independent subagent fact-chain 6/6, severity Medium; characterization tests in mcp_reload_test.go: workspace add reaches manager without watcher tick, edit replaces the plugin, workspace persistence without global leak)
  • fix(#493,#494,#495): spiral_hallucin per-topic verified + spiralMinGap enforcement ends the global-reset false positive (spiralTrackedTopic gains per-topic verified — recordSpiralVerification and prose-verification mark all tracked topics, new uncertainty no longer wipes any protection, so the exact issue scenario "unrelated 'I think the retry logic' re-arming warnings on verified postgres" stays silent; the dead spiralMinGap constant and write-only sourceTurn field now gate committed counting — commitment at gap<2 is ordinary inconsistency and does not count, same-turn gap=0 included; the obsolete "3+ topics AND no verification" global mitigation that the reset used to bypass is removed; characterization suite 4bb20bd flipped with a no-verification contrast case proving the true positive survives), tool_equiv implements the documented :32 exact-match suppression contract (rawFingerprints write-only dead map replaced by per-normFp raw tracking — when EVERY occurrence of a normalized fingerprint is byte-identical the repetition belongs to tool_redundancy and this detector yields, killing the double-warning where byte-identical grep x3 fired both "Semantic duplicate... already in context" and "already in your context from earlier calls"; threshold 2→3 so divergent-raw (reordered keys / volatile fields) semantic equivalence — the case this detector exists for — is what warns; both messages now carry "unless context compaction has trimmed earlier results" so post-compaction re-queries are not falsely asserted to be in context), and tool_overuse write bookkeeping moves post-execution gated on !result.IsError (maybeWarn is pre-execution and could never see the outcome, so failed edit_file — the most common LLM arg error — recorded filesWritten, then the recovery read_file that edit_fail_recovery itself recommends received false-premise "Trust the content from your edit" guidance contradicting that recommendation in the same iteration; recordWriteResult added at the existing spiral-verification gate which already has result.IsError, failed writes also clear stale entries from prior successful edits, and trivial command matching is now whole-word-sequence so cat .pwd_history / ls /tmp/uname-dir / echo which python3-config no longer match)
  • fix(#497): RemoveIMAdapter cascade reorder makes the #460 retry advice structurally reachable (unbind-first uses im.Manager.UnbindAdapter's documented idempotency — runtime_bindings.go:194 no-bindings-is-no-op — so a failed unbind now leaves the adapter config intact for a real retry; the old delete-then-unbind order left every retry dead at config.RemoveIMAdapter's "not found" guard (config_save.go:511) before it ever reached the unbind, meaning a binding-store failure during adapter removal permanently leaked the ghost binding the #396 cascade was built to prevent, while the error text kept advising "retry to clear the leftover binding"; both failure paths are now retryable — unbind failure keeps config for full-chain re-entry, config-delete failure re-runs a harmless unbind no-op — and the intermediate state degrades from config-gone+ghost-binding to config-present+unbound), characterization test TestRemoveIMAdapterFailedUnbindRetryWorks asserts the surviving-config invariant the old order violated plus the recovered retry completing full removal, nil-manager path covered separately
  • test: characterization tests proving spiral_hallucin gap-semantic and verified-granularity defects
  • fix(#490,#491,#492): plan_abandon execution-evidence gate ends the ~100% false-positive on faithful multi-step completion (maybeWarnPlanAbandon now takes runStats and only warns when a declared step CATEGORY has zero matching evidence — edit-class step with empty FilesEdited or run-class step with empty CommandsRun; pure-read plans have no evidence channel and stay silent, nil stats keeps the declare→claim-done-without-doing shape triggering — fulfillment_gate.go's evidence pattern reused, characterization test c7946c9 flipped to assert the fixed behavior with a true-abandonment contrast case), correction_spiral joins the #483/#485/#488 run_command-content-classification fix family as its third missed consumer (successful cat/ls between edit and build no longer breaks the correction chain via pendingEdit=false — sub-problem A detector-blindness; failed exploratory commands no longer pollute errorSequence — sub-problem B; psArgs deserialization hoisted above the block, start_command excluded entirely since its result reflects job startup not verification outcome, dead csIsVerifyTool removed), and csClassifySeverity crash/test ordering fixed (test-failure shape fail+test/assert/expect now checked BEFORE crash markers so "--- FAIL: TestSignalHandler" classifies as sevTest not sevCrash, bare "signal" substring tightened to contextual "received signal"/"signal:" forms — sub-problem C); momentum_loss shell-channel blindness fixed (run_command/start_command classified by command CONTENT via mlIsObservationalCommand — cat/ls/head/tail/pwd/rg/grep/find/ag/ack/bat/less/more/tree first words plus git log/diff/show/status/blame subcommands demote to consumptive, unknown and genuine build/test commands stay productive so the conservative whitelist cannot introduce new false positives — late-phase shell exploration now triggers last-mile stall exactly like its tool-channel equivalent read_file/grep per the documented contract)
  • test: characterization test for plan_abandon false positive on faithful completion
  • fix(#487,#488,#489): premature_refactor revived from 100%-dead wiring (prematureRefactorRecordVerify was called unconditionally on EVERY tool result, so the first read_file set hasVerified and silenced the detector for the whole run; now gated on command CONTENT via psIsVerifyCommand(extractCommandFromArgs) like its two neighbors, plus F1 localized-fix exemptions — line-wise core diff, ≤2 differing lines with <32 changed bytes, or small pure tail append/delete — and F2 keyword hardening: whole-word matching with comment lines stripped, so extractTargets/os.Rename identifiers and "optimize later" TODOs no longer classify, and recordEdit hoisted out of the per-file loop so one multi_file_edit no longer doubles the counter), target_scatter window no longer wiped by observational commands (ls/cat/pwd/git log interleaved with diagnostics is the most common investigation shape — scatterIsVerification was the exact #350 tool-name-vs-content bug family; now only genuine verify commands clear the window via psIsVerifyCommand, hasMutation set-then-clear dead store and its dead check gate removed, scatterIsMutation extended with file_ops/git_*/undo_edit/write_command_input/enter_worktree, dead reset() wired into the per-turn reset), chat.go persist cross-write re-armed by post-Cancel nil fallback fixed via per-run closure-captured persist snapshot + run generation counter (run start binds snapshot; ClearCurrentSession bumps generation and drops it; late tail persists from the cancelled run now drop instead of falling through runSes==nil → currentSes==NEW session — the exact #270 contract re-armed), context.Canceled filtered from appendLiveError, emit() generation guard drops stale-run stream events, and the zero-caller wailskit.NewSession dead export removed
  • fix(#484,#485,#486): compounded_uncertainty wires the born-dead unverified_success channel (premature_success feed at its trigger point, weightUnverifiedSucc was never referenced by any production caller; assumptions category retired honestly — docs/warning text updated to the three real channels after 387282a removed the assumption detector), strategy_fixation green-verification FULL reset (whole-tree build terminates every file's streak, not just lastFile — stale cross-file counts fired "approach not converging" right after a green build) plus directory-qualified failure attribution (same-base-name collision internal/agent/agent.go vs internal/tool/agent.go no longer misattributes), sfExtractMutationPaths walks multi_file_edit files[] + notebook_path (previously first-path-only / never extracted), and run_command verification gated by psIsVerifyCommand (successful cat/ls no longer resets streaks); patch_exhaust implements the documented-but-never-built lastPatch excursion tolerance (single one-read excursion resumes the stashed count, second departure hard-resets so two-dir ping-pong cannot accumulate, patchOf normalizes ./ prefixes and trailing-slash directory inputs via weNormalizePath)
  • test(#480): concurrency coverage for the writeMessage lock split — 4 writers x 50 frames alternating writeMessage/sendNotification paths under -race, every pipe line must parse as exactly one JSON-RPC frame (interleave detection), plus sendNotification no-deadlock structural check for the Unlocked variant contract
  • fix(#482,#483): wasted_explore cross-format path matching (weNormalizePath/wePathsMatch — ./ rg output matches absolute lsp/read paths, base-name rescue; code_search 'N. path' enumerator stripped so its searches are tracked at last) and premature_success hyphen-prefix verify patterns restricted to COMMAND position — 'git add test-utils.go'/'cat verify-config.yaml'/'gofmt -w test_utils.go' no longer arm everVerified and silence the detector for the whole run (ninja check-all arg-position casualty accepted per issue analysis); issue's 6 characterization tests flipped to fixed-behavior assertions, all 11 false-positive table entries verified
  • test: characterization tests for wasted-explore path normalization bug (#480 verification)
  • fix(#479,#480,#481): Summarize nonTailMutSeq TOCTOU guard (discards stale snapshot on any non-tail mutation during the LLM window — deletes/mid-inserts/mechanical clears; pure tail appends still rescued by extraMsgs), MCP server-request responses write under c.mu (writeMessage/writeMessageUnlocked split — stdio NDJSON interleave corruption and gorilla single-writer violation both fixed), Windows stale-lock cleanup unlocks+ closes BEFORE os.Remove (sharing-violation-proof order matching Release) with debug logging
  • fix(#478): SearchSessions uses a bounded bufio.Reader (readLineLimited) so a single >10MB JSONL line only discards THAT line — previously the Scanner cap aborted the scan and the caller's error-continue silently dropped every collected hit with no log; skipped lines are now debug-logged, hits before AND after the blob are kept
  • fix(#477): drainPendingInterrupt pops the parallel pendingSource/pendingExclude pair when it consumes a visible queued message (agent iteration boundary), and QueueMessage appends its own desktop pair — FIFO alignment between queue and slices restored, no more stale im/telegram pairs shifting later drains or leaking unboundedly
  • fix(#476): tunnel vision counts search-tool breadth (grep/lsp/code_search result files via extractSearchResultPaths, reusing searchResultTools) and exempts test-fix tasks from the ratio warning — a 12-file grep sweep plus 2 read_file's no longer scores as '2 files, broaden exploration'
  • test(#463,#465,#467,#469,#471,#472): regression coverage for the detector fixes — comment-prefix stripping end-to-end, strict verify prefix semantics, formatter exclusion from convergence lock, read-window detection, serial-read neutral unknown tools, tool-storm consecutive-iteration check
  • fix(#475): queued IM messages carry their own source+excludeAdapter through the drain (FIFO index-aligned slices replace the dead single-value field; source stays 'im' so exclusion is actually consumed — no more self-echo on the originating adapter)
  • fix(#474): guidance text merges into first tool message content instead of a user message between tool_calls and tool results — OpenAI ordering contract restored for strict backends
  • fix(#471..#473): extractCommandFromArgs strips mandated '# ' comment prefix (4 detectors revive on conventionally-formatted commands), convergence lock excludes pure formatters (gofmt/prettier/cargo fmt no longer arm it), drift_recurrence exempts compliant 0-new-dir agents + dirSignature absolute-path collapse
  • fix(#462..#470): 9 detector/business-logic findings — guidance_conflict phrase-anchored matching + mutual exclusion, redundant_read windowed-read exemption, sessions export merges tunnel events, guidance promoter write-only mechanism removed, serial_read fail-open mutation set, tool_storm consecutive-iteration check, tool_target_mismatch path-like filter + threshold 2, fix_cascade strict verify set + honest wording, diminishing_edit multi_file_write sizing
  • fix(#459,#460,#461): clipboard post-read size recheck (FIFO bypass), RemoveIMAdapter cascade error surfaced, pending IM message echo-exclusion restored
  • fix(#445..#458): 14 review findings across file_guard glob prefix, a2a SSE error/ctx-guard/input-required, desktop config .bak+atomic, IM ctx snapshot, cron step reversed-range, session restore unwindowed lookup, openai tool_result text + non-stream truncation, errclass anchored 429/529, fallback joined errors, app.go chat snapshots, wailskit single-bridge snapshot
  • fix(#443,#444): read-only tools bypass FileGuard; IsDestructive includes ask rules
  • fix(#442): write_file overwrite message — drop duplicated (was N bytes) suffix
  • fix(#431 follow-up): split daemon identity per-platform — syscall.Sysctl is darwin-only, unix build tag broke Linux CI
  • fix(#441): guidance critical classification — head-tag exact match only, single keyword source
  • fix(#429..#440): hooks malformed-pattern guard, lock final SameFile fail-closed, daemon identity argv truncation + Windows image-name + corrupt PID cleanup, IM backoff reset + data-race locks + ghost-binding purge + ReadLimited backoff, batch_replace braced-ref guard, command_gate newline separator + quoted-separator block, unverified_claim make/test-runner tightening, image ReadFile size pre-check, cron reversed-range rejection + migration duplicate-firing prevention
  • perf(agent): remove 7 low-certainty/high-noise monitors that cost more tokens than they saved
  • fix(#416..#423): verify-coverage path normalization + cumulative verification + multi-path scopes, token-waste full read-history expiration + negative-result exemption, quality scorer rune-safe truncation + struct keys, regression severity closure + continuous error threshold, checkpoint pre-existing-file classification
  • fix(#424..#428): artifact_guard separator scoping + non-empty line growth ratio, clipboard platform guard + error surfacing, data-URI MIME/base64 parsing, approval notification events + badge/dedup fixes, desktop-config .bak recovery + logstream dead branch removal
  • fix(#409..#415): PS quote escaping, rm gate separator scoping, lock path sanitization, daemon identity check, hooks env/process-group/mixed-match, cron migration atomicity+lock, cron parser range validation
  • test(#387,#388): word-boundary and ReadLimited regression coverage
  • fix(#401..#408): session ID sanitization, stdin Stat guard, flock unlink-race inode check, signal ReadLimited, a2a auth fallthrough + push card, MCP symmetry, claim word boundaries
  • fix(#384..#400): rm long-flag gate bypass, batch_replace group-ref guard, rune-safe pinned truncation, detector precision, desktop cascade/notify/tray fixes
  • fix(#387..#391): go-test word boundary, image size overflow detection, reconnect backoff reset, stream-failover retry reachable, clone isolation
  • fix(#382,#383): Summarize path re-injects pinned context; calibration samples aligned with tool-definition overhead
  • fix(#374..#381): explicit-restart queueing, tool-activity liveness, stream success reset, dead-wiring fixes, detector precision
  • test(#364,#366): regression tests for caveat-negation scoping, done-identifier exclusion, adaptiveMinSamples gate
  • fix(#369,#370,#371,#372,#373): wire MCP ContextFill injection, complete #362 restart guard block, async-error stream failover, FallbackProvider optional-interface forwarding, wildcard charset hardening
  • fix(#366,#367,#368): adaptiveMinSamples enforcement, symmetric budget-stop sentinel error, instance vendor writeback for desktop config
  • fix(#365): scope-aware truncation advisories, task_output tail_lines paging, context-fill-aware MCP result cap
  • fix(#360,#361,#362,#363,#364): LSP install WaitDelay, desktop hotkey init order, TUI restart stall-aware fallback, truncation marker variants, success_declare negation scoping
  • test(#359): align action_hedging tests with narrowed patterns
  • fix(#356,#357,#358,#359): adaptive-effort self-disarm, tunnel-message live seed, tool-effectiveness anchoring, hedging pattern precision
  • test(#355): compositionLocked unit test (companion to 685ffe9)
  • fix(#352,#353,#354,#355): detector false-negative/veto fixes, LSP install option guard, calibrator composition isolation
  • fix(#351): task_output surfaces failed/cancelled/limit-rejected sub-agent errors
  • fix(#349,#350): premature-success whitelist verification + failed-verify contradiction, X11 empty-pattern window guard
  • fix(#346,#347,#348): delegation ID namespace bridging, turn-aware restart, availability-gated tool ads
  • fix(#345): pre-wiring audit — detector logic fixes before activation
  • fix(tui): restart tool now rides the proven IM /restart path (remoteRestartMsg)
  • debug(tui): probe agentDoneMsg RunID drop path for pending restart
  • fix(tool): align restart test with terse result text
  • fix(tool): terse restart result — stop echoing the caller's own reason back
  • fix(#341-#344): 4-issue batch — dormant detector wiring + stale-failure and misclassification fixes
  • fix(tui): fire pending restart from agentDoneMsg — the real completion path
  • fix(tui): restart arming lost to Bubble Tea value semantics — send msg instead
  • fix(#334-#340): 7-issue batch — detector false-positive/dead-code/pipe-context fixes
  • refactor(tui): single beginRestart core shared by /restart and the LLM restart tool
  • fix(tool): restart fires only after tool results are persisted to session
  • feat(tool): LLM-invocable restart tool — self-restart preserving the session
  • refactor(tool): extract ydoModifierClick from Wayland dispatch
  • test(#328/#330): close assembly-coverage gaps for interface-compliance and dep-major-bump
  • feat(tool): Linux UI-tree actions via AT-SPI — snapshot_ui/find_element/find_and_click/wait_and_click on X11 AND Wayland
  • test(#331-#333): regression tests for detector fixes
  • fix(#331-#333): 3-issue batch — detector false-positive/miss fixes
  • fix(#328-#330): 3-issue batch — register 6 orphan detectors into write_integrity, desktop file API security containment
  • fix(#325-#327): 3-issue batch — shadow-check var-decl reuse, IM callback correlation, A2A cancel generation tokens
  • fix(#321-#324): 4-issue batch — file tracker integrity, thinking-signature pollution, summary-plan recent-group guarantee
  • fix(#318-#320): 3-issue batch — assertion detector false positives, error-wrap delta key
  • fix(#308-#317): 10-issue batch — detector false-positive fixes, MCP OAuth multi-server, cron sleep compensation
  • fix(#302-#307): 6-issue batch — provider cancel/timeout semantics, auth misclassification, session-delete resurrection, string-400 retry
  • fix(#301): use util.SnapToRuneStart for export truncation + rune-boundary test
  • fix(#297-#301): 5-issue batch — session deletion resurrection guards, IM cascade cleanup, adapter field preservation, UTF-8 export
  • fix(#282-#296): 15-issue batch — config persistence scope correctness, session deletion safety, MCP race, desktop hardening
  • fix(#263-#281): 19-issue batch — session lifecycle races (High), detector FP/FN fixes from review follow-ups, retry policy, template rename
  • fix(#248-#262): 15-issue batch — session cleanup data loss (Critical), MCP hot-reload deadlock, security gates, IM reliability, desktop fixes
  • fix(#245-#247): 3-issue batch — insecure-pattern matching precision, secret-check exemption scoping, LoadSession failure cleanup
  • fix(#232-#244): 13-issue batch — detector false-positive/false-negative fixes, session-switch races, gemini truncation
  • fix(#228 follow-up): extracted thinkingAccumulator — text deltas were lost from signed blocks
  • fix(#228-#231): 4-issue batch — interleaved thinking blocks, multimodal first message, template name collisions, interrupt double-write
  • fix(#221-#227): 7-issue batch — detector deltas, streaming lifecycle, usage parsing, subagent limits
  • fix(#195): TestNormalizeFilePath — case-fold expectation is platform-conditional
  • fix(#216): X11 modifier_click — xdotool click has no modifier syntax
  • fix(#218,#220): concurrent_map — WaitGroup/Once/Pool are not mutexes; delta key by content fingerprint
  • fix: 30-issue batch (#188-#217) — Windows/Wayland input, permission hardening, detector fingerprints, desktop races
  • fix(mcp): surface gateway error bodies instead of misleading Notification type
  • fix(mcp): add NDJSON fallback for HTTP responses
  • test: regression tests for #183-#186 fixes; empty-error fingerprint includes function name
  • fix: 11 issues (#177-#187) — desktop_control, stream contract, MCP DoS, TUI wrap, detector deltas
  • refactor(tool): extract list_windows AppleScript body into named helper
  • feat(tool): close computer-use gaps vs Anthropic 20250124 spec — middle_click, mouse_down/up, hold_key
  • refactor(tool): extract windows desktop_control actions into helpers
  • feat(tool): Windows window management for desktop_control via Win32
  • feat(tool): Linux Wayland support for desktop_control via ydotool
  • fix: 5 issues (#172-#176) — detector delta-awareness completion
  • feat(tool): drag duration interpolation (macOS) + Windows SendInput backend