Add agent-first observability to code — structured logs, health endpoints, failure-state persistence, and explicit failure modes — so the next agent hitting a p
复制下面这句话,粘贴给 Claude Code、Codex、Cursor 等 AI 编程工具,它会读取安装说明并在你确认后完成安装。
请阅读 https://ai.atlankj.com/install/asset/gh-observability-39f43047b0c1 ,按照其中的说明把「observability」安装到你(当前 AI 工具)中。执行前先告诉我将运行的命令和写入的位置,等我确认。
查看 AI 将读取的安装说明正在读取 GitHub 原文…
内容来自 GitHub 原始文件,由原作者维护。在 GitHub 查看
This skill is the thinking process for adding it. Not "add logs everywhere" — add the right signals at the right decision points.
Invocation points:
<core_principle>
LOG DECISIONS, NOT ACTIVITY. "Entering function X" is noise. "Dispatched unit slice/S02 after guard check passed because status=pending" is signal. Every log line should answer a question a future debugger will ask.
FAIL LOUDLY AND PERSIST THE REASON. Silent try/catch that returns undefined is an anti-pattern. If something fails, the failure state needs to be somewhere a fresh agent can find it — a JSONL, a status file, a health endpoint.
OBSERVABILITY IS NOT FREE. Every log allocation, every metric, every health check costs CPU and disk. Add only what you would actually read. </core_principle>
Before instrumenting, list what can go wrong:
This map tells you where to instrument. Don't instrument uniformly — instrument at the decision points where these failures would manifest.
For each decision the code makes that could plausibly go wrong later:
Format:
log.info({
event: "unit-dispatched",
unitType: "slice",
unitId: "S02",
reason: "pending",
attempt: 1,
flowId,
});
Use the project's existing logger if one exists. In gsd-2, follow the patterns in src/resources/extensions/gsd/activity-log.ts and src/resources/extensions/gsd/journal.ts — structured JSONL, one event per line, with ts, event, and domain-specific fields.
Avoid:
console.log("here") — what does "here" mean in six months?When something fails in a way the caller can't immediately handle, write the failure state to disk:
await writeAtomically(
resolve(".gsd/runtime/last-error.json"),
JSON.stringify({
ts: new Date().toISOString(),
phase: "execute",
unitId,
error: { message, stack, code },
retryCount,
})
);
A fresh agent reading .gsd/runtime/ sees what happened last, what was retried, and where the process stopped. Pattern exists already in gsd-2 — reuse the atomic-write.ts helpers and the .gsd/runtime/ and .gsd/forensics/ directories.
For long-running processes:
{status: "healthy" | "degraded" | "down", ...diagnostics}.STATE.md and the health widget. In a server, it's /internal/status with last 10 request summaries.Don't build a metrics empire. Build exactly what you'd check at 3am.
Replace silent handling with explicit:
// Bad
try {
return await db.getUser(id);
} catch {
return null;
}
// Good
try {
return await db.getUser(id);
} catch (err) {
log.error({ event: "db-getuser-failed", userId: id, err: serializeError(err) });
throw new DatabaseError("Failed to load user", { cause: err, userId: id });
}
The caller now knows the failure happened, gets an error type it can branch on, and a log line exists for forensics.
Before shipping, cull the ad-hoc instrumentation you used while debugging. Keep only:
Drop:
console.log debug linesThe system prompt says it plainly: "Remove noisy one-off instrumentation before finishing unless it provides durable diagnostic value."
Pick one plausible failure mode from Step 1 and simulate it (inject an error, point at a missing file, break a dependency). Confirm:
If any signal is missing, add it — that's the gap this skill exists to catch.
<anti_patterns>
"Processing user 42 now" vs {event: "user-process-start", userId: 42} — the latter is queryable, the former is not.catch {} or catch (err) { /* ignore */ } without a log is a deferred production incident.</anti_patterns>
<success_criteria>
.gsd/runtime/, /var/log/, a status file).try/catch swallowing errors.</success_criteria>