Project tier and type drive every rule below.
| Key | Value |
|---|---|
| TIER | B — shared non-critical (developer build tool, no SLA, used across Intrinsec Go projects) |
| TYPE | 2 — Go CLI (protoc plugin invoked at build time) |
| API_STYLE | n/a (codegen plugin, not an API server) |
| Go version | 1.18 (declared in go.mod) |
| Module path | github.com/intrinsec/protoc-gen-sanitize |
protoc-gen-sanitize is a protoc plugin (built on protoc-gen-star) that
generates sanitization methods on Go structs produced from .proto files. The
plugin reads sanitization options declared in sanitize/sanitize.proto, walks
the message tree, and emits *.pb.sanitize.go files alongside the standard
*.pb.go outputs.
A strict mode (--sanitize_opt=strict) makes the plugin fail when a string
field has no explicit sanitization option, so CI/CD pipelines that depend on
protoc-gen-sanitize can prevent unsanitized fields from slipping through.
Default agent response: English, even if user writes French. French response only when user explicitly asks French.
Caveman compression mandatory for all conversational responses (default level: full).
Code blocks, commit messages, PR descriptions, security warnings, irreversible-action
confirmations stay normal prose (Caveman auto-clarity rules). Do not disable Caveman unless
user says "stop caveman" or "normal mode".
HARD RULE: all code, comments, identifiers, doc strings, commit messages, ADRs, technical docs in English — every project type, regardless of team spoken language. User-facing strings + UI copy exempt — match audience language.
Project specifics:
- Go 1.18 minimum (raise floor to current stable when bumping deps).
- Avoid runtime reflection where compile-time type checking is possible.
- Generated test files (
tests/*.pb.go,tests/*.pb.sanitize.go) must be produced bymake test(or the CI codegen step) before runninggo test ./...orgolangci-lint run ./...— both fail withundefined: EntityNotherwise. This is by design (we do not commit generated test artifacts), so every workflow that lints or tests must run codegen first.
Every agent session in this repo must load + apply these skill packs:
- superpowers — process discipline (
brainstorming,writing-plans,executing-plans,test-driven-development,systematic-debugging,verification-before-completion,requesting-code-review). - caveman — response compression (see Language section).
Pack missing? Install per iagen-dev INSTALL.md before work.
Before any response, clarification, repository inspection, shell command, or file edit:
run superpowers:using-superpowers first, then run caveman so compression is active
for every response. Use superpowers:using-superpowers to decide which additional
skills apply, then follow the selected skill workflows.
Any feature, refactor, bugfix touching more than one function, or agent cannot reason in one pass:
- Run
superpowers:brainstorming— clarify intent + requirements. - Run
superpowers:writing-plans— persist plan atdocs/superpowers/plans/<short-name>.md(commit to git). - Execute via
superpowers:executing-plans(single-session) orsuperpowers:subagent-driven-development(parallelisable steps). - Gate completion with
superpowers:verification-before-completion— no "done" claim without evidence (test output, lint output, build output).
Trivial edits exception: typos, single-line config tweaks, self-evident one-liners skip steps 1–3 but still verify before claiming done.
Any bug, failing test, unexpected behaviour → superpowers:systematic-debugging first.
No symptom patching without root cause.
Before merge or PR for non-trivial work: run superpowers:requesting-code-review.
superpowers:test-driven-development— write or updatetests/*.proto+entity_test.goexpectations before touchingsanitizer.go.caveman-commit— commit messages.caveman-review— PR review comments.
After modifying any Go file: run golangci-lint run ./... before marking work complete.
Fix all lint errors, re-run until clean. Lint errors = task not done.
gofmt non-negotiable — zero diff allowed. Run gofmt -w . if in doubt.
In this repo, make lint runs make generate first so the linter has the codegen
output in place — see ## Linting below for the codegen-first ordering rationale.
Generated sources are read-only. Never hand-edit files produced by a code generator:
protoc/bufoutputs for gRPC + Protobuf (typically*.pb.go,*_grpc.pb.go, often undergen/,pb/, orproto/)mockgen/moqmockssqlc,ent,gqlgen,wire_gen.go,oapi-codegen,swagoutputs- any file with a
// Code generated ... DO NOT EDIT.header
To change generated code: change the source of truth (.proto, .sql, schema, interface)
then re-run the generator (buf generate, go generate ./..., sqlc generate, etc.).
Commit the regenerated files alongside the source change in the same commit.
This project's particularity: protoc-gen-sanitize IS a code generator. Two
separate categories of generated code exist:
sanitize/sanitize.pb.go— generated fromsanitize/sanitize.proto, committed (consumers import it).tests/*.pb.go,tests/*.pb.sanitize.go— generated bymake test, NOT committed (regenerated on every run; tests rely on them being fresh).
Modifying the plugin's codegen templates (in sanitizer.go) is a source-of-truth
edit, not a generated-code edit — covered by normal lint + test rules.
"Crash early, let orchestrator recover" model:
- Transient errors (network, timeout): retry 1–3× exponential backoff, log each retry at WARN. Retries exhausted → log ERROR with full context, exit non-zero.
- Structural errors (missing config, unavailable critical dep): crash immediately at startup. No retry. Never swallow errors silently. Every error includes enough context for diagnosis without accessing running pod.
For this plugin: errors during codegen must surface back to protoc via the
plugin protocol (return non-zero, write diagnostic to stderr). Strict mode
(--sanitize_opt=strict) escalates "missing sanitization option" warnings to
errors so CI/CD pipelines fail fast on unsanitized fields.
After any meaningful change (feature, bugfix touching public behaviour, API surface,
config schema, CLI flags, deps with user impact): verify README.md + docs/** still
match shipped reality before mark task done. Out-of-sync doc = task not done.
Pre-release sweep (mandatory before every tag, all release levels):
- README accurate — install steps, quickstart, examples runnable as-is.
- All in-repo doc links + references resolve (no dead anchors, no stale paths).
- Public API docs match shipped surface (endpoints, flags, env vars).
- Migration notes present for breaking changes.
- Screenshots / diagrams reflect current UI + architecture.
CHANGELOG.mdmatches release scope (see Changelog section).
Release blocked if sweep fails. Doc fix = same MR as code change, never separate follow-up.
Maintain user-friendly CHANGELOG.md at repo root. Format: Keep-a-Changelog
(https://keepachangelog.com/en/1.1.0/) + SemVer.
Every user-visible change → entry under ## [Unreleased] with one type:
Added / Changed / Deprecated / Removed / Fixed / Security.
Wording rules (user-facing, not commit log):
- End-user perspective. No commit hash, no internal module name, no implementation detail.
- Bad: "refactor auth middleware to use JWT v2 lib".
- Good: "Sessions survive backend restarts; existing tokens stay valid".
- Breaking changes prefix
**BREAKING:**+ 1-3 line migration note.
Release cut process:
- Rename
## [Unreleased]→## [X.Y.Z] - YYYY-MM-DD. - Create fresh empty
## [Unreleased]block at top. - Tag matches header version exactly (
vX.Y.Z). - Bump compare links at file bottom.
Internal-only changes (refactor with zero user impact, test infra, CI config) skip
CHANGELOG entry — but if in doubt, log under Changed.
Required on developer machine and in CI:
go≥ 1.22 (build), 1.18 module floor.protoc(binary) — codegen.protoc-gen-go(vendored viamake bin/protoc-gen-go).golangci-lint≥ v2 — installed via/lint-go-install.govulncheck— installed via/govulncheck-install.
.golangci.ymllives at repo root. Use/lint-go-configto refresh.- Lint runs after
make test(or its codegen step) so generated files are present. Running lint on a fresh checkout without codegen will fail ontests/entity_test.gowithpackage testtypecheck errors. - Zero-issue policy on tracked code (generated
*.pb.*.gofiles excluded viaissues.exclude-filesorpath-except).
After modifying go.mod / go.sum: run govulncheck ./... before marking work complete.
(vendor/ is honored via GOFLAGS=-mod=vendor in env; govulncheck has no -mod CLI flag.)
Fix called vulns: go get <module>@<fixed>, go mod tidy, re-vendor if applicable,
re-run until clean. Imported-only vulns: report to user.
Called vulns remaining = task not done.
Project specifics:
govulncheck ./...must report zero called vulnerabilities.- Uncalled CVEs in transitive modules are tracked but do not block merge — bump via Renovate when patch available.
- Wired to CI as a non-skippable job (
.github/workflows/ci.ymlvulnjob).
Tier B Go projects must commit vendor/. This repo currently does not — see
correction plan *-onboard-vendoring.md.
Once vendored:
go mod tidy && go mod vendorafter every dependency change.- CI must build with
-mod=vendor.
- Unit tests:
tests/entity_test.goagainst the live codegen output for everytests/*.protofixture. - New behavior → add a
tests/<feature>.protofixture + a test case inentity_test.go(or a new_test.gofor clarity). make testis the canonical local entry point.
This repo has no CI pipeline yet. Tier B requires at minimum:
- lint
- vet / build
- generate + test
- govulncheck
- SBOM
- secret scan
- release (tag-triggered) producing checksummed + signed binaries.
See *-onboard-ci-pipeline.md for the GitLab CI bootstrap (skill:
gitlab-cicd-go). If the repo lives on GitHub instead of GitLab, mirror the
job set in .github/workflows/.
- Tag scheme:
vMAJOR.MINOR.PATCH, Conventional Commits feeding the changelog. - Release artifact:
protoc-gen-sanitizebinary forlinux/amd64,linux/arm64,darwin/amd64,darwin/arm64. - Each artifact accompanied by:
SHA256SUMS(and.minisigor cosign signature — see release-signing correction plan).sbom.cdx.json(CycloneDX).
- Renovate or Dependabot configured. PRs auto-rebase, manual merge.
go.modcurrently pinned to old versions (2022 era). Plan*-onboard-dep-policy.mdupgrades + sets up the bot.
- gitleaks pre-commit hook + CI job. Baseline scan committed to
.gitleaks.toml(allowlist).
- All direct deps must be in the allowlist (
Apache-2.0,MIT,BSD-2-Clause,BSD-3-Clause,ISC,MPL-2.0). CI gate viasyft/fossa.
.
├── main.go # protoc plugin entrypoint
├── sanitizer.go # codegen logic (templates)
├── goimports_post_processor.go # post-processor for generated files
├── sanitize/
│ ├── sanitize.proto # plugin options schema
│ └── sanitize.pb.go # generated, committed
├── tests/
│ ├── *.proto # fixtures
│ ├── entity_test.go # depends on generated *.pb.go (not committed)
│ └── *.pb.go, *.pb.sanitize.go # generated by `make test`, NOT committed
├── Makefile
├── AGENTS.md
└── docs/
└── superpowers/plans/ # iagen-dev correction plans
The following standard sections are explicitly excluded from this project. Re-evaluate on tier change or quarterly review.
| Section | Reason | Note |
|---|---|---|
| Monitoring / alerts / dashboards | not-applicable |
Build-time codegen plugin, no runtime to instrument. Process exits after protoc invocation. |
| Authentication (Keycloak) | not-applicable |
No user-facing surface, no auth. |
| Secrets management (Vault AppRole) | not-applicable |
No runtime secrets. Plugin only reads .proto inputs and writes .go outputs. |
| Database (CNPG manifest, backups) | not-applicable |
No persistent state. |
| API contract (REST/gRPC/OpenAPI/proto schema) | not-applicable |
Plugin consumes .proto but does not serve an API. sanitize/sanitize.proto is an options schema, not an external contract. |
| Container hardening (distroless, non-root, healthcheck) | not-applicable |
No Dockerfile. Plugin is consumed as a binary on developer/CI hosts via go install. Re-evaluate if we ever publish a protoc-gen-sanitize OCI image. |
| Mobile (TYPE=6) and frontend (TYPE=3) tooling | not-applicable |
Pure Go CLI. |
Local Development (docker compose up) |
not-applicable |
Build-time plugin invoked by protoc. No services, no DB, no cache to stand up. make test covers the entire local loop. |
| Dependency Injection (constructor DI, Wire, Dig) | not-applicable |
Single-binary plugin with one entrypoint (main.go) and one codegen module (sanitizer.go). No service graph to wire. |
Logging (slog JSON, structured fields) |
not-applicable |
Plugin runs synchronously inside protoc and exits. Diagnostics go to stderr via protoc-gen-star (DEBUG_PG_SAN=1). No log shipping. |
| Testing & Architecture template (Red-Green-Refactor + edge I/O + interface-at-call-site) | replaced-by:fixture-driven codegen tests |
Project is a code generator. Tests live in tests/*.proto fixtures + tests/entity_test.go asserting against the generated output. Standard service-DDD test layering doesn't fit. |
Project Layout template (internal/ four-layer split) |
replaced-by:flat-cli-layout |
Plugin total ~3 Go files plus the sanitize/ proto package. The template's cmd/internal/domain/usecase/repository/delivery split would be 100% empty directories. Actual layout documented in ## Repository structure above. |
Carve-outs are honored by dev-update-project, dev-arch-review, and other
iagen-dev skills — they will not be re-suggested unless the user removes the row.