forge-agent-lens-for-claude-code
AGENTS.md9 starsChanged 24 days agoApache-2.0View on GitHub
Notes for agents working on this repo. Everything below was verified against live traces, not inferred. Each of these looks like a bug and is not. Check here before filing one. - **genai.usage.inputtokens sums three fields.** buildUsage in src/genaiSpans.ts adds Anthropic's inputtokens, cachereadinputtokens and cachecreationinput_tokens, because OTel defines input tokens as the total prompt. The cache fields are also emitted separately as the breakdown, which reads like double counting but is not. See the comment and the semconv link at…
- Reads credentials
We split this AGENTS.md into the rules it gives an agent
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 74
Read through Session.parseTranscript(), which treats an unreadable transcript as empty.
≈ “Common” is an estimate: at least 1 in 5 of the 36,368 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
AGENTS.md
Notes for agents working on this repo. Everything below was verified against live traces, not inferred.
Settled decisions: do not "fix" these
Each of these looks like a bug and is not. Check here before filing one.
gen_ai.usage.input_tokenssums three fields.buildUsageinsrc/genaiSpans.tsadds Anthropic'sinput_tokens,cache_read_input_tokensandcache_creation_input_tokens, because OTel defines input tokens as the total prompt. The cache fields are also emitted separately as the breakdown, which reads like double counting but is not. See the comment and the semconv link at the function.- A turn's last
chatspan appears only at Stop. Earlier ones are sent at rootPreToolUse(Session.emitCompletedResponses). Claude Code writes the rest of a response after running its tool, and a sent span cannot be amended. - The first
chatspan carries the user prompt asgen_ai.input.messages, not the full model input, because the root span exports only at turn close. status_code: UNSETon a successful span is correct. OTel reservesOkfor explicit developer intent; UNSET is the success default. Failures already route throughspan.end({ error }), which sets ERROR pluserror.type.
Verifying traces
Agent spans go to the W&B Agents store at
https://trace.wandb.ai/agents/otel/v1/traces, which is separate from classic
Weave calls. The weave-calls MCP tools read 0 from it even when the project is
full, so query the agents endpoints directly:
curl -s -u "api:$WANDB_API_KEY" -X POST https://trace.wandb.ai/agents/spans/query \
-H 'Content-Type: application/json' \
-d '{"project_id":"<entity>/<project>","limit":50,"include_details":true,
"sort_by":[{"field":"started_at","direction":"desc"}]}'Three request-shape traps that produce believable but wrong conclusions:
sort_bymust be a list of{field, direction}. A bare string 422s.- Tool payload columns (
tool_call_arguments,tool_call_result,tool_type) populate only withinclude_details: true. Without it they read empty, which looks exactly like the client failing to record them. raw_span_dump.attributescomes back nested, because the server parses JSON-string attribute values. Recursively flattening it turns one wire attribute (gen_ai.tool.call.arguments) into many dotted sub-keys (gen_ai.tool.call.arguments.command). Do not infer the wire format from a flattened view.
Span names carry their target (invoke_agent claude-code, execute_tool Bash,
chat <model>), so select spans by operation_name, not span_name.
Here tracing is imported from @coreweave/forge-sdk/agentlens/tracing.
For an end-to-end check, drive the real Daemon with the real OTLP exporter
(tracing.init(project) with no span-processor override), route synthetic hook
events through routeEvent, await tracing.forceFlush(), then read the spans back
with the query above. tests/helpers.ts has the in-memory equivalent for unit
tests.
Export failures are invisible by default: a bad key or an unwritable project
drops every span while hooks keep succeeding and the log rotates. Before trusting
an empty project, grep the daemon log for OTLPExporterError. Surfacing the last
rejection through forge-agent-lens-for-claude-code status is in flight on
feat/status-export-health.
Transcript lifecycle
Claude Code creates the session transcript after SessionStart, so any hook
that fires early can see a path that does not exist yet. TranscriptFile.getFd()
throws in that window and caches the fd once it succeeds.
Read through Session.parseTranscript(), which treats an unreadable transcript
as empty. A bare parseSessionFd(this.transcript.getFd()) in a turn-creation
path lets ENOENT escape to the dispatcher and drops the whole hook, which is how
the first prompt of every new session went untraced.
Config
resolveApiKey and resolveProject in src/config.ts prefer the environment
over settings.json. A revoked key in settings.json therefore stays hidden as
long as WANDB_API_KEY is exported, and only surfaces for anything launched
without it. forge-agent-lens-for-claude-code status prints which source won.
The daemon inherits the environment of whatever spawned it, so a daemon started
from a shell without WANDB_API_KEY resolves a different key than your terminal
does. ps eww -p <pid> shows what it actually got.
Known gaps
- No content gate or redactor. Tool arguments, tool results, prompts and
model output are written to spans raw. A
Bashstep that prints a credential puts that credential ingen_ai.tool.call.result.stdout. Any payload capture added here inherits that exposure until a gate lands. chatspans carrygen_ai.agent.namebut notgen_ai.agent.version.agent_nameis the join key, so attribution works without it.
Checks
npm run check runs tsc then the full suite. Run it before pushing. Tests live
in tests/ mirroring src/, grouped per concern rather than per fix.
Other versions3 projects use a version of this.
Word for word · 3
- wandb/forge-agent-lens-for-claude-code★ 9100% same
- wandb/forge-claude-code★ 9100% same
- wandb/weave-claude-code★ 9100% same
Edited · 0
None found.
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 28,693 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository4 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- forge-configSkill
- forge-installSkill
- forge-statusSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

