Imported from luhmann/dotfiles_yadm (
AGENTS.md). Install upstream withnpx skills add luhmann/dotfiles_yadm. Copyright stays with the author.
Testing
- Test observable behavior, not implementation details. Prefer tests that exercise the public boundary of a feature (e.g., call service → assert on result/DB state/published events) over tests that mock internal collaborators. Tests should break only when behavior changes, not when code is refactored. Isolated tests are warranted for self-contained logic (calculations, parsing, validation) where edge cases are hard to reach through the outer boundary.
- Creation tests should assert the complete resulting data set to catch missing or incorrect defaults. Every asserted value must be explicitly visible in the arrange phase — do not rely on hidden factory defaults for values that appear in assertions.
- Focused tests (updates, single-field mappings, edge cases) should only include the fields relevant to the behaviour under test in both arrange and assert — keep everything else at factory defaults so the test clearly communicates what it is checking.
- Make sure each test has visible Arrange, Act, Assert-phases, but DO NOT add comments that say exactly that
- Follow existing test patterns
Error Handling
- Fail fast with descriptive messages
- Include context for debugging
- Handle errors at appropriate level
- Never silently swallow exceptions
General Coding Style
- Prefer simple, elegant and easy to understand solutions at all times.
- Prefer functional programming patterns, like small functions, pipelining and composition.
Communication
- Lead with the outcome. Use concise, plain language and bullets when useful.
- Explain material decisions, tradeoffs, risks, and blockers instead of routine mechanics or a blow-by-blow transcript.
- Keep long-running work visible with brief status updates.
- Make final responses self-contained.
Kotlin
- Prefer scope-function pipelines (
.let,.also,.apply) over intermediate variables when a function is a linear chain of transformations and side effects. Each step should be a single operation — break the chain if logic branches.
TypeScript
- Never use barrel files (index.ts re-exports); import from specific modules directly
Git
- Before committing any changes run the projects tasks for formatting, linting and the whole test suite. Check the project setup what the tools are (eg. ktlint format, maven/gradle tests)
- when opening prs on my behalf, include the ticket number in the title, have a simple description as PR body, do not include a test plan, include a link to the ticket you should be able to derive it with
linear --help. If the PR is stacked on another Branch/PR that is notmaster/main, then include references to all prs that need to be merged before this one can go into main
Tools
- When creating Linear tickets, verify the team's workflow states with
linear team states <TEAM>and explicitly pass--state Todo(using the team's exact spelling) to bypass Triage unless another state is requested. - for researching you have the
search- andwebsearch-skills available, additionally if they do not yield enough material you can invokekagi search --helpfor instructions to leverage a full search engine. - you are usually sandboxed via
agent-safehouse, if you encounter permissions problems check ~/.config/agent-safehouse and~/.aliasesto see the setup mem --helpsearches past agent sessions (Claude Code, Pi, Codex, OpenCode) — usemem search --json "<query>"to find prior conversations,mem view <session-id>to read them.- Never poll a spawned subagent (
sleeploops, repeated status checks) — it messages you when done; just end your turn. And don't build while a child builds in the same checkout: you'll collide overtarget/.
Java / JDK (mise)
- Java is managed by
mise(not asdf). Non-login shells don't have JAVA_HOME set, so./mvnw/./gradlewfail with "Unable to locate a Java Runtime". - Fix:
export JAVA_HOME="$(mise where java <version>)"before the build, e.g.export JAVA_HOME="$(mise where java 21.0.2)"(match the repo's mise/.tool-versionspin).
CLI gotchas
rg: ripgrep recurses by default — never add a-r-style flag for that.-r/--replaceconsumes the next token as a replacement string and rewrites matches (e.g.rg -rln "pat"becomes--replace=lnand prints garbage). Use-lfor "files with matches".rg/Grep "No matches" can be a false negative (.gitignore/.ignore, binary/NUL files, symlinks). Never conclude code doesn't exist from an empty result in a repo you know contains it — recheck withrg -uu "pat"first.- Use
rg -Ffor literal strings containing.,(,{,[,*(e.g.rg -F "User.findOne({id})"); use regex mode only when you actually want a pattern. - Prefer native
rgflags over pipes:-t kotlin/-t java/--globto filter,-lfiles-only,-ccounts,-C 2 -nfor context+line numbers. Avoidrg | grep | awkchains. - awk here is macOS BSD awk (no
gawk); avoid GNU-only features (gensub,--version). Prefer structured queries (jq,yq,xmlstarlet) over hand-rolled awk range-matching for specific JSON/YAML nodes. psandtopfail with "Operation not permitted" inside agent-safehouse. They are setuid root, and macOS blocks setuid exec from any Seatbelt sandbox — no policy change can fix it, so don't try to debug it. Usepgrep -l -f <pat>for lookup,lsof -nP -c <name>for sockets/files, andkill/pkillfor signalling (these need--enable=process-controlto reach host processes; without itpgrepreports "sysmond service not found").
Wiki
- A curated personal wiki lives at
~/jcloud/org/wiki/— domain (Zalando purchasing/orders) + engineering knowledge that compounds across sessions. - For cross-service or domain questions, consult
~/jcloud/org/wiki/index.mdfirst; search specifics withrgin that directory. - Never write to the wiki directly — mutations only go through the wiki skill.