Skip to content

Dev to Main#97

Merged
langowarny merged 1078 commits into
mainfrom
dev
May 24, 2026
Merged

Dev to Main#97
langowarny merged 1078 commits into
mainfrom
dev

Conversation

@langowarny

Copy link
Copy Markdown
Collaborator

Summary

Large integration release rolling up ~3 months of work on dev. Highlights span
agent runtime, voice/live channels, teammate platform, cockpit operator UX,
graph admission telemetry, and a sweeping coverage push.

Agent Runtime

  • ADK v1.0 → v1.3 upgrade with capability-first user-facing surface;
    ThoughtSignature boundary documented, plugin.go spike removed.
  • Streaming function toolsTool.StreamingHandler + AdaptStreamingTool.
  • SkillToolset adapter — agent skill discovery from filesystem.
  • Voice channel infrastructure (Phase 1)internal/voice STT/TTS
    interfaces with a Gemini provider and noop fallback.
  • ADK Live API plumbing (Phase 1)internal/live Session, VoiceMode,
    LiveExecutor.
  • a2a outbound migration to remoteagent/v2.

Teammate Platform

  • Teammate runtime v1 (scaffolding → capability runtime → production
    cutover), with approval identity policy and runledger mirroring of
    approval-blocked state.
  • Built-in multi-agent execution path hard-cut; routing hints decoupled
    from instructions.

Cockpit & Operator UX

  • Mission control in cockpit: lifecycle service, store persistence,
    durable-first reads, write paths, collaboration context, proposal
    registry (prepare/accept/dismiss), operating loop agenda.
  • Standalone Workbench shell; bare lango routes to workbench.
  • Dead-letter operator surface across cockpit + CLI: read, filter
    (subtype/reason/dispatch/actor/time/family), retry, reset, summary
    strips (actor, dispatch, family), grouped summaries, refresh recovery.

Graph / Telemetry

  • Observe-only graph admission policy types, runtime event producers,
    write-failure baseline event, unmapped admission source telemetry,
    extractor drop telemetry.
  • Sparse admission config preserved across load/save round-trips.

Reliability / Runtime

  • Normalized replay recovery runtime policy.
  • Dispute runtime flow completed; reputation runtime model strengthened.
  • Consolidated operator dead-letter surfaces; narrowed dead-letter
    bridge API.
  • CLI decoupled from app boundary (refactor: decouple cli from app boundary).

Security

  • KMS bootstrap fail-closed; passphrase/secrets prompt routing hardened.
  • Recovery error surfacing, UTF-8-safe redacted projections, gateway
    shutdown lifecycle hardened, sandbox worker exit codes, doctor bind
    failure classification.

P2P / Knowledge

  • Local p2p workspace CLI control; knowledge exchange runtime docs +
    retriever residual coverage; FTS/vector path consolidation.

Test Coverage

  • ~176 coverage commits across 57 numbered waves (later renamed to
    "slices") targeting ADK, app wiring, runledger, p2p, security,
    ontology, cockpit, smartaccount, etc. Spec gate tests added for ADK
    v1.3 Tracks B/C.

Docs

  • README refactored into landing page with docs/ navigation; Workbench
    vs Cockpit distinction clarified.
  • 7 ADK v1.3 capability track plans under docs/superpowers/.
  • Architecture docs expanded (mission, dead-letter, settlement,
    adjudication, knowledge exchange, identity/trust).

langowarny and others added 22 commits May 21, 2026 22:06
Reduce README from 1887 to ~275 lines. Move CLI / configuration / payment /
security details into the existing docs/ tree. Add Documentation Map as the
primary navigation surface. Fix three broken Quick Start commands
(agent.provider / providers.openai.apiKey, serve not gateway, chat not run).

See internal-docs/superpowers/specs/2026-05-23-readme-refactor-design.md
for the design and internal-docs/superpowers/plans/2026-05-23-readme-refactor.md
for the implementation plan.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bare `./bin/lango` launches the Mission Workbench TUI; the multi-panel
Cockpit dashboard is the explicit `./bin/lango cockpit` subcommand.
README previously conflated the two. Update the Quick Start Run section
to show both invocations and broaden the Architecture diagram's
Surfaces row to reflect all three TUIs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7-track integration design under feature/adk-v1.3:
- Track 0: deps bump v1.0.0 → v1.3.0
- Track A: wrapper slim-down (plugin.go delete + ThoughtSignature doc)
- Track D: A2A v2 immediate migration
- Track B: Skill Toolset adapter
- Track C: Streaming function tools
- Track E: Live API plumbing (Phase 1; Bubbletea cockpit Phase 1.5, audio Phase 2 deferred)
- Track F: Channel voice infrastructure (Phase 1; per-channel integration Phase 2 deferred)
- Track G (Discord realtime voice): deferred to future cycle

Reviewed via reviewing-plan-artifacts-with-subagents (3 rounds on initial
design, 2-3 rounds per track plan). Status Log captures completion dates
for all tracks landed in this branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Upgrade ADK SDK three minor versions. v1.1 added FindAgent/FindSubAgent
interface methods (transparent — Lango uses upstream agent factories, no
custom implementations). v1.3 added model.LLMResponse transcription
fields (additive, nil-safe). Transitive bumps include genai v1.57,
a2a-go v0.3.15, new a2a-go/v2 v2.3.1, gRPC v1.81, OTel v1.43.

Prerequisite for Tracks A-F (capability adoption). See
internal-docs/superpowers/specs/2026-05-23-adk-v1.3-capability-first-design.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Compile-time tripwire that verifies the v1.3 API surface (Agent.FindAgent,
LLMResponse transcription fields, tool/functiontool streaming,
tool/skilltoolset, agent.LiveSession) is reachable. Guards against
accidental downgrade or vendor drift.

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

Track A (wrapper slim-down) of ADK v1.3 capability-first design:

A1 — ThoughtSignature handling: KEPT internal/adk/state.go round-trip.
Initial plan called for removal because v1.3 added stream_aggregator
ThoughtSignature propagation. Investigation showed upstream operates at
the streaming layer (between Parts within one LLM response) while
Lango's code operates at the persistence layer (ADK runtime ↔ Lango
session storage). Orthogonal concerns. Removing Lango's code would
break thought_signature continuity across session restarts. Added an
explanatory block comment at functionCallToToolCall pointing to spec
§4 Track A A1.

A2 — plugin.go: DELETED. 290 LoC spike with zero production consumers
(verified by grep). ADK v1.3 has a stable plugin API; if Lango adopts
plugins later, a fresh design from v1.3 will be more accurate than the
stale parity-gap doc the spike contained. Also removed the spike's
test fragment from context_helpers_round_trip_agent_and_child_session_test.go
(TestPluginConstructorsExposeCallbacksAndMapKeys + helper struct + interface
assertion), preserving the file's 5 other valid tests.

A3 — OTel reasoning attribute rename: no-op confirmed. grep returned
zero hits for experimental.reasoning|reasoning_tokens|gen_ai.usage in
Lango source.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Track D of the ADK v1.3 capability-first design. Switch
internal/a2a/remote.go (+ remote_test.go) from
google.golang.org/adk/agent/remoteagent (v1, now Deprecated) to .../v2:

- A2AConfig.AgentCardSource string was removed in v2; replace with
  AgentCardProvider: remoteagent.NewAgentCardProvider(url), the v2 lazy
  URL/file resolver. Semantics equivalent (lazy resolution preserved).
- All other A2AConfig fields, NewA2A() signature, and AgentCard handling
  are signature-compatible.

internal/a2a/server.go is NOT migrated. It is Lango's custom AgentCard
JSON server with P2P/pricing/ZK extensions, NOT an ADK adka2a executor.
The previously-planned A2AClientProvider v1-fallback plumbing is
unnecessary because Lango is an A2A *client*; outbound goes v2 immediately
without affecting inbound peers.

Verification: build clean; ./internal/a2a/... tests pass; whole-tree
test suite matches Track 0 baseline; zero v1 agent/remoteagent imports
remain in Lango source.

a2a-go/v2 v2.3.1 entered the module graph as transitive // indirect
during Track 0's go.mod bump; Track D does not promote it to direct.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Track B of the ADK v1.3 capability-first design. Wire ADK v1.2+
tool/skilltoolset into Lango so agents can discover markdown-defined
skills from a configured directory via three tools: load_skill,
list_skill_resources, load_skill_resource.

Added:
- internal/adk/skilltoolset.go: BuildSkillToolset(ctx, dir) helper using
  upstream skill.NewFileSystemSource + skilltoolset.New (no custom
  skill.Source — the upstream filesystem source already covers our
  requirements per agentskills.io frontmatter spec).
- internal/adk/skilltoolset_test.go: 5 unit tests (empty / nonexistent /
  file-not-dir / valid / empty-dir).
- internal/adk/agent.go: WithToolsets(...tool.Toolset) option plumbing
  into llmagent.Config.Toolsets.
- internal/orchestration/orchestrator.go: Config.Toolsets field plumbed
  into the orchestrator's llmagent.Config.Toolsets. Sub-agents do NOT
  receive these toolsets — orchestrator only.
- internal/config/types.go: AgentConfig.SkillsDir field (default empty
  = disabled).
- internal/app/wiring.go: shared SkillToolset construction routed to
  BOTH single-agent (adk.WithToolsets) and multi-agent (orchCfg.Toolsets)
  paths.
- assets/skills/example-skill/SKILL.md: demo skill with valid YAML
  frontmatter proving the wiring.

.claude/skills/ migration is DEFERRED to a future change — existing files
mix Claude-Code-specific frontmatter (with compatibility, metadata fields)
and bare markdown. A clean migration requires deciding which Claude-Code
fields to keep/strip per agentskills.io.

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

Track C of the ADK v1.3 capability-first design. Wire ADK v1.3's
functiontool.NewStreaming[TArgs] into Lango so tool authors can yield
partial results via iter.Seq2[string, error].

Added:
- internal/agent/runtime.go: new StreamingToolHandler type and optional
  StreamingHandler field on Tool (nil-default — backward compatible).
- internal/adk/tools.go: AdaptStreamingTool routes through NewStreaming
  when StreamingHandler is set, else falls back to functiontool.New.
  Per-call timeout and agent-name context injection work for both paths
  via the new injectCallContext helper.
- internal/adk/tools_streaming_test.go: 3 unit tests covering streaming
  branch, fallback branch, and regression of non-streaming AdaptTool.

Scope note: in non-live agent runs ADK aggregates yielded chunks into a
single result string (base_flow.go:1106-1117). Real-time chunk surfacing
in cockpit/turnrunner is deferred to Track E (Live API), where chunks
flow to liveSess.Send. Track C only ships the adapter — non-live behavior
is identical to a regular tool from the caller's perspective.

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

Track E Phase 1 of the ADK v1.3 capability-first design. Framework-
agnostic Live API plumbing; Bubbletea cockpit wiring is Phase 1.5,
audio I/O is Phase 2.

Added:
- internal/live/config.go: Config{Modalities, AudioSink, AudioSource,
  MaxLLMCalls} + DefaultTextConfig() (text-only Live mode).
- internal/live/audio.go: AudioSink / AudioSource interfaces + NoOpSink /
  NoOpSource defaults consumed by Phase 1 text mode. Phase 2 will swap
  in malgo-backed implementations.
- internal/live/session.go: Session wrapper around runner.Runner.RunLive
  with Send / Events / Close. Nil-safe Close; idempotent.
- internal/live/voicemode.go: VoiceMode state machine (Off / Active)
  with Toggle / Send / Events / Close / IsActive / State / Indicator.
  UI-framework-agnostic so Phase 1.5 can adapt to Bubbletea by wrapping
  Toggle in tea.Cmd and bridging Events() → program.Send(liveEventMsg).
  Local LiveExecutor interface lives here (in package live) to avoid the
  import cycle that would arise if it lived in turnrunner.
- internal/live/session_test.go + voicemode_test.go: 11 unit tests
  (5 session + 6 voicemode) with stubbed executor.
- internal/turnrunner/live.go: RunnerLiveExecutor default impl satisfies
  live.LiveExecutor (compile-time check via
  var _ live.LiveExecutor = (*RunnerLiveExecutor)(nil)).

Phase 1.5 (deferred — separate change):
- Ctrl+V keymap binding, tea.Cmd wrapping of VoiceMode.Toggle, goroutine
  bridging Session.Events() → program.Send(liveEventMsg), header indicator.

Phase 2 (deferred — separate spec):
- malgo / portaudio CGO bindings for mic + speaker, PCM resampling,
  genai.ModalityAudio, gemini-3.1-flash-live-preview.

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

Track F Phase 1 of the ADK v1.3 capability-first design. Ships the
vendor-agnostic voice interfaces + Gemini-backed STT provider + config
plumbing. Per-channel integration is Phase 2.

Added:
- internal/voice/types.go: Audio{MimeType, Data}, STTOptions{Language},
  TTSOptions{Language, Voice}, sentinel errors ErrNotConfigured /
  ErrTTSNotConfigured.
- internal/voice/stt.go: SpeechToText interface.
- internal/voice/tts.go: TextToSpeech interface.
- internal/voice/noop.go: NoOpSTT / NoOpTTS defaults that return the
  sentinel errors so callers can detect disabled-voice and fall back to
  text-only behavior without crashing.
- internal/voice/gemini_provider.go: NewGeminiSTT (Gemini STT via
  genai.Models.GenerateContent with audio Blob + transcribe prompt).
  NewGeminiTTS returns ErrTTSNotConfigured wrapped — Phase 2 will
  implement real Gemini TTS audio output.
- internal/voice/voice_test.go: 8 unit tests covering NoOp,
  nil-client fallback, empty-audio validation, and sentinel-error
  distinctness.

Modified:
- internal/config/types.go: VoiceConfig + VoiceChannelConfig +
  VoiceCostGuard structs; Voice VoiceConfig field on top-level Config.
  Defaults to disabled.

Phase 2 (deferred — separate change):
- Per-channel inbound: Telegram getFile → audio download →
  voice.SpeechToText.Transcribe → inject as text message.
- Per-channel outbound: optional voice.TextToSpeech.Synthesize → sendVoice
  / file upload.
- Real Gemini TTS implementation.

Single-vendor by user decision: Gemini API only. Interface shape exists
so a future swap is not blocked.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Final comprehensive code review (post-merge) flagged two test gaps where
the implementations were correct but the tests didn't exercise the
spec-mandated behavior:

Track C gate: spec §4 Track C said "sample streaming tool yielding N
chunks". TestAdaptStreamingTool_WithStreamingHandlerReturnsStreamingTool
only asserted construction. Added:
- TestStreamingHandler_YieldsExpectedChunks: verifies the iterator yields
  the three expected chunks in order when consumed.
- TestStreamingHandler_StopsOnConsumerCancel: verifies the iterator
  honors consumer early-termination (the contract AdaptStreamingTool's
  timeout-cancel closure relies on).

Track B gate: spec §4 Track B said "an agent can call load_skill and
receive its markdown body. Round-trip test asserts content equality."
TestBuildSkillToolset_ValidSkillReturnsToolset only asserted the tool
count was 3. Added:
- TestSkillSource_RoundTripsSkillContent: writes a SKILL.md fixture,
  builds the upstream skill.NewFileSystemSource, then exercises
  ListFrontmatters + LoadFrontmatter + LoadInstructions to verify the
  metadata round-trips and the markdown body content is recoverable.
  This is the same data path that load_skill / list_skill_resources /
  load_skill_resource use; we test the layer Lango actually owns
  (skill.Source) without needing ADK's internal tool.Context.

These tests close the spec gates without requiring access to ADK's
internal/toolinternal package (which would be unreachable from outside
the ADK module).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two follow-up artifacts captured by the final reviewing-code-changes-with-subagents
pass on feature/adk-v1.3 (10 commits 6db812e..c4d7a37):

1. Cross-reference comments for the dual SkillsDir fields:
   - cfg.Agent.SkillsDir (Track B — ADK SkillToolset, agentskills.io frontmatter)
   - cfg.Skill.SkillsDir (pre-existing — knowledge module's internal registry)
   Both now point at each other so future readers don't conflate them.

2. Spec Status Log gains a "Post-merge follow-ups (non-blocking)" section
   listing 3 known scope expansions/future-phase items intentionally left
   out of this branch:
   - Agent.SkillsDir settings-form coverage (Track B Phase 1.5-equivalent)
   - SaveLiveBlob opt-in surface (Track E Phase 2)
   - End-to-end streaming tool test through ADK RunStream (Track C Phase 1.5)

No production code touched; documentation only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per .claude/CLAUDE.md "Whenever you change core code, update all downstream
artifacts" — update user-facing docs for the new config + capabilities
landed in feature/adk-v1.3 (11 commits):

- docs/configuration.md:
  - New `agent.skillsDir` row (Track B — ADK SkillToolset, agentskills.io)
  - New `## Voice` section (Track F — voice.enabled, sttModel, ttsModel,
    perChannel, costGuard) with Phase 1 / Phase 2 status note
  - Skill section header reorganized to explain the dual subsystems
    (knowledge skill registry vs ADK SkillToolset) and disambiguate
    `skill.skillsDir` from `agent.skillsDir`
- docs/features/skills.md:
  - Admonition at the top explaining the two independent skill systems
    (knowledge registry, this page; ADK SkillToolset, separate)
  - Cross-references to assets/skills/example-skill/ for the ADK side
- docs/features/channels.md:
  - Phase 1 (voice infra shipped) / Phase 2 (per-channel integration
    deferred) status note — honest about what works today vs Phase 2
  - Tells users explicitly that voice.enabled is currently inert until
    per-channel wiring lands

[[capability-honesty]]: docs accurately reflect what's implemented vs
what's deferred. No phantom-feature documentation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@langowarny langowarny self-assigned this May 24, 2026
@langowarny langowarny added bug Something isn't working documentation Improvements or additions to documentation enhancement New feature or request labels May 24, 2026
langowarny and others added 3 commits May 24, 2026 21:50
Resolve every warning so dev → main is gated on a clean lint run:

- unused (9): drop dead wrapper funcs/consts/stubs in
  cli/alerts, cli/metrics, cli/chat/input, cli/p2p/sandbox_test,
  cli/status, plus the now-unused strings import in cli/chat/input.
- ineffassign (5): retire ineffectual writes in tuicore form,
  knowledge retriever, mcp server-connection, p2p firewall, and
  testutil mock-graph-store tests; discard append result with _.
- govet testinggoroutine (1): replace t.Fatal inside a non-test
  goroutine with a buffered channel that the test inspects after
  watchServeSignals returns.
- govet structtag (1): drop the unused json tag on the private
  field in TestRedactConfigGetReflectValue — the redaction logic
  already gates on field.PkgPath, so tag presence was incidental.
- staticcheck (21):
    QF1011 — remove redundant interface type in ADK v1.3 smoke test
    QF1001 — clear double-negation in recovery prompt + testutil
      doc/stream guards
    QF1008 — drop embedded selectors in runledger writethrough
      and docker_runtime test
    S1011 — replace per-element loop with append(slice, batch...)
      in graph admission
    S1031 — drop unnecessary nil checks around map ranges in mcp
      config_loader
    SA4006 — stop reassigning page when the result is never read
      in cockpit mission-control branch tests
    ST1008 — reorder executeExtensionCmd return to (string, int,
      error) and update all 9 call sites
    SA1019 — annotate legacy P2P.KeyDir / Embedding.ProviderID
      regression tests with //nolint:staticcheck + rationale (these
      tests intentionally pin deprecated-field behavior)
    SA1012 — annotate the nil-parent submitCmd test with
      //nolint:staticcheck (code path is the test's subject)

Verified: go build, go vet, golangci-lint, and targeted go test on
all touched packages all clean. README docs-completeness guard
failures in internal/testutil pre-date this work and are tracked
separately.

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

Two requested cleanups so dev → main goes in green on a stock host:

CI
- Remove the `docs-version-check` job from .github/workflows/ci.yml.
  It grep-enforced ADK version strings across docs/ and is no longer
  the gate we want — version sync now lives in go.mod + release notes.

Docs guard tests
- The README was refactored into a landing page (docs(readme): refactor
  into landing page with docs/ navigation); the README-as-CLI-quickref
  layout it guarded no longer exists. Repoint every surviving
  internal/testutil README guard to docs/cli/index.md (or the matching
  docs/cli/<topic>.md) where the actual quick-reference rows now live.
- Drop README halves of `TestArchitectureAndREADMEUseCurrent*` inventory
  guards (Memory, Contract) — the architecture half still pins the
  inventory; the README half pinned a compact form that the new README
  intentionally no longer carries.
- Delete the package/architecture inventory guards that hard-coded the
  old plain-text README package map (`pkg/  # description`). The new
  docs/architecture/project-structure.md uses markdown tables, so these
  guards have no current target and rewriting them is its own task:
    docs_quality_guard_test.go: 19 stale Architecture/README package-
    inventory tests removed.
    smartaccount_docs_inventory_guard_test.go: removed
    (TestSmartAccountDocsUseCurrentCommandInventory only checked the
    deleted README compact form; architecture-doc coverage lives in
    docs_quality_guard_test.go).
- Rewrite `TestREADMEP2PGitSummariesStayTruthful` against the new
  markdown table rows in docs/cli/index.md.
- For multi-target tests (p2p, metrics, provenance), drop the now-stale
  README target rather than letting it shadow real coverage.

Locale-stable git invocations
- internal/p2p/gitbundle: introduce `gitCommand(ctx, args...)` helper
  that forces `LC_ALL=C` + `LANG=C`, and route every git invocation
  through it (runGit, CreateBundle, UnbundleAll, CreateIncrementalBundle,
  VerifyBundle). The "empty bundle" / "missing prerequisite" detection
  was string-matching English stderr from git, which broke on Korean-
  locale hosts (저장소에 필수적인 다음 커밋이 없습니다 etc). With C
  locale forced, the existing English string checks are stable.

Verified: go build, go vet, golangci-lint (still 0 issues), and
go test ./... all clean on a Korean-locale macOS host.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Failures only surfaced under CI's stricter environment (Linux + macOS
runners, `-race`, fresh hosts without dev-machine state). Address each
root cause rather than skip:

1) archtest depends on `rg`
   `internal/archtest/bootstrap_boundary_test.go` shells out to ripgrep
   for the package-boundary scans. CI runners did not have rg installed,
   so all 10 boundary tests failed at "executable file not found in
   $PATH". Add `ripgrep` to both the Linux (`apt-get install`) and
   macOS (`brew install`) dependency steps in .github/workflows/ci.yml,
   and also to the lint job.

2) handshake test pinned `time.Local`
   `time.Unix(1_700_000_000, 0)` returns a Time with Location set to
   `time.Local`. The handshake responder caches the bundle and the
   round-trip normalizes the Location to `time.UTC`. `assert.Equal`
   compares the *Location pointer*, so Local≠UTC even when both
   evaluate to the same instant on a UTC host. Pin the test's CreatedAt
   to `.UTC()` so both sides agree on Location.

3) handshake `Initiate`/`HandleIncoming` could hang past their timeout
   `TestInitiateCachesV2BundleAndRegistersAlias` hit a 10-minute test
   timeout in CI because `json.Decoder.Decode` does not observe
   ctx cancellation. When the peer (real or simulated) stalls,
   `cfg.Timeout` was effectively ignored and the test wedged on
   `net.Pipe.read`. Set `stream.SetDeadline(deadline)` from the
   `WithTimeout` deadline in both Initiate and HandleIncoming so the
   underlying network read terminates with the rest of the handshake.
   Best-effort: ignore errors from SetDeadline since not all stream
   implementations support it.

4) `internal/tools/exec` PTY race on timeout
   `RunWithPTY` launches a goroutine that `io.Copy`s into `output
   bytes.Buffer`, then on `ctx.Done()` reads `output.String()` while
   the goroutine may still be writing — race-detector flags
   `Buffer.grow()` vs `Buffer.String()` concurrent access. Wait on the
   copy goroutine's `done` channel after SIGTERM before reading the
   buffer.

5) glamour `BlockStack` race in chat markdown renderer
   `internal/cli/chat/markdown.go` cached a single `glamour.TermRenderer`
   in a package-level variable on the assumption that bubbletea
   serializes message dispatch. Tests (notably parallel
   `cli/cockpit/pages` tests) invoke `renderMarkdown` from multiple
   goroutines, and glamour's internal `BlockStack` is not safe for
   concurrent use. Guard the cached renderer + the render call with a
   `sync.Mutex`.

6) gitbundle `commit-tree` lacks author identity on fresh CI hosts
   `branch.go` merge runs `git commit-tree` against a bare repo. Bare
   repos do not pick up a user.name/user.email config, and CI runners
   have no global git identity, so the command fails with
   "Author identity unknown". Pass `GIT_AUTHOR_NAME`/`GIT_AUTHOR_EMAIL`
   and the matching `GIT_COMMITTER_*` env vars (using a stable
   `Lango Bundle <bundle@lango.local>` identity) so commit-tree always
   has the metadata it needs.

7) streamx `TestParallelReadOnlyExecutor_ContextCancellation` deadlock
   The test wired two parallel handlers to send a "started" signal on
   an *unbuffered* channel before waiting on `<-ctx.Done()`. The test
   reads `started` exactly once, so the second handler's send blocks
   forever and never reaches the ctx.Done() arm — even after cancel().
   Under high race-detector overhead the test sometimes hung the full
   suite for >10 minutes. Buffer `started` to the handler count.

Verified: targeted `go test -race ./internal/streamx/... ./internal/
tools/exec/... ./internal/p2p/handshake/... ./internal/p2p/gitbundle/
... ./internal/cli/cockpit/pages/... ./internal/cli/chat/... ./internal
/archtest/...` clean; golangci-lint still 0 issues.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@langowarny langowarny merged commit 006c775 into main May 24, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant