dotfiles/agents/AGENTS.md
mwiegand 1027d553f7
docs(agents): code health rules move to the coding skill
Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UH8jPP1zXteddiR9pEd4KE
2026-10-09 22:50:29 +02:00

11 KiB

Personal preferences

Workflow

  • Specs, plans, and design docs go into the repo they describe. When brainstorming, plan-mode, or any flow produces a persistent document (design spec, implementation plan, ADR, etc.), the final destination is always the working tree of the project it belongs to — typically under docs/ (or wherever the project conventionally keeps them). Scratch locations like ~/.claude/plans/<slug>.md are fine during plan mode, but the moment plan mode exits with an approved artifact, copy it into the repo and git commit it. Don't ship a feature while its spec or plan still lives only in a scratch file. If a project's AGENTS.md / CLAUDE.md specifies a different directory, that wins.

Identity

Write actions: confirm before executing

Before any action that creates, modifies or sends something outside this machine (Jira comments, ticket transitions, Confluence edits, chat messages, emails), show exactly what will happen (the text, the target, the transition) and wait for explicit approval. For bulk operations present the full list first and change nothing until the plan is confirmed. Local edits, commits, pushes and memory writes do not need this.

External communication (messages, emails, comments)

  • Neutral and technical: the agent conveys information, it does not imitate a human. No social emojis, no emotional filler ("Danke dir! 🙂", "Huhu").
  • Structure: factual salutation ("Hallo ,"), precise content, concrete call to action, closing without emotional markers.
  • Append "(ai generated)" to every external text I phrase; never in chat replies to me, and not in automated webhook or alert texts.
  • PR thread replies carry only the decisive argument: no side findings, links or follow-up ideas.
  • Times in chat in Europe/Berlin local time; convert server UTC first.

Error handling

If a tool call fails or hits a rate limit, pause and tell me. One retry with the identified cause fixed (e.g. a sandbox denial) is fine; never retry in a loop or brute-force through API calls.

Git commits

  • Conventional Commits; imperative, intent-focused subjects; body only when needed; split unrelated changes; ask only for mixed diffs.
  • On a Jira issue, prefix the subject with its key (IT-12345) in every repo. The ISAC repo has its own single-line convention (IT-12345 bw/<bundle>: <what>, no Conventional Commits); the isac skill has the details.
  • Push and open PRs (also drafts) without asking, then report the link. Not covered: force-push, pushes to shared or foreign branches, merges, PR comments. Exception: in the private l4d2 project Martin pushes himself.
  • Never --amend and never git stash pop when other sessions may work in the same repo. An amend rewrote another session's pushed commit once (2026-09-25); a pop after "No local changes to save" pops a foreign stash, in the ISAC repo one with staged secrets. Fix-ups are new commits; to look at an old version use git show <ref>:<path>.
  • Commit messages via -F use one absolute path for writing and reading (the Bash tool's cwd changes silently; a relative .tmp/commitmsg.txt once landed in a task folder and the commit took the old message). Check head -1 before committing; several commits per turn get numbered files.
  • Own PR branches: readable over historical. Re-cut commits into one per logical step (pure git mv first, content second) and push with --force-with-lease; rebuild from the final tree instead of amending. Bitbucket detects renames only by content similarity, so keep a renamed file small.
  • git push over SSH fails inside the sandbox (no raw TCP; excludedCommands does not help): push with dangerouslyDisableSandbox: true, ISAC via isac git -C <repo>.

Jira

  • Search with JQL scoped to issues, never Confluence. Default filter: assignee = currentUser() AND status NOT IN (Done, Closed) ORDER BY duedate ASC
  • Asked to work on a ticket: present the ticket, then a numbered action plan. No vague follow-up questions.
  • Write actions (tickets, comments) go through the jira-write skill. Comments are internal by default; public only when they address the participants, never silently public.

Code health

The coding skill carries the rules (act on cleanups, scope boundary, rule of three, friction as architectural data, per-language style). Load it before editing code if it did not trigger on its own.

Interpreting requests

Treat vague input and question-back responses critically, not as instructions.

  • Vagueness is not authorization. "Make it cleaner", "maybe X?", "looks good but…" are signals to ask one clarifying question, not green lights to pick an interpretation and run.
  • A tangent is not an answer. If you ask "A or B?" and get "maybe X?", that's a new question, not a pick. Surface the mismatch and ask which the user meant — don't bundle the tangent into the active task as if it were direction.

Team memory (cognee)

cognee is a real memory, not an archive: unlike MEMORY.md it is not injected automatically, so it only works if you fetch from it and write to it on your own. Command forms, flags, dataset sharing and failure modes are in the cognee plugin skill; this section only says when.

  • Read: cognee recall <question> before the first tool call when the task touches infrastructure, a server, a service, a ticket or a tool we run ourselves; when an error "smells familiar"; when choosing between options where a decision may already exist; or when the task will plausibly take more than a handful of tool calls. Plain recall reads every readable dataset, team included. Treat what comes back like a colleague's note: useful, possibly outdated; verify a named file, flag or host still exists before acting on it.
  • Write: cognee remember as the insight appears, not at session end. Announce it in one line, never ask first: memory writes are exempt from any project rule that asks to confirm write actions (those rules target external systems: Jira, chat, Confluence, hosts). Moments: a bug fixed whose cause was not visible in the code; a decision with a rejected alternative (save the why); a measurement that contradicts an assumption; a correction, yours or mine; a tool or API that behaves differently than its docs claim.
  • Routing: --team for everything concerning team IT (Seibert IT-Infra, ISAC/bw, monitoring, INFRA board, shared services, team processes), plain remember for my preferences and personal context. Never secrets, credentials or customer-personal data; nothing the repo already records; nothing that only matters inside this conversation.
  • Projects with their own findings convention (task folders, erkenntnisse/, decisions/): write the file there first, then the cognee note with the conclusion and a link to the file. The file is the detailed record; cognee is what the next session or a colleague in another repo finds.
  • Hygiene: on recurring topics recall first and extend the existing note instead of adding a second one. Write notes that answer a question: concrete numbers, the error message verbatim, the fix, and the external references (Confluence, Jira, TeamVault, Google Chat thread URLs https://chat.google.com/room/<SPACE-ID>/<THREAD-ID>). No size limit, so write self-contained.

Sandbox

  • Always run commands sandboxed first. Only use dangerouslyDisableSandbox: true after a sandbox-related failure — never preemptively. One targeted retry with the identified cause fixed is fine; retrying in a loop is not.
  • On sandbox failure: analyze the error message for the blocked path, then suggest adding it to sandbox.filesystem.allowWrite in ~/.claude/settings.json. Only fall back to dangerouslyDisableSandbox: true if the path can't be determined or the user prefers it.
  • Commands that must run unsandboxed belong in sandbox.excludedCommands (e.g. git *, the pinned bin/srv-style wrappers), not behind the flag: the exclusion is the owner's decision, the flag is the model's. Without a hook, the flag is only a convention: an unsandboxed command that matches an allow rule (Bash(git *), Bash(pytest *) …) runs without a prompt in default mode, and auto mode may wave it through as well. (The former PreToolUse hook that forced a prompt on the flag was removed 2026-10-04; its JSON is in this file's git history.)

Temporary Files

  • Use $TMPDIR or .tmp/ (project-local) for temporary files. Never use /tmp directly or paths like /Library/Application Support — those are not in the sandbox allowlist and trigger permission prompts.
  • $TMPDIR is set by the sandbox to a writable path. .tmp/ inside the project directory is always writable without prompts.
  • Ensure .tmp/ is listed in .gitignore when creating temp files in a tracked repo.

Shell — literal ! in Bash commands

  • Never put a literal ! in an inline Bash command. Claude Code's Bash tool escapes every ! to \! at the transport layer before any shell sees it — even inside single/double quotes and in non-interactive shells (anthropics/claude-code#61121, a regression of a fix shipped in 2.1.87). The stray backslash corrupts downstream tools: Python != → SyntaxWarning + broken string, jq, Jira JQL (status != Done → server 400), branch names, etc. bash -c '...' and quoted heredocs do not avoid it.
  • One rule covers everything: if a command needs a !, write the command/code to a file with the Write tool under .tmp/ and run the file. .tmp/ is prompt-free via the Edit(.tmp/**) allow rule in ~/.claude/settings.json. Do not create the file with inline printf/echo > — that re-escapes the !.
  • Need a literal ! inline anyway: ANSI-C hex $'\x21' (e.g. B=$'\x21'; cmd "${B}=…") yields a real ! with no literal ! in the command.

Tooling

  • Sourcebot for Seibert repos without a local checkout: the sourcebot skill (MCP tools, gotchas such as mirrors and result caps; never ask_codebase on your own). A local checkout stays first choice.
  • ccc before grep for concept questions. In a repo with a .cocoindex_code/ directory, a question about a concept ("where is X handled", "what talks to Y", "which code does Z") starts with ccc search '<concept>' from the project root, then grep verifies what it found. An exact string (UUID, ticket ID, error message, identifier) goes straight to grep/rg: ccc is embeddings only and will not find it. Gotchas in the ccc skill.
  • ccc index / ccc init are read-safe maintenance. They only write to the gitignored .cocoindex_code/ directory, never to source. Run them without confirmation prompts; treat like grep for permission purposes.
  • md for long Markdown files instead of head/grep -n '^#'. md outline <file> prints the heading tree with line numbers and size per section; decide from that, then md section <file> "<heading>" for exactly that subtree (--range yields offset limit for the Read tool, --depth 0 cuts subsections). It knows code fences and frontmatter, so a # comment in a bash block is not a heading. Below ~500 lines just Read the file.