personio-eng-kotlin

Skills and conventions for Kotlin development at Personio.

Installation

claude plugin install personio-eng-kotlin@personio-claude-code-marketplace

What does this plugin do?

This plugin provides skills, tools, and guidance specific to Kotlin development at Personio. It covers patterns, conventions, and best practices for Kotlin backend services.

Kotlin LSP

Integrates the JetBrains Kotlin Language Server to give Claude rich language intelligence for .kt and .kts files, including go-to-definition, find references, hover info, document symbols, and more.

What you need:

  • Java 17+
  • kotlin-lsp installed and available on your PATH

Installing kotlin-lsp:

# macOS with Homebrew
brew install JetBrains/utils/kotlin-lsp

For manual installation, download from the kotlin-lsp releases page and add to your PATH.

Note: Currently supports JVM-only Kotlin Gradle projects.

What Claude can do with the LSP:

  • Go to definition and implementation of symbols
  • Find all references to a symbol
  • Hover over symbols for type information
  • List and search for symbols across the workspace
  • Navigate call hierarchies (incoming and outgoing calls)

personio-framework skill

Makes Claude aware of Personio Framework (PF) and grounds PF-related answers in real sources instead of fabricating them.

What you need:

  • gh CLI, authenticated (gh auth login)
  • git
  • The personio-mcp-server MCP connected (used for the live techdocs)
  • A local clone of Personio-Internal/personio-framework at ~/dev/personio-framework — the skill offers to clone this for you on first use

Quick Start:

# Nothing to do — the skill activates automatically when you ask a
# PF-related question, detects your service's pinned PF version from
# libs.versions.toml, and answers from the right tag of the local clone.
claude "How does PF handle multi-tenancy?"

Example:

In a Kotlin service that pins personioFrameworkBom = "20.18.0+2026-04-14" in gradle/libs.versions.toml:

You: How is gRPC resiliency set up in PF?

Claude (via personio-framework skill):

  • Detects v20.18.0+2026-04-14 → target tag.
  • Reads apis/synchronous/grpc-service-development/resiliency/ from the techdocs MCP for the narrative.
  • Reads the matching implementation at the v20.18.0+2026-04-14 tag via git show.
  • Cites both a GitHub deep-link and a reproducible git show command.

Why would you want this?

  • No fabricated answers. Every PF claim is grounded in either the live techdocs or the pinned version's source code.
  • Version-aware. Answers reflect the PF version your service actually uses, not "latest" or an LLM's memory.
  • Current on conventions. Deprecations and convention changes go into techdocs first — the skill always prefers MCP for convention / deprecation questions, so you won't be told to use something that was retired since your pinned release.
  • Non-invasive. The skill is read-only over ~/dev/personio-framework: no git checkout, no git pull, no disturbance to a clone you may also be actively editing.

Auto-loaded on Personio Kotlin service files

A PreToolUse hook in this plugin watches Read / Edit / Write calls. The first time Claude touches a Personio Kotlin service file in a session, the hook injects a one-shot system reminder pointing Claude at this skill. Subsequent matching file touches in the same session do not re-inject — Claude already has the context.

Matched files (basename, case-sensitive):

  • Kotlin source and build scripts: *.kt, *.kts (incl. build.gradle.kts, settings.gradle.kts)
  • Gradle config: libs.versions.toml, gradle.properties, gradle-wrapper.properties, build.gradle (Groovy DSL)
  • Spring application.* config: application.yaml, application.yml, application.properties, plus profile variants application-*.yaml / application-*.yml / application-*.properties

The reminder is suppressed when the file lives in the personio-framework repo itself (detected via the repo's origin remote, so git worktrees count too): the skill bootstraps a read-only clone of that repo for consuming services, so it adds nothing when you are editing the framework's own source.

The hook never blocks a tool call; it only adds context. If you want to opt out, remove the plugin or delete hooks/hooks.json from your local install.

pf-golden-path skill

Deprecation-first advisor for Personio Framework. Surfaces @Deprecated symbols in code reviews, answers golden-path dependency questions, and reports on deprecation hot-spots — all grounded in live sources, no curated tables.

What you need:

  • gh CLI, authenticated (gh auth login)
  • git
  • The personio-mcp-server MCP connected (used for golden-path / BOM curation pages)
  • A local clone of Personio-Internal/personio-framework at ~/dev/personio-framework — the skill offers to clone this on first use

Quick Start:

# The skill activates automatically on deprecation or golden-path questions.
claude "Is com.personio.framework.multitenancy.implicit.RequestContext deprecated?"

Example:

In a Kotlin service editing build.gradle.kts:

You: Can I use Rollbar for error tracking in this service?

Claude (via pf-golden-path skill):

  • Reads managing-dependencies/golden-path from the techdocs MCP.
  • Reports Rollbar is explicitly listed as "Not on the golden path".
  • Suggests the golden alternative: io.sentry:sentry-spring-boot-starter-jakarta (BOM-managed).
  • Cites the techdocs entity_ref + path.

Another example — reviewing a Kotlin file:

You: Review this class for deprecated PF APIs:

import com.personio.framework.multitenancy.implicit.RequestContext
...

Claude (via pf-golden-path skill):

  • Detects $TARGET_TAG from gradle/libs.versions.toml.
  • Greps @Deprecated at that tag for each com.personio.framework.* import.
  • Lists every finding with the annotation's message and ReplaceWith target.
  • Cites both a GitHub deep-link and a reproducible git show command per finding.

Why would you want this?

  • Deprecations caught before they propagate. A new file pasted for review is scanned for deprecated PF symbols at the right tag — catches warnings that would otherwise slip into the commit.
  • No curated table to rot. All three sources (@Deprecated in the clone, golden-path techdocs, BOM-curation techdocs) are live and authoritative. Zero maintenance.
  • Dependency decisions grounded. "Can I use Micronaut / Rollbar / Quarkus?" gets a tagged answer from the golden-path reference, not an LLM guess.
  • Version-aware. Same substrate as personio-framework: answers reflect your service's pinned PF version.
  • Non-invasive. Read-only over ~/dev/personio-framework.

pf-upgrades

Read-only advisor for Personio Framework version upgrades. Walks the CHANGELOG slice between the pinned start tag (from libs.versions.toml) and the requested target (default: server-side latest), surfaces any hand-written upgrade guides first, and flags release-date deltas that cross the 60-day deprecation-notice window.

What you need:

  • ~/dev/personio-framework locally cloneable (the skill bootstraps it via gh repo clone Personio-Internal/personio-framework if missing).
  • gh authenticated (gh auth login).
  • A service with gradle/libs.versions.toml pinning personioFrameworkBom (or a user-supplied start version if you're asking from outside a service).

Quick Start:

# Ask the skill in Claude Code, e.g.:
"Help me upgrade from PF 20.11 to 20.18."
"What's new in PF 20.18?"
"What CVEs were patched between 20.14 and 20.18?"

Example — named range:

You: Help me upgrade from PF v20.11.0+2026-02-24 to v20.18.0+2026-04-14.

Skill: Upgrade PF v20.11.0+2026-02-24 → v20.18.0+2026-04-14 (49 days, within 60-day window).

📘 Read first — upgrade guides

  • 20.11.0 → 20.12.0: @EnableMultitenancyProtection now bundles ContextPropagationConfiguration … [link]
  • 20.12.0 → 20.13.0: automatic context propagation for RequestContext fields across thread boundaries … [link]

⚠️ Breaking changes (grouped by libs/… / plugins/…)

✨ New features (grouped)

🔒 SecuritySpring Boot 3.5.10 → 3.5.12 (CVE-2026-24281); Datadog agent 1.60.x → 1.60.4; N other routine Dependabot bumps.

Example — >60-day jump:

You: Upgrade my service from PF v20.0.0+2025-12-15 to v20.18.0+2026-04-14.

Skill: Delta: 120 days — exceeds the 60-day notice window. Deprecations introduced near the start tag may already be hard compile errors at the target. Recommend stepping through an intermediate hop at v20.11.0+2026-02-24 first (≈ start + 60 days). Full range report follows below anyway.

Why would you want this?

  • One command for the whole upgrade range — no more skimming eight CHANGELOG entries manually.
  • Every claim is reproducible — deep-links and git show commands under each bullet.
  • 60-day policy is baked in — you don't have to remember to check whether your jump is safe.
  • CVE signal is never lost — routine Dependabot bumps collapse, but CVE entries stay verbatim at the top of the Security section.
  • Advisory-only — the skill never edits libs.versions.toml or runs gradle. You keep control of the actual bump.

ppm

Read-only advisor for Personio Plugin Manager (PPM) — Personio's Gradle plugin-convention system. Answers "what does this convention do?", "is it on by default?", "how do I turn it off?", "what PPM plugins should I enable?", and enumeration requests by reading the PPM README (plus per-convention deep-dive pages where they exist) at the pinned PF version.

What you need:

  • ~/dev/personio-framework locally cloneable (the skill bootstraps it via gh repo clone Personio-Internal/personio-framework if missing).
  • gh authenticated (gh auth login).
  • A service with gradle/libs.versions.toml pinning personioFrameworkBom (or falls back to the latest tag).

Quick Start:

# Ask the skill in Claude Code, e.g.:
"What does PactConvention do?"
"How do I disable the JacocoPluginConvention?"
"List all PPM conventions."

Example — toggle question:

You: How do I disable the JacocoPluginConvention?

Skill: JacocoPluginConvention is on by default when the Jacoco plugin is applied. To disable:

# in gradle.properties
personio.convention.jacoco=false

Source: plugins/personio-plugin-manager/README.md at v20.18.0+2026-04-14github / git -C ~/dev/personio-framework show v20.18.0%2B2026-04-14:plugins/personio-plugin-manager/README.md

Example — deep-dive lookup:

You: Explain the DataRegistryConvention.

Skill: Reads both the README row and DataRegistryConvention.md at the pinned tag, summarizes the local-development workflow, names the keyword personio.convention.data.registry, and cites both files.

Why would you want this?

  • One lookup, one answer — no tab-switching between README, per-convention pages, and source.
  • Toggle snippets are ready to paste — the skill emits the exact gradle.properties line for the convention you asked about.
  • Version-pinned — all answers are grounded in the PF tag your service actually uses, not the latest main.
  • Advisory-only — the skill never edits gradle.properties or build.gradle.kts. You keep control of the actual change.

debug-with-mirrord

Run a Personio microservice locally wrapped in mirrord so traffic matching your session header is stolen from the dev cluster to your laptop, then drive end-to-end test scenarios via API calls or browser automation. Replaces the IntelliJ [mirrord] <service> run configuration for agentic workflows.

What you need:

  • mirrord CLI (brew install metalbear-co/mirrord/mirrord)
  • Active AWS SSO session for EKS auth (aws sso login --profile dev)
  • kubectl context set to dev (or whichever cluster the service's .mirrord/mirrord.json targets)
  • A service repo containing .mirrord/mirrord.json
  • Recommended: personio-eng-common plugin installed for the dev-auth skill — used to mint dev JWTs for API/gRPC calls during scenarios. Without it, Claude will ask you how to obtain a token.

Quick Start:

# In your service repo:
claude "Start payroll-data-import-service with mirrord and upload a YTD file."

The skill inspects .mirrord/mirrord.json, launches the service via the bundled mirrord-start.sh, polls /health-check/readiness, then runs the requested scenario — handing off to the dev-auth skill (in personio-eng-common) for token minting and hitting localhost:<port> directly so downstream I/O still routes through the remote pod.

Example — API scenario:

You: Start payroll-data-import-service with mirrord and import the employees from playground/employees.csv for company 12345.

Claude (via debug-with-mirrord skill):

  • Reads .mirrord/mirrord.json; reports target rollout, namespace, and resolved session header (baggage: mirrord-session=$USER).
  • Checks for stale mirrord PIDs in /tmp/mirrord-*.pid to avoid session-header collisions.
  • Launches via mirrord-start.sh --service-dir . and waits for /health-check/readiness to return 200.
  • Hands off to the dev-auth skill to mint a user-scoped dev token.
  • POSTs the import request against http://localhost:8080/... and tails /tmp/mirrord-<service>.log to confirm the local controller handled it.
  • Stops the session with mirrord-stop.sh --service-dir . when done.

Example — concurrent sessions:

You: Start a second mirrord session for notification-service while payroll-data-import-service is still running.

Claude: Detects an existing PID file. Without an override both sessions would match the same mirrord-session=$USER header and traffic routing becomes non-deterministic. Offers to (a) stop the existing session, or (b) start the new one with --session-id $USER-notification-service and prints the matching baggage header to copy into curl / the Personio Developer Extension.

Why would you want this?

  • No IntelliJ required — agentic workflows can start, exercise, and stop a service end-to-end from the terminal.
  • Real downstream behaviour — DB, Temporal, gRPC clients, S3, env vars all still route through the remote pod under mirrord, so business logic executes identically to a dev-URL call.
  • Safe concurrency — collision detection and an explicit --session-id override stop two sessions from silently fighting over the same mirrord-session header.
  • Localhost-by-default — the skill calls http://localhost:<port> directly, so you don't need to know the public dev URL or wrangle baggage headers for routine testing.
  • Graceful cleanup via mirrord-stop.sh