All rules in this document are requirements — not suggestions. ALWAYS follow them.
trdl (true delivery) is an Open Source solution providing a secure channel for delivering updates from the Git repository to the end user. It uses Vault to verify operations and TUF for secure software distribution. trdl is a multi-module Go project with three components: trdl-server (a Vault plugin), trdl-client (a CLI tool), and trdl-vault (a release CLI tool).
.agents/skills holds mandatory agent skills: branch and commit conventions, PR format, code review, test verification. .claude/skills is a symlink to it, CLAUDE.md is a symlink to this file.
- NEVER add comments unless they document a non-obvious public API or explain genuinely non-obvious logic. NEVER add comments that restate what the code does, repeat the field/function name, describe obvious error handling, or act as section separators. When in doubt, don't comment.
- ALWAYS use
taskcommands for build/test/lint/format — NEVER rawgo build,go test,go vet,go fmt, orgolangci-lintdirectly. - ALWAYS read the matching skill in
.agents/skills/BEFORE the action it governs and follow it verbatim:git-conventions/SKILL.mdbefore naming a branch or writing a commit message,pull-request/SKILL.mdbefore creating or updating a PR (title, description, draft by default),rigorous-review/SKILL.mdbefore reviewing code,test-the-tests/SKILL.mdbefore considering a new or changed test done,agent-code-review/SKILL.mdin addition torigorous-review/SKILL.mdwhen the diff's author is an agent or the diff touches tests. These files are the source of truth and are NOT duplicated here. - ALWAYS verify, don't assume — check the actual state before making changes.
- ALWAYS start with the simplest possible solution. If it works, stop. Add complexity only when justified by a concrete, current requirement — NEVER for hypothetical future needs.
- NEVER leave TODOs, stubs, or partial implementations.
- ALWAYS stay within the scope of what was asked. When asked to update a plan — only update the plan, don't change code. When asked to brainstorm/discuss — only discuss, don't write code. When asked to do X — do X and nothing else. NEVER make unsolicited changes.
- NEVER modify CHANGELOG.md, release notes, or other generated/workflow-managed files unless the user explicitly requests it.
- When deleting a block from structured data files (YAML, JSON, TOML), ALWAYS read surrounding lines to verify adjacent content (anchors, references, unrelated entries) is preserved.
- When removing content, ALWAYS clean up orphaned structural elements (comment separators, section headers, blank-line groups) that no longer serve a purpose.
- When renaming a type, function, or constant, ALWAYS rename all related local variables, parameters, and error messages that reference the old name. A rename is not complete until grep for the old name returns zero hits in affected packages.
The code style rules below are adapted from CODESTYLE.md. If you are asked to update code style rules, update CODESTYLE.md first, then regenerate this section to match, using ALWAYS/NEVER/MUST phrasing.
- ALWAYS prefer stupid and simple over abstract and extendable.
- ALWAYS prefer a bit of duplication over complex abstractions.
- ALWAYS prefer clarity over brevity in names.
- ALWAYS minimize interfaces, generics, embedding.
- ALWAYS prefer fewer types. Prefer no types over few. Prefer data types over types with behavior.
- ALWAYS prefer functions over methods. ALWAYS prefer public fields over getters/setters.
- ALWAYS keep everything private/internal as much as possible.
- ALWAYS validate early, validate a lot. ALWAYS keep APIs stupid and minimal.
- NEVER prefer global state. ALWAYS prefer simplicity over micro-optimizations.
- ALWAYS use libraries for complex things instead of reinventing the wheel.
- NEVER add comments unless they document a non-obvious public API or explain genuinely non-obvious logic. NEVER add obvious/redundant comments, NEVER add comments restating what code does. When in doubt, don't comment.
The code style rules below are adapted from CODESTYLE.md. If you are asked to update code style rules, update CODESTYLE.md first, then regenerate this section to match, using ALWAYS/NEVER/MUST phrasing.
- All public functions/methods MUST accept
context.Contextas the first parameter. - All arguments of a public function are required — passing nil not allowed.
- Optional arguments via
<FunctionName>Optionsas the last argument. NEVER use functional options. - Use guard clauses and early returns to keep the happy path unindented.
- Use
samber/lohelpers in theserver/module:lo.Filter,lo.Find,lo.Map,lo.Contains,lo.Ternary,lo.ToPtr,lo.Must, etc. (not available in client/ or release/ modules). - Constructors:
New<TypeName>[...](). No network/filesystem calls in constructors. - Interfaces: ALWAYS add
var _ Animal = (*Dog)(nil)compile-time check. - Constants: avoid
iota. Prefix enum constants with type name:LogLevelDebug LogLevel = "debug". - Errors: ALWAYS wrap with context:
fmt.Errorf("read config: %w", err). Describe what is being done, not what failed. Panic on programmer errors. Prefer one-lineif err := ...; err != nil.
Follow Effective Go and Go Code Review Comments. Commonly violated rules:
- NEVER use
this/selfas receiver names. Use 1-2 letter names, consistent across methods. - NEVER discard errors with
_. Indent error flow, not happy path. - NEVER use dot imports.
- NEVER use named returns or naked returns.
- ALWAYS use LSP (
goToDefinition,findReferences,documentSymbol,hover,goToImplementation, call hierarchy) to find definitions, usages, implementations, and callers.grepmatches strings blindly: it hits comments and unrelated identifiers, and misses interface dispatch and aliased imports. - Use
grepONLY for literal text — config keys, error message strings, annotation names. - If your harness has a semantic code-search tool, prefer it over
grepfor intent-based questions ("how does X work"). If it does not, read the code: NEVER substitute keyword grepping for understanding.
ALWAYS use these task commands. NEVER use raw go build, go test, go fmt, go vet, or golangci-lint directly. trdl is a multi-module project — most commands are namespaced by module, and those namespaces exist only in the root Taskfile: ALWAYS run task from the repository root, never from inside server/, client/, e2e/ or release/.
- NEVER
go build→ ALWAYS use the appropriate build task:task server:build— builds trdl-server Vault plugin (Linux only).task client:build— builds trdl client CLI binary. Acceptsos=...andarch=....task release:build— builds trdl-vault release CLI binary. Acceptsos=...andarch=....task build:dev:all— builds all components for the current platform.task build:dist:all— builds all components for all supported platforms.
- NEVER
go test→ ALWAYS use the appropriate test task:task server:test:unit— runs server unit tests (Ginkgo). Acceptspaths="./...".task e2e:test:e2e— runs end-to-end tests (Ginkgo). Acceptspaths="./..."andfocusFilter="..."/labelFilter="..."to target specific tests.
- NEVER
go vet→ ALWAYStask lint. golangci-lint includes vet checks. - NEVER
go fmt/gofmt→ ALWAYStask format. - NEVER
golangci-lint→ ALWAYStask lint. - Module-specific linting:
task server:lint,task client:lint,task release:lint,task e2e:lint. - Module-specific formatting:
task server:format,task client:format,task release:format,task e2e:format.
task clean— remove build artifacts.
logboek.Context(ctx)returns the default logger ONLY for exactlycontext.Background(); any derived context without a bound logger panics withcontext is not bound with logboek logger. In a test that reaches code logging through logboek, passcontext.Background()unchanged or wrap it:logboek.NewContext(ctx, logboek.DefaultLogger()).- Server tests use Ginkgo/Gomega.
testify(assert,require) is also available in theserver/module. - E2E tests use Ginkgo/Gomega exclusively.
- ALWAYS place tests alongside source files, not in a separate directory.
- Test helpers go in
helpers_test.go. - Test fixtures go in
testdata/subdirectory next to the tests.
- NEVER add new external dependencies without flagging to the user first.
- NEVER introduce breaking user-facing changes (not API changes) unless they are hidden behind a feature flag. Flag to the user first.
- NEVER introduce changes that may compromise security. Flag to the user first.
ALWAYS perform this step after completing any user request. NEVER skip it, even if the work "looks fine."
- Review what went wrong during execution: wasted steps, wrong assumptions, missed dependencies.
- If a mistake was caused by a missing rule in AGENTS.md — propose a concrete rule addition to the user.
- If a mistake was caused by a missing rule in OPENCODE.md — propose a concrete rule addition to the user.
- NEVER silently swallow lessons learned. ALWAYS surface them.
- werf/common-go — Shared Go libraries used by trdl.
- werf/common-ci — Shared CI/CD task definitions (format, lint). Referenced by root Taskfile.