gsd-headless
Orchestrate GSD (Git Ship Done) projects programmatically via headless CLI. Use when an agent…
Add agent-first observability — structured logs, health endpoints, failure-state persistence, explicit failure modes — so the next agent can diagnose problems unattended. Use when asked to "add logging", "add observability", "add metrics", "make this observable", or when
$ npx -y skills add open-gsd/gsd-pi --skill observability --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/observabilityContext preview
The summary Claude sees to decide when to auto-load this skill.
Add agent-first observability — structured logs, health endpoints, failure-state persistence, explicit failure modes — so the next agent can diagnose problems unattended. Use when asked to "add logging", "add observability", "add metrics", "make this observable", or when
name: observability description: Add agent-first observability — structured logs, health endpoints, failure-state persistence, explicit failure modes — so the next agent can diagnose problems unattended. Use when asked to "add logging", "add observability", "add metrics", "make this observable", or when building/refactoring a subsystem that runs unattended (auto-mode engine, background jobs, servers, watchers).
<objective> Instrument code so that a cold-start agent can understand what happened by reading signals, not by rerunning with extra logging. The deliverable is a set of specific instrumentation additions: structured logs at decision points, health/status surfaces for long-running processes, persisted failure state, and explicit failure modes that don't get swallowed. </objective>
<context> gsd-pi's `VISION.md` lists "agent-first observability" as a principle, and the system prompt calls it out: "A future version of you will land in this codebase with no memory… you add observability because you're the one who'll need it at 3am." gsd-pi already exemplifies this — `activity/*.jsonl`, `journal/*.jsonl`, `metrics.json`, `doctor-history.jsonl` — but new code doesn't get that treatment automatically.
This skill is the thinking process for adding it. Not "add logs everywhere" — add the *right* signals at the *right* decision points.
Invocation points:
</context>
<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>
<process>
Before instrumenting, list what can go wrong:
1. **What inputs could be invalid?** External API responses, user-submitted data, filesystem state, env vars. 2. **What external dependencies could fail?** Network, DB, child processes, filesystem permissions. 3. **What internal invariants could break?** State transitions, lock acquisition, concurrency assumptions. 4. **What silent corruption is possible?** Truncated writes, partial transactions, stale caches.
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-pi, 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:
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-pi — reuse the `atomic-write.ts` helpers and the `.gsd/runtime/` and `.gsd/forensics/` directories.
For long-running processes:
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:
GSD Pi is a local-first coding agent for planning, implementing, verifying, and tracking project work from the command line.
Repo: open-gsd/gsd-pi
Orchestrate GSD (Git Ship Done) projects programmatically via headless CLI. Use when an agent…
Audit and improve web accessibility following WCAG 2.1 guidelines. Use when asked to "improve…
Browser automation CLI for AI agents. Use when interacting with websites — navigating pages,…
Design or review an HTTP/REST/GraphQL API for versioning, pagination, error shapes,…
Apply modern web development best practices for security, compatibility, and code quality.…
Ask a quick side question about your current work without derailing the main task. Answers…