Skip to content

Harness ​

The harness is the set of guardrails that lets humans and coding agents change DrinkIt without breaking master. Each layer catches a mistake as early as it can, and no layer relies on the previous one having run.

A person can skip the local git hooks with --no-verify, a Claude Code session cannot skip pre-push. The GitHub layers cannot be skipped, owner included.

Coding agent ​

/implement-issue <number> takes an open issue to a pull request, whatever its board status: a worktree of its own, the code test first, the lanes CI would run, a fresh judge, then the pull request and the board moves. The maintainer merges. Run again once the pull request is merged, it removes the worktree and the branch, unless work is left in them, pulls master, and moves the issue to Done once it is closed.

PartWhat it doesWhereSince
ConventionsCommands, structure, code patterns and traps a coding agent reads when a session starts. Claude Code, Codex, Copilot and Cursor all read the fileAGENTS.md2026-05-01 as CLAUDE.md, AGENTS.md since 2026-10-03
Agent issue-judgeA fresh subagent that sees the issue and the branch, not the author's account of it. It reruns the tests it relies on and returns two verdicts: Spec, each acceptance criterion met, and Design, the refactor done and the guidelines followed. Two rounds at most, then the maintainer decides.claude/agents/issue-judge.md2026-10-03
Agent security-reviewerA fresh, read-only subagent that reviews a branch for security: authorization of each endpoint, CSRF, CORS, headers, input validation, secrets, new dependencies, the frontend's handling of user input. implement-issue starts it when the diff touches one of those.claude/agents/security-reviewer.md2026-10-03
Lint hookOnce Claude Code writes a Kotlin source or a Gradle script, runs lint-kotlin from its worktree, and shows Claude the findings when detekt fails.claude/hooks/detekt-after-edit2026-09-13, Kotlin only and findings shown since 2026-10-03
Guard hookRefuses, before it runs, a command that writes to a ruleset, to branch protection or to a push protection bypass, or that merges a pull request, through gh, gh api, GraphQL or curl. Also refuses any way around pre-push: --no-verify, an overridden or unset core.hooksPath, a push from a clone without the git hooks. Reads pass, and so does text inside quotes or a heredoc that no shell reads. It reads the command as text, so one built indirectly, through a variable or a script, passes. permissions.deny in the same settings refuses gh pr merge and git push --no-verify a second time.claude/hooks/guard-github-protections2026-10-02, merges and pre-push since 2026-10-03
cmux status hookIn the cmux terminal, names the session's tab after its issue and step, #382 implement, #382 judge, #382 PR #450, and shows the judge's verdict and the pull request's CI gate as sidebar pills, read from gh pr checks whether it passes or fails. Does nothing elsewhere.claude/hooks/cmux-status2026-10-03
Generated files hookRefuses a hand edit of a file a tool writes: jOOQ classes, the generated API client, Gradle's and npm's lock files, the generated documentation pages, and .env files. Its message names the command that changes the file instead. A shell command that writes the file passes.claude/hooks/protect-generated-files2026-10-03
Compile hookAt the end of a turn that changed Kotlin sources, compiles them with their tests. When frontend sources changed, runs the typecheck script of each app that has one and has its dependencies installed. The errors go back to Claude, which keeps working until they are fixed. A state already checked is not checked again, so it cannot loop.claude/hooks/compile-at-stop2026-10-03
Session context hookAt start, on resume and after a compaction, tells Claude its branch, its issue, how far it is ahead of master, its uncommitted work, and the worktrees whose branch is merged into the last fetched master, left for /implement-issue to clean up.claude/hooks/session-context2026-10-03

Each Claude Code hook has a test next to it, <hook>.test, and so do pre-commit and pre-push, run by hand for now. Claude Code also loads the personal configuration of whoever runs it, from ~/.claude, on top of these parts, and a project cannot turn it off.

Skills ​

A skill holds how to carry out one kind of change. Claude loads it when the files it names are touched, or when it is asked for, and other agents are told to read it by AGENTS.md.

SkillWhen it appliesStarted by
implement-issueTaking an open issue to a pull request, and cleaning up once it is mergedThe maintainer, /implement-issue <number>
new-issueTurning an idea, a bug or discovered work into an issue that follows its form, created at once and Ready unless blockedOn request, or by implement-issue
review-prReviewing a pull request and posting inline comments ranked by severityThe maintainer, /review-pr <number>
test-driven-developmentAdding or changing behavior, backend or frontend: a test list, red-green-tidy cycles, then a technical and functional refactorCode under drinkit/ or a backend starter
test-existing-codeCovering code that works but has no tests, each test proven able to failOn request
hexagonal-backendWhere each piece of a backend feature goes, from use case to controller and security ruleBackend production code
api-contract-changeChanging the OpenAPI contract, then both generated sidesThe contract and the client generator settings
database-changeChanging the schema in place, idempotently, then the jOOQ classes and their testsThe changelogs and the generated jOOQ code
frontend-featureShaping a feature of the Nuxt app around the generated clientFrontend sources
new-backend-tech-starterCreating a backend tech starter, or aligning one with the conventionsOn request
new-frontend-tech-starterCreating the first frontend tech starter, a Nuxt layer, once its layout is agreedOn request
pentestScanning the running application locally with OWASP ZAP, then triaging the findingsThe maintainer, /pentest
threat-modelA STRIDE threat model of one feature or flowThe maintainer, /threat-model
ci-workflow-changeChanging a workflow, an action reference or a lane: pinned actions, mapped paths, actionlint and zizmorThe workflows and the in-repo actions
harness-changeChanging a hook, its test, the settings, a skill or an agent: portable sh, tests next to each hook, globs that matchThe hooks, the settings, the skills and the agents
dependency-changeAdding or bumping a dependency: one version in the catalogs and the BOM, then the verification metadata and the lockThe platform, the build scripts and the package.json files
build-convention-changeChanging a convention plugin: its id from its file name, the archetypes, the configuration cachebuild-logic
cavemanTerse replies to the maintainer, on by default through AGENTS.md, with plain prose where clarity needs itEvery session, /caveman lite, full or ultra

Git hooks ​

Enabled once per clone with git config core.hooksPath .githooks.

PartWhat it doesWhereSince
lint-kotlinFormats Kotlin sources and Gradle scripts, fails when detekt does. Shared by pre-commit and Claude Code.githooks/lint-kotlin2026-09-13
pre-commitRefuses a staged file over 5 MB, and a dependency change in a package.json without its package-lock.json. When Kotlin files are staged, runs lint-kotlin and re-stages what it reformatted.githooks/pre-commit2026-09-13, size and lock file checks since 2026-10-03
pre-pushRefuses any push, force push or deletion of master before anything is sent.githooks/pre-push2026-09-27

Gradle ​

PartWhat it doesWhereSince
detekt convention pluginApplies detekt with type resolution to every module, skips generated code, makes check run detektAllcom.drinkit.code-analysis-convention2024-03-17, fails the build since 2026-09-13
detekt configurationThis project's deviations from detekt's defaults, formatting rules includedcode-analysis/detekt/detekt.yml2024-03-17
detekt baselineFindings older than the switch to failing builds. They no longer block, new ones docode-analysis/detekt/baseline.xml2024-03-17
Wrapper checksumThe wrapper refuses a Gradle distribution whose SHA-256 differs from the committed one. Renovate updates it with each Gradle versiongradle-wrapper.properties2026-09-30
Dependency verificationThe build refuses a dependency or plugin whose SHA-256 differs from the committed one, or has none. Renovate regenerates the file in its own pull requests. Sources, javadoc and two artifacts that plugins resolve on their own are trusted, each with its reasonverification-metadata.xml2026-10-02
Dependency lockingdrinkit-backend resolves exactly the versions its gradle.lockfile lists, transitive and test dependencies included. The libraries are not locked, so a new release inside a version range they ask for, cucumber's for instance, fails verification until the files are regeneratedcom.drinkit.api-convention2026-10-03

Continuous integration ​

One workflow, ci.yml, runs the lanes a change touches, each from a file of its own. CI/CD explains the lanes and the choices behind them.

PartWhat it doesWhereSince
CI gateThe only required check. Fails when a lane failed or was cancelled, passes when a lane was not neededci.yml2026-10-01
DecisionMaps the changed files to the workflows, backend, frontend, docs, ops and dependencies lanes. A file no list knows runs every laneci-lanes.yml2026-10-01
CI filesactionlint checks that the workflows are valid and zizmor audits their security, whenever they change. A finding blocks the mergeci-workflows.yml2026-10-02
BackendCompiles and tests the backend and runs detekt in one Gradle run, findings in code scanningci-backend.yml2024-03-03 as build, detekt in the same run since 2026-10-01
CodeQLLooks for security flaws in the Kotlin and Java code, results in code scanning. Not requiredci-codeql.yml2024-03-25, off from 2026-09-13 to 2026-09-26
FrontendGenerates the API client and builds the Nuxt appci-frontend.yml2026-10-01
OpsValidates the local compose fileci-ops.yml2026-10-01
Docs and Deploy docsGenerate the living documentation and build the site on pull requests, deploy that build from masterci-docs.yml, cd-docs.ymldeployed since 2025-07-03, built on pull requests since 2026-10-01
Dependencies: graph submissionSends the Gradle dependency graph to GitHub for every master commit and for pull requests that change dependenciesci-dependencies.yml2024-03-25, nothing sent from 2025-01-25 to 2026-09-27
Dependencies: reviewFails a pull request that adds a dependency with a known vulnerabilityci-dependencies.yml2026-10-01
Weekly full runRuns every lane on master each Monday, CodeQL on all the code includedweekly.yml2026-10-01
Gradle wrapper validationChecks that the Gradle wrapper is an official release, in every Gradle setupsetup-gradle-jdk2024-03-25, in the shared setup since 2026-10-01

GitHub configuration ​

PartWhat it doesWhereSince
master rulesetNo bypass, owner and agents included. Pull request required, CI gate green on a branch up to date with master, conversations resolved, no force push, no deletion.github/rulesets/master.json2026-09-27
Merge settingsRebase is the only merge method, "Update branch" is offered, merged branches are deletedRepository settings2026-09-27
Workflow tokenRead-only by default, each workflow asks for what it needsRepository settingsNot recorded
Fork pull requestsWorkflows of every external contributor wait for approvalRepository settings2026-10-02, first-time contributors only before
Pinned actionsA workflow that references an action by tag or branch does not start: every action is pinned by commit SHA, in-repo ones with $/Repository settings2026-10-02
RenovateOpens dependency update pull requests a week after a release, on Monday mornings, and pins GitHub Actions and compose images by digest. Security fixes and undated releases (JDK, large Docker Hub images) skip the wait. Majors and lock file refreshes wait for a checkbox on the Dependency Dashboard. A pull request is rebased only on conflict.github/renovate.json2024-04-11, delayed since 2026-09-30, rebased on conflict only since 2026-10-01
Dependabot alertsFlag dependencies with a known vulnerability, which Renovate turns into security updates. Dependabot opens no pull request of its ownRepository settingsNot recorded, its security updates off since 2026-10-01
Secret scanning and push protectionGitHub refuses a push that contains a known secret format, from git, the web interface or the API, and scans the whole history for secrets already pushedRepository settings2026-10-02
Private vulnerability reportingA vulnerability can be reported from the Security tab, without a public issue. SECURITY.md points thereRepository settings, SECURITY.md2026-10-02, policy since 2026-10-03
Issue forms"New issue" offers a work item, an epic and a bug form. Only collaborators can still open a blank issue. Forms only apply on github.com, so AGENTS.md tells agents to reuse their headings.github/ISSUE_TEMPLATE2026-10-03
Pull request templatePrefills a new pull request: what changes and why, Closes #, choices, verificationpull_request_template.md2026-10-03
Contributing guideHow to write issues, pull requests and comments for a human reader, by people and agents alike. GitHub links it when an issue or a pull request is openedCONTRIBUTING.md2026-10-03
Gradle configuration cache keyThe GRADLE_ENCRYPTION_KEY secret lets Backend keep Gradle's configuration cache between runsRepository secrets2026-10-01

Not covered yet ​

  • An agent session holds the owner's admin token: only the guard hook keeps it from editing the ruleset
  • Container images and a deployment for the backend and frontend: #421
  • Detekt and coverage reports on every pull request: #404
  • Documentation generation as part of the build: #395
  • Frontend upgrade, then its linting and tests in the frontend lane: #388, then #400
  • The judge is required by /implement-issue, not by a hook: the pull request shows its verdict line, which the maintainer checks before merging
  • The hook tests run by hand, not in CI: #434

MIT Licensed