From b9f1f68e95af2f946b27a314af11cae0eeea229e Mon Sep 17 00:00:00 2001 From: wentaoyuan Date: Wed, 15 Apr 2026 16:31:39 +0800 Subject: [PATCH 1/2] docs: rewrite AGENTS.md with verified repo-specific guidance Replaced the previous generic Technical Context / Key Constraints structure with high-signal facts an agent would otherwise miss: - Exact developer commands (bun install/test/build) and the absence of any linter/formatter/typecheck scripts - Full architecture listing including session.ts and version.ts - Interceptor recursion guard (inEmit), Bun AsyncLocalStorage caveat, gRPC-only trace constraint, and silent-inactive behavior - Complete service.name resolution chain and config file placeholders - Test location, runner, and coverage scope - CI publish workflow trigger and steps - Design spec references including 011-align-bat-otel --- AGENTS.md | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..c1c2832 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,71 @@ +# AGENTS.md + +## What This Project Is + +**opencode-otel** — an OpenCode npm plugin that forwards runtime stderr logs to any OTLP-compatible log collector via gRPC or HTTP. Business-level observability (session traces, tool calls) is handled separately by opencode-plugin-langfuse; this plugin only ships runtime logs and correlates them to sessions via traceId. + +## Commands + +```bash +bun install # install deps (uses bun.lock) +bun test # run all unit tests +bun run build # tsup → dist/index.js (ESM + .d.ts) +``` + +There is no linter, formatter, or typecheck script configured. The build command is the only verification beyond tests. `prepublishOnly` runs the build automatically. + +## Tech Stack + +- **TypeScript 5.5+** on **Bun** runtime — not Node +- **tsup** builds ESM only; configured inline in `package.json` (no tsup.config file) +- `@opencode-ai/plugin@>=1.1.0` is a **peer dependency** — must be external in builds +- `tsconfig.json` uses `"noEmit": true` with strict mode; the build is handled entirely by tsup +- No `.eslintrc`, `.prettierrc`, or `biome.json` exists + +## Architecture + +```text +src/index.ts ← plugin entry, orchestration only (returns {event} hook) +src/config.ts ← env vars > otel.json config file > defaults +src/provider.ts ← LoggerProvider (gRPC or HTTP) + BasicTracerProvider (gRPC only) +src/interceptor.ts ← monkey-patch process.stderr.write, line buffering, severity parsing +src/session.ts ← Map for log-to-session trace correlation +src/shutdown.ts ← graceful flush on beforeExit/SIGTERM/SIGINT +src/version.ts ← reads version from package.json at runtime (never hard-coded) +``` + +## Key Constraints + +- **Cannot modify OpenCode source** — integration via npm plugin `event` hook only +- **Monkey-patch `process.stderr.write`** — interceptor has a recursion guard (`inEmit` flag); if emit callback writes to stderr it is silently skipped +- **Session tracking uses a module-level Map, not AsyncLocalStorage** — Bun's AsyncLocalStorage is broken; `session.ts` tracks active session via a plain variable +- **Trace exporter only supports gRPC** — if a non-gRPC traces protocol is configured, trace export is disabled with a warning +- **Plugin goes inactive silently** if `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` is not set (no interceptor installed) + +## Config Precedence + +For most settings: **env var → otel.json config file → default** + +`service.name` has a deeper chain: `OTEL_SERVICE_NAME` → `OTEL_RESOURCE_ATTRIBUTES[service.name]` → `otel.json serviceName` → `PAAS_APP_APPID` → `"opencode-agent"` + +Config file default path: `~/.config/opencode/plugins/otel.json` (override with `OTEL_PLUGIN_CONFIG_PATH`). The config file supports `${ENV_VAR}` placeholders. + +## Testing + +- Tests live in `tests/unit/` — three files covering config, provider, and interceptor +- Tests manipulate `process.env` directly; they run in Bun's test runner (`bun test`) +- Provider tests validate the candidate-based resource attribute resolution and BAT runtime metadata backfilling +- No integration tests, no test services, no fixtures needed + +## CI / Publishing + +- **publish.yml**: triggered by `v*` tags → `bun install` → `bun test` → `bun run build` → `npm publish` (requires `NPM_TOKEN` secret) +- Two Claude Code review workflows exist for PR automation (read-only) + +## Specs + +Design documents are in `specs/`. Key references: +- `specs/constitution.md` — project principles +- `specs/010-stderr-log-forwarder/` — current feature spec +- `specs/011-align-bat-otel/` — BAT OTEL alignment spec +- Directory-level `CLAUDE.md` files throughout the repo define ownership boundaries for Claude Code sessions From 2319d5c106ad6457cde622043c94a8a28ebcbe82 Mon Sep 17 00:00:00 2001 From: wentaoyuan Date: Wed, 15 Apr 2026 16:35:43 +0800 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20address=20review=20=E2=80=94=20fix?= =?UTF-8?q?=20AsyncLocalStorage=20claim=20and=20inactive=20behavior=20word?= =?UTF-8?q?ing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove claim that Bun AsyncLocalStorage is 'broken'; describe the actual design choice (module-level Map + plain variable) and note the concurrent session interleaving consequence - Replace 'silently inactive' with accurate description: logs a status message via app logger but does not install the interceptor --- AGENTS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c1c2832..cef37d7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -38,9 +38,9 @@ src/version.ts ← reads version from package.json at runtime (never hard-c - **Cannot modify OpenCode source** — integration via npm plugin `event` hook only - **Monkey-patch `process.stderr.write`** — interceptor has a recursion guard (`inEmit` flag); if emit callback writes to stderr it is silently skipped -- **Session tracking uses a module-level Map, not AsyncLocalStorage** — Bun's AsyncLocalStorage is broken; `session.ts` tracks active session via a plain variable +- **Session tracking uses a module-level Map, not AsyncLocalStorage** — `session.ts` tracks the active session via a plain variable (`activeSessionId`); concurrent session interleaving will mis-tag logs - **Trace exporter only supports gRPC** — if a non-gRPC traces protocol is configured, trace export is disabled with a warning -- **Plugin goes inactive silently** if `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` is not set (no interceptor installed) +- **Plugin stays inactive** if `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` is not set — logs a status message via the app logger but does not install the interceptor ## Config Precedence