From 28205bf36adfbfc1f7a9cc4fa5df34579c4d2be1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 14:25:05 +0000 Subject: [PATCH 1/3] Initial plan From 312473583cbe653d1848876e868879e84ea94f2d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 14:31:13 +0000 Subject: [PATCH 2/3] initial plan: create pkg/syncutil/README.md and update dependencies Agent-Logs-Url: https://github.com/github/gh-aw/sessions/db670686-3855-4da4-a456-cdf483b377b8 Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- .github/workflows/smoke-otel-backends.lock.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/smoke-otel-backends.lock.yml b/.github/workflows/smoke-otel-backends.lock.yml index c5f41ee2366..a581ff20194 100644 --- a/.github/workflows/smoke-otel-backends.lock.yml +++ b/.github/workflows/smoke-otel-backends.lock.yml @@ -773,7 +773,7 @@ jobs: "url": "https://mcp.datadoghq.com/api/unstable/mcp-server/mcp?toolsets=core", "headers": { "DD_API_KEY": "\${DD_API_KEY}", - "DD_APPLICATION_KEY": "\${DD_APPLICATION_KEY}", + "DD_APPLICATION_KEY": "\${DD_APP_KEY}", "DD_SITE": "\${DD_SITE}" }, "tools": [ From 132efaf712b9ef77026f21d2426b2063fee8766e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 19 May 2026 14:32:35 +0000 Subject: [PATCH 3/3] docs: create pkg/syncutil/README.md and update cross-package dependencies Agent-Logs-Url: https://github.com/github/gh-aw/sessions/db670686-3855-4da4-a456-cdf483b377b8 Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- pkg/cli/README.md | 1 + pkg/syncutil/README.md | 65 ++++++++++++++++++++++++++++++++++++++++++ pkg/workflow/README.md | 1 + 3 files changed, 67 insertions(+) create mode 100644 pkg/syncutil/README.md diff --git a/pkg/cli/README.md b/pkg/cli/README.md index 20eba81acc6..4a6023a89af 100644 --- a/pkg/cli/README.md +++ b/pkg/cli/README.md @@ -478,6 +478,7 @@ err := cli.RunHealth(cli.HealthConfig{ - `github.com/github/gh-aw/pkg/gitutil` — Git and GitHub CLI helpers - `github.com/github/gh-aw/pkg/repoutil` — repository name parsing and normalization - `github.com/github/gh-aw/pkg/stringutil` — string manipulation and sanitization utilities +- `github.com/github/gh-aw/pkg/syncutil` — thread-safe one-shot caching (used for repository slug lookup) **External**: - `github.com/spf13/cobra` — CLI framework diff --git a/pkg/syncutil/README.md b/pkg/syncutil/README.md new file mode 100644 index 00000000000..bf8859127a4 --- /dev/null +++ b/pkg/syncutil/README.md @@ -0,0 +1,65 @@ +# syncutil Package + +The `syncutil` package provides thread-safe synchronization utilities for concurrent operations. + +## Overview + +This package provides generic types for common concurrency patterns with zero-allocation caching. It is designed for situations where an expensive or fallible operation should be executed at most once, with subsequent callers receiving the cached result. + +## Public API + +### Types + +| Symbol | Kind | Description | +|--------|------|-------------| +| `OnceLoader[T]` | struct | Caches the result of an expensive, fallible one-shot fetch; safe for concurrent use | + +### Methods on `OnceLoader[T]` + +| Method | Signature | Description | +|--------|-----------|-------------| +| `Get` | `func (o *OnceLoader[T]) Get(loader func() (T, error)) (T, error)` | Returns the cached result, invoking `loader` exactly once | +| `Reset` | `func (o *OnceLoader[T]) Reset()` | Clears the cached result and error so that the next `Get` call re-invokes `loader` | + +## Usage Examples + +```go +import "github.com/github/gh-aw/pkg/syncutil" + +var cache syncutil.OnceLoader[string] + +// loader is called only once; subsequent calls return the cached value +value, err := cache.Get(func() (string, error) { + return expensiveOperation() +}) + +// Reset allows re-fetching the value on the next Get call +cache.Reset() +``` + +**Typical usage as a package-level cache**: + +```go +var currentRepoSlugCache syncutil.OnceLoader[string] + +func getCurrentRepoSlug() (string, error) { + return currentRepoSlugCache.Get(func() (string, error) { + return fetchRepoSlugFromGitHub() + }) +} +``` + +## Design Notes + +- The internal mutex ensures that `loader` is invoked at most once, even when multiple goroutines call `Get` concurrently. +- If `loader` returns an error, the error is cached alongside the zero value of `T`; subsequent calls return the same error without re-invoking `loader`. +- `Reset` acquires the same mutex, making it safe to call concurrently with `Get`. +- The zero value of `OnceLoader[T]` is ready to use; no constructor is needed. + +## Dependencies + +This package has no internal or external dependencies beyond the Go standard library (`sync`). + +--- + +*This specification is automatically maintained by the [spec-extractor](../../.github/workflows/spec-extractor.md) workflow.* diff --git a/pkg/workflow/README.md b/pkg/workflow/README.md index 479c627f49d..7ae9fcebbae 100644 --- a/pkg/workflow/README.md +++ b/pkg/workflow/README.md @@ -518,6 +518,7 @@ pkg/workflow ── FrontmatterConfig (typed structs) - `github.com/github/gh-aw/pkg/typeutil` — safe type conversions - `github.com/github/gh-aw/pkg/tty` — terminal capability detection - `github.com/github/gh-aw/pkg/stringutil`, `github.com/github/gh-aw/pkg/fileutil`, `github.com/github/gh-aw/pkg/gitutil`, `github.com/github/gh-aw/pkg/sliceutil` — utilities +- `github.com/github/gh-aw/pkg/syncutil` — thread-safe one-shot caching (used for repository feature cache) - `github.com/github/gh-aw/pkg/types` — shared MCP types **External**: