← Go: Distributed SystemsCONTENT HISTORY

Update to Go: Distributed Systems

Snapshot Sep 30, 2026 · 23:15 UTC · version 0.4.0

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "description": "Use for in-process Go concurrency ownership, cancellation, races, leaks, synchronization, and bounds. Do not use for broker semantics.",
  "included_files": [
    {
      "relative_path": "agents/openai.yaml",
      "size_in_bytes": 274
    },
    {
      "relative_path": "evals.json",
      "size_in_bytes": 3867
    },
    {
      "relative_path": "references/lifecycles.md",
      "size_in_bytes": 2284
    },
    {
      "relative_path": "references/supervision-and-failure.md",
      "size_in_bytes": 3299
    },
    {
      "relative_path": "references/synchronization.md",
      "size_in_bytes": 2052
    },
    {
      "relative_path": "skill.json",
      "size_in_bytes": 2358
    }
  ],
  "name": "go-concurrency-lifecycle",
  "skill_md_contents": "---\nname: go-concurrency-lifecycle\ndescription: \"Use for in-process Go concurrency ownership, cancellation, races, leaks, synchronization, and bounds. Do not use for broker semantics.\"\nlicense: Apache-2.0\ncompatibility: \"Go 1.24 or newer. Guidance targets the stable Go 1.25 and 1.26 families and degrades to older repository versions when required.\"\n---\n\n# Go concurrency lifecycle\n\nMake concurrent code explain who owns each goroutine, what can block it, and how it terminates. Treat channels, mutexes, atomics, and contexts as tools with different contracts—not as a hierarchy of idiomatic preference.\n\n## Establish the contract\n\nInspect the call path and repository conventions before proposing a pattern. Write down only the invariants relevant to the change:\n\n1. **Ownership:** which component starts the goroutine and waits for or stops it?\n2. **Lifetime:** is it request-scoped, operation-scoped, component-scoped, or process-scoped?\n3. **Termination:** enumerate every blocking point and the event that releases it.\n4. **Failure:** where does an error go, and which sibling work should it cancel?\n5. **Capacity:** what bounds goroutines, queued work, memory, and downstream concurrency?\n6. **State:** which data is shared, and what establishes happens-before ordering?\n\nDo not add concurrency until the expected latency or ownership benefit justifies the extra state space.\n\n## Choose the synchronization mechanism\n\nChoose from the invariant, not a slogan:\n\n| Need | Default starting point |\n| --- | --- |\n| Protect a small in-memory invariant | `sync.Mutex` guarding the data |\n| Publish independent read-mostly snapshots | immutable value plus `atomic.Pointer` |\n| Transfer ownership or coordinate a stream | channel with documented producer and close owner |\n| Wait for a fixed set of goroutines | `sync.WaitGroup` or an existing repository error group |\n| Cancel work derived from an operation | propagated `context.Context` |\n| Bound parallel calls | fixed worker count or semaphore acquired before spawning |\n\nChannels do not make shared state disappear. Mutexes do not make lifecycle disappear. A buffer is capacity, not correctness.\n\nRead [references/synchronization.md](references/synchronization.md) when selecting between mutexes, atomics, and channels or when proving publication safety.\n\n## Design the lifecycle\n\n### Operation-scoped work\n\n- Accept the caller’s context; do not replace it with `context.Background()`.\n- Derive cancellation only when this layer owns the shorter lifetime, and call the cancel function.\n- Start sibling goroutines only after defining how the first failure affects the others.\n- Wait before returning if the goroutines access operation-owned memory or resources.\n- Preserve the primary error; do not turn expected cancellation of siblings into the reported cause.\n\n### Component-scoped work\n\n- Make `Start`/`Run` and `Stop`/context ownership visible at the component boundary.\n- Decide whether repeated start or stop is invalid, idempotent, or supported; encode that state.\n- Reject new work before draining accepted work during shutdown.\n- Bound shutdown with the caller’s deadline, but do not silently abandon resource owners.\n\n### Detached work\n\nDetached work is valid only when its lifetime, failure reporting, and resource ownership are intentionally process-scoped. A context is not automatically required for a short, non-blocking goroutine; conversely, passing a context does not prevent a leak if blocking operations ignore it.\n\nRead [references/lifecycles.md](references/lifecycles.md) for worker, pipeline, and shutdown patterns. Read [references/supervision-and-failure.md](references/supervision-and-failure.md) when goroutines can fail, panic, cancel siblings, or outlive the caller.\n\n## Bound work before spawning\n\nPrefer admission control before goroutine creation. If the caller owns the operation or must observe failure, join the work and propagate its error; a detached goroutine is valid only under the process-owned contract above. This fragment demonstrates admission only, not a complete request-scoped lifecycle:\n\n```go\nselect {\ncase slots <- struct{}{}:\ncase <-ctx.Done():\n    return ctx.Err()\n}\n\ngo func() {\n    defer func() { <-slots }()\n    process(ctx, item)\n}()\n```\n\nIf a goroutine is created first and then waits for a slot, overload still creates unbounded goroutines. If the caller must observe admission failure, return it synchronously rather than hiding it in the goroutine.\n\nFor a queue, state the overload policy: block, reject, shed oldest/newest, or persist elsewhere. Never infer safety from an arbitrary buffer size.\n\n## Review shared state\n\nFor every mutable value reachable by multiple goroutines:\n\n- identify all readers and writers;\n- name the lock, channel transfer, atomic operation, or immutable publication that orders them;\n- keep the protected invariant adjacent to its synchronization field;\n- do not copy values containing synchronization primitives;\n- do not call unknown or blocking code while holding a lock unless the invariant requires it and the latency is bounded;\n- consider compound invariants—a collection of individually atomic fields can still be inconsistent.\n\nTreat the race detector as runtime evidence over executed paths, not as a proof that unexecuted paths are safe.\n\n## Review channels as protocols\n\nFor each channel, record:\n\n- producer set and consumer set;\n- who closes it, if anyone;\n- whether close means end-of-stream, cancellation, or broadcast;\n- whether a send or receive can remain blocked after a peer exits;\n- the capacity rationale and overload behavior.\n\nOnly a sender with exclusive knowledge that no future send can occur should close a channel. Many channels never need closing because their lifetime is bounded by the owning object.\n\n## Diagnose before changing\n\n- **Race:** start from the reported accesses and find the missing ordering edge.\n- **Deadlock:** capture goroutine states; map locks and channel waits as a wait-for graph.\n- **Leak:** compare goroutine profiles across steady load and after shutdown; locate the first blocking frame owned by the application.\n- **Throughput collapse:** measure queueing, contention, scheduler delay, and downstream saturation before changing worker counts.\n\nGo 1.27’s announced goroutine leak profile is prerelease as of this skill version. Do not prescribe it for stable toolchains until the repository adopts a released version.\n\n## Finish with evidence\n\nWithin the authority of the request:\n\n1. inspect the diff for every new `go`, channel, mutex, atomic, and `WaitGroup` operation;\n2. exercise cancellation, early consumer exit, error, overload, and shutdown paths;\n3. run focused tests under `-race` when the environment supports it;\n4. use deterministic synchronization or `testing/synctest` on Go 1.25+ rather than timing sleeps;\n5. report what was not exercised and why.\n\nDo not claim race freedom, leak freedom, or deadlock freedom solely because a test passed.\n\n## Output contract\n\nFor implementation, make ownership and termination legible in the code without architecture ceremony. For review, report concrete findings with the violated invariant, failure schedule, and smallest sufficient correction. Do not produce a generic concurrency checklist when the code has no concurrent path.\n"
}

SHA-256 of public snapshot: 9de3da32ebe9f3ae3f54ad8238d40c6059d8cec11a274302c30217e9f97d8c79