logging-and-comments
Logging and code-comment standard for Python and Bash: library vs app logging, stderr vs stdout, no silent failures, run-level context for pipelines, why-comments, docstrings, constant rationale, TODO format, and the Ruff rules that enforce it. Use when writing or reviewing any script, CLI, hook, pipeline, or library code, or when adding try/except, || true, or 2>/dev/null.
- License 许可
- Apache-2.0
- 最近更新
- 2026-11-11
- Stars
- ★ 6
- Forks
- 1
01 前言 Front Matter
- name
- logging-and-comments
- description
- Logging and code-comment standard for Python and Bash: library vs app logging, stderr vs stdout, no silent failures, run-level context for pipelines, why-comments, docstrings, constant rationale, TODO format, and the Ruff rules that enforce it. Use when writing or reviewing any script, CLI, hook, pipeline, or library code, or when adding try/except, || true, or 2>/dev/null.
02 资源文件 3
- 参考文档references/bash.md4.0 KB
- 参考文档references/python.md5.5 KB
- 参考文档references/ruff-baseline.toml1.2 KB
03 SKILL.md 正文
Logging & Comments Standard
Why this exists: a 2026-10-06 audit of our repos found that the most expensive defects were silent. Examples:
- The
~/.clauderm-guard hook was fail-open with no log line. It never fired, and nothing showed it. - Backfill sources dropped repos with
except: continue, so billing totals undercounted. except Exception: passsat on a state write.
The rules below exist so that failures are visible and the reason behind the code is written down.
Detail: references/python.md · references/bash.md · lint config: references/ruff-baseline.toml
The five non-negotiables
-
No silent failure. Every
except,|| true,2>/dev/null,continue-on-error or fail-open exit must do one of these:- log one line saying what failed and why it's safe to continue;
- increment a counter that is reported in the run summary;
- re-raise.
If it's deliberately silent, add a
# why:comment saying so. -
Data → stdout, diagnostics → stderr. stdout is for pipeable output (in Claude Code hooks it carries model context or a JSON decision; see the hooks section below). Logs, progress, warnings and errors go to stderr or a log file.
-
Configure logging once, at the entrypoint.
- Library modules only do
logger = logging.getLogger(__name__). - Never call
basicConfigor add handlers in an importable module. - Never call the root
logging.info(...)directly.
- Library modules only do
-
Never log secrets or PII. That covers tokens, keys, passwords, connection strings with userinfo, git remote URLs containing credentials, patient or person identifiers, and full request payloads. Log the shape (field names, counts, hashes), not the values. See lessons.md "git config key containing a token".
-
Comments explain why, never what. If a comment restates the code, delete it or rename the variable. Update comments in the same diff as the code they describe.
Python logging — quick reference
import logging
logger = logging.getLogger(__name__) # module level, always __name__
logger.info("Loaded %d records from %s", n, path) # lazy %-args, NOT f-strings (Ruff G004)
try:
parse(row)
except ValueError:
skipped["bad_row"] += 1 # count it…
logger.warning("Skipping row %d: unparseable", i) # …and/or say it
except Exception:
logger.exception("Unexpected failure on row %d", i) # includes traceback (TRY400)
raise
| Level | Use for |
|---|---|
| DEBUG | Internals useful when diagnosing (payload shapes, branch taken) |
| INFO | Milestones a user running the job wants: start, stage done, summary |
| WARNING | Something unexpected, but the run continues (skips, fallbacks, retries) |
| ERROR | An operation failed; the output is incomplete |
| CRITICAL | The process cannot continue |
CLIs:
- Provide
-v/--verbose(repeatable) and-q/--quiet, with aLOG_LEVELenv var as the default; flags win over the env var. - Two debug switches only:
LOG_LEVELfor programs,HOOK_DEBUG=1for hooks. No per-tool verbose or debug variables; one switch per kind means one thing to remember. print()is fine only for the CLI's real output. Ruff T20 is ignored forcli.py,__main__.pyandscripts/**.
Batch / pipeline / scientific runs: log enough to reproduce the run and to check it.
- At start:
run_id, git SHA (+ dirty flag), tool/package versions, config (or its hash), input paths + sha256, random seed. - At end: counts in / out / dropped-by-reason, per-stage wall time, output paths.
- Write the same facts to a
manifest.jsonnext to the outputs. - Shape: one
run()entrypoint that writes the start facts, runs the stages, then writes the end facts andmanifest.json.
JSON logs: the default for long-running services, opt-in everywhere else.
- A service emits JSON lines to stdout and lets the platform collect them (12-factor). Bind a
trace_id/run_idto every line viacontextvarsor aLoggerAdapter. Don't run two logging libraries in one service. - CLIs, hooks and batch jobs log text to stderr. JSON is an opt-in,
LOG_FORMAT=json|text(defaulttext), because a human reads them first. - Use stdlib logging and a JSON formatter.
aa_ma.logsetup.configure_loggingis the forge's own: it masks secrets in both formats. - Node: pino defaults to stdout, so CLIs and hooks must pass
pino.destination(2)to keep stdout for data.
Bash / hooks — quick reference
set -Eeuo pipefail
log() { printf '%s %-5s %s\n' "$(date -Is)" "$1" "${*:2}" >&2; } # ISO time, level, stderr
trap 'log ERROR "line $LINENO: \"$BASH_COMMAND\" exited $?"' ERR
cp "$src" "$dst" || { log WARN "copy failed for $src; continuing with stale copy"; }
grep -q foo f 2>/dev/null || true # why: f may not exist yet on first run; absence == no match
set -e is a safety net, not error handling. It does not fire inside if, && and ||, in functions called from those, or in $(…) without inherit_errexit, and local x=$(cmd) hides the status (BashFAQ/105). So check the status wherever a failure changes the result. Detail: Skill(bash-defensive-patterns).
Claude Code hooks are a special case (rules verified against the hooks docs, 2026-10-06):
- On exit 0, stderr reaches only the debug log; nobody sees it. Plain stdout goes to the model for UserPromptSubmit and SessionStart, and to the debug log for most other events. Hooks must exit fast.
- To tell the user something on exit 0, print JSON
{"systemMessage": "..."}to stdout. It is shown to the user but not to the model, and on PreToolUse the tool call still proceeds. Verified live, it shows asPreToolUse:Bash says: …. - A fail-open path writes a
systemMessageand also one line to a log file:~/.claude/logs/hooks.log(override:CLAUDE_HOOK_LOG). Each line isISO-ts LEVEL hook-name: msg, e.g.2026-10-12T09:41:00+01:00 WARN pre-compact-aa-ma: could not append ….hooks/lib/aa-ma-log.shwrites it:log_info|warn|error|debug <hook> <msg>masks secrets, caps the line at 8192 chars, and never tripsset -e;fail_open_notice <hook> <msg>emits thesystemMessagetoo. Keep its regression test, feed it a real payload, and assert that the message is emitted. Patterns:claude-code/hooks/pre-compact-aa-ma.sh(fail_open_noticeon an append failure) and its testtests/hooks/pre-compact.bats. - Debug chatter is opt-in. Gate it with
HOOK_DEBUG=1→log_debug(hooks/lib/aa-ma-log.sh).
Comments & docstrings
- Docstrings on every public module, class and function, in Google style (
Args:,Returns:,Raises:). State constraints the signature can't show: units, side effects, idempotency, which HTTP verb (PUT vs PATCH), and thread safety. - Every magic number gets a name, a unit and a reason, ideally with its source or a test that pins it:
LINE_TOL = 1.5 # pt; JS1 definition text puts "a)" markers 1.0 pt off their line's baseline MAX_FINDINGS = 200 # `check` output reaches an agent's context; the rest is counted, not printed - Suppressions carry a reason:
# noqa: S105 # why: enum value, not a credential. The same applies to# nosec,# type: ignore[...],|| trueand2>/dev/null. - TODOs are traceable:
# TODO(#123): drop once upstream fixes Xor# TODO(ADR-0014): …. A bare# TODOis not allowed (Ruff TD). - Non-obvious design choices point to the record:
# See ADR-0009or# See lessons.md L-1300. - Delete commented-out code (Ruff ERA). Git remembers it.
- Deliberate shortcuts are marked
# ponytail: <ceiling>, <upgrade path>. - Don't narrate (
# loop over rows), leave section banners in short functions, or write docstrings that claim features the code doesn't have (e.g. "async queue" when there is no queue).
Enforcement
- Ruff: merge
references/ruff-baseline.tomlinto each project'spyproject.toml. If an existing repo has many pre-existing violations, put the noisy codes inextend-ignoretagged# TODO(logging-std): burn down, rather than mass-fixing them. - The PostToolUse
ruff-format.shhook runsruff check --fixon every edit. T201's fix is unsafe-only, so prints are reported, never deleted (verified on ruff 0.15.4). Findings it cannot fix come back to the model asadditionalContext(≤10 lines). Hook failures are logged to~/.claude/logs/hooks.log. - Review checklist for a diff:
- grep the added lines for
except,|| true,2>/dev/null,continueandpass, and check that each one logs, counts or has a# why:; - check that each new constant has a rationale;
- check that each new public function has a docstring.
- grep the added lines for
Sources
Python Logging HOWTO and Cookbook · PEP 8 comments · PEP 257 · Google Python style §3.8, §3.10, §3.12 · Google Shell style §3.1, §4 · Ruff rules · clig.dev · 12-factor logs · OWASP Logging Cheat Sheet
04 同仓库更多 Skills
deploy-helper
You can use this to help with deploy stuff, deploying, deployments, releases, shipping, pushing to prod, going live, rollouts.
writing-for-agents
Writing documents for agents. Use when creating or editing skills, or modifying AGENTS.md or CLAUDE.md.
verify-plan
Slash entry for plan verification: runs plan-verification on an existing AA-MA plan, writes [task]-verification.md, and with --iterate loops until the plan converges. Use when the user types /verify-plan or wants the verification report saved.
verify-impl
Post-impl adversarial review symmetric to /verify-plan. Dispatches up to 5 parallel audit agents (code-reviewer, security-auditor, tdd-sequence-auditor, context7-evidence-auditor, future-proofing-auditor) based on the milestone's plan-declared Audit-Profile. CRITICAL findings surface via AskUserQuestion accept/dispute/defer panel before §7.3 user authorization. Introduced in v0.8.0 per ADR-0005. Invoked by Phase 6.8 of /execute-aa-ma-milestone.
understand-codebase
Onboard to a new, inherited, or shared codebase. Read it, understand it, map it, and learn its conventions, versioning, tests, tech stack, rules (AGENTS.md / CLAUDE.md / .cursorrules / etc.), build/run/CI, integrations, security posture, and repo health — then produce an honest pros/cons verdict, a "contribute safely" playbook, and an "add a feature" playbook, written to ONBOARDING.md at the repo root plus a .claude/onboarding/ set of deep-dives — and optionally author an AGENTS.md if one is missing, or review and propose improvements to an existing one. Tiered (Quick / Standard / Deep). Reuses codemem (index + diagrams), gsd-map-codebase, system-mapping, code-intelligence, impact-analysis rather than re-implementing them; Deep tier runs a TeamCreate agent-team. Keywords: new codebase, shared codebase, inherited code, onboard, understand this repo, how do I contribute, how do I add a feature, ramp up, get oriented, ONBOARDING.md, AGENTS.md, codebase walkthrough, joining a project.
token-compression
Output token compression for AA-MA workflows. Reduces response tokens by 40-75% while preserving full technical accuracy. Three intensity levels mapped to execution modes: lite (HITL), full (general), ultra (AFK). Auto-activates during AA-MA execution based on task Mode field. Manual activation via /compress lite|full|ultra.