skill-observability
Defines the skill observability event contract (event.schema.json) and emit substrate (obs-emit.mjs). The SSOT for the measurement layer of the skill improvement loop. Referenced by other skills and hooks.
- License 许可
- MIT
- 所属仓库
- ozzy-labs/skills
- 最近更新
- 2026-07-13
- Stars
- ★ 0
- Forks
- 0
01 前言 Front Matter
- name
- skill-observability
- description
- Defines the skill observability event contract (event.schema.json) and emit substrate (obs-emit.mjs). The SSOT for the measurement layer of the skill improvement loop. Referenced by other skills and hooks.
- user-invocable
- false
02 资源文件 3
- 参考文档event.schema.json3.3 KB
- 脚本obs-derive.mjs8.6 KB
- 脚本obs-emit.mjs10.7 KB
03 SKILL.md 正文
skill-observability - Measurement event contract and emit substrate
A referenced companion that defines the contract and tools underpinning the measurement layer of the skill improvement loop (install → use → reflect). Provides the SSOT for the shape of events (schema) and the convention for writing them (emit), along with the trace-derivation hook (obs-derive.mjs) that derives invocations after the fact from the transcript. Aggregation (/skill-metrics) and reflection are built separately on top of this contract.
Principles
- Trace derivation as the main axis: Measurement is derived after the fact, as much as possible, from
gh/gitground truth and transcript traces (avoiding self-report bias). Inline emit from within a skill prompt is secondary, limited to semantic signals that don't show up in traces (fallback, HITL rejection, loop cap reached, etc.). - fail-open: A failed emit must not stop the skill being measured.
obs-emit.mjsnever throws — whether absent, failing, or hitting a validation error — it prints a warning to stderr and exits 0. - privacy (strictest mode): Events are metadata only.
event.schema.json'sadditionalProperties: falsemechanically rejects unknown fields such as payload / diff / token / path. Repo identifiers are never stored raw — only a hash (repo_hash). Sending (reflection) is always explicit opt-in / HITL (this skill has no send path of its own). - Count-based reasoning: Because a single author's low-frequency data volume doesn't reach statistical significance, this contract does not force rates or confidence intervals. The aggregation side is built primarily around counts + notable events.
Event log
~/.agents/observability/events.jsonl # append-only, 1 line per event, self-contained without OTel dependency
HOME-anchored (outside the skills dir). Survives a dogfood mirror rebuild. Readable by any consumer / hook.
Event contract (event.schema.json is the SSOT)
event.schema.json (a sibling of this skill) is the sole SSOT. Both obs-emit.mjs and the tests validate by reading this file, so doc and code never drift apart. Field names lean toward the shape of the OpenTelemetry GenAI semantic conventions (skill≈gen_ai.agent.name / operation≈gen_ai.operation.name). Since the conventions are experimental, this skill does not couple tightly to them.
Required fields: schema_version(=1) / ts(ISO 8601) / adapter / session_id / skill / event.
Types of event:
| event | Purpose | Additional required field |
|---|---|---|
start |
Skill invocation | — |
phase |
Phase transition (implement / ship / review, etc.) | — |
outcome |
End state | status ∈ {completed, aborted, fallback} |
signal |
Semantic counter (a transition that doesn't show up in traces) | name (fixed vocabulary) |
heartbeat |
Records that "observation ran" (prevents misreading absent data as success) | — |
Fixed vocabulary (initial) for signal.name: review.loop_iter / review.deep_to_quick_fallback / usage_guard.fail_open / hitl.rejected / loop.hit_cap.
privacy: repo_hash is 12-digit hex (a sha256 prefix) only. The contract makes it impossible to write raw repo names, cwd, or raw PR numbers (additionalProperties: false).
Emit substrate (obs-emit.mjs)
obs-emit.mjs (sibling, a CLI that runs on all adapters) is the build→validate→append write primitive. Both the trace-derivation hook and inline emit ultimately go through this to append a single event.
# Examples
node obs-emit.mjs --skill=drive --event=outcome --status=completed
node obs-emit.mjs --skill=review --event=signal --name=review.loop_iter --value=2
node obs-emit.mjs --skill=drive --event=heartbeat
node obs-emit.mjs --skill=drive --event=outcome --status=merged --repo="$(git rev-parse --show-toplevel)"
Arguments: --skill / --event (effectively required), --status / --name / --value / --phase / --operation / --reason / --run (optional), --repo (hashed and stored in repo_hash), --adapter / --session (resolved from env by default).
Any event that fails validation, and any other failure, is not appended — it warns and exits 0 (fail-open). Does not break callers chained with &&.
Trace-derivation hook (obs-derive.mjs)
obs-derive.mjs (sibling, a SessionEnd hook) is the primary capture path. After a session ends, it reads the transcript, derives after the fact which skill fired, and records it via the obs-emit substrate. Because it reconstructs from traces rather than asking the model for a mid-run self-report, it avoids self-report bias (the worst-case runs that abort being exactly the ones that drop the emit).
The reliable core it derives:
- One
heartbeatper session (a record that "observation ran." Makes an empty window distinguishable between "zero invocations" and "the hook didn't fire"). - One
startper skill invocation found in the transcript. 2 channels:- A model-invoked
Skilltool_use →operation: invoke_agent - A user-entered
/slash-command→operation: slash_command(only for skills that actually exist. Built-in commands with no sibling skill dir, such as/clear/compact, are excluded to prevent data contamination)
- A model-invoked
The skill's arguments (which may be sensitive) are not recorded (only the skill name and channel).
Deferred (not derived by this hook): the merge/abort outcome. The merge state at session end is not yet settled — it requires session→PR linkage plus deferred re-evaluation, and abort inference ("ended without a PR") is hard to distinguish from a human interruption or an idempotent resume, and is noisy. To keep things reliable and low-noise, this is split off into a separate increment.
Enabling the SessionEnd hook (manual opt-in)
This repo does not distribute settings/hooks (same policy as the usage-guard hook). Add a SessionEnd entry to ~/.claude/settings.json (or settings.local.json). command must be the absolute path to obs-derive.mjs (differs between user-scope ~/.claude/skills/skill-observability/... and dogfood <repo>/.claude/skills/skill-observability/... — fill in your own path):
{
"hooks": {
"SessionEnd": [
{
"hooks": [
{ "type": "command", "command": "node /home/<you>/.claude/skills/skill-observability/obs-derive.mjs" }
]
}
]
}
}
Receives the SessionEnd JSON on stdin (session_id / transcript_path / cwd / reason), always exits 0, and stays lightweight (lines are pre-filtered by substring before JSON.parse).
Scope of application
This skill provides the contract + emit substrate + trace-derivation hook (invocation capture). Layers built on top of this contract (capture → aggregate → reflect → consume):
/skill-metrics(aggregation, shipped): read-only aggregation of events.jsonl into counts + notable events.- Reflection channel (reflection, shipped): the metrics-primed version of
lessons-triage. Privacy-cleansed rollups are reflected into a backlog-pointer issue via HITL (sending is opt-in). backlog(consumption, shipped): passes that issue todriveas a priority index.- outcome derivation (separate PR, pending): folds merge/abort state into the rollup using
gh/gitmerge ground truth + session→PR linkage (the next increment of the trace-derivation hook).
Notes
- Does not read
.envfiles. - Events must not include verbatim logs, secrets, or raw private repo names/paths/PR values (the schema mechanically rejects these).
04 同仓库更多 Skills
verify
An integrated skill that runs the combined validation of build / typecheck / test / lint in one shot. The `verify.mjs` engine auto-discovers validation commands via a discovery chain (AGENTS.md's 「検証」 section → package.json scripts → justfile/Makefile/lefthook → language heuristics), executes them serially with their source attached, and returns a result summary. If a higher-priority stage produces a hit, only that stage is executed (no crossing stages).
usage-guard
Monitors Claude Code's Usage Limit (5-hour = Current / weekly = Weekly), pauses work when it exceeds 95%, and automatically resumes once the reset restores headroom. Bundles an engine form that a caller such as drive Reads at a checkpoint, and a standalone form, `/usage-guard "<continuation command>"`, that guards arbitrary work. Claude-only (depends on the OAuth usage-rate endpoint + ScheduleWakeup).
topics
Selects GitHub topic candidates by having the `topics.mjs` engine perform constraint validation, popularity measurement, broad+narrow / singular-plural comparison, and hardcoded ozzy-labs conventions, then applies them via `gh repo edit --add-topic` following the policy's `externally-visible` gate (default batch-confirm). Scope is limited to use within ozzy-labs.
skill-metrics
Aggregates the local observability event log (~/.agents/observability/events.jsonl) and presents, read-only, per-skill invocation counts and notable events (fallback / HITL rejection / loop cap reached / abort). Fires on "show me the skill metrics," "aggregate the observability results," "which skills get used the most?" Does not send anything.
ship
Runs verification, commit, and PR creation all at once. An integrated pipeline that runs verify (build + type + test + lint) → commit → PR creation in sequence against the changes.
review
Reviews code changes or PRs across 11 perspectives, reporting via JSON structured output plus a human-readable report. Supports switching between quick and deep modes. Takes a PR number or working-tree diff as input.