Skip to content

edikt for Teams

Every engineer on your team uses Claude Code. Every engineer prompts it differently. The output looks like it came from five different organizations. You know this because you're the one reviewing the PRs — catching the naming inconsistencies, the business logic in HTTP handlers, the error handling that doesn't match what the team agreed on last sprint.

Code review has become standards enforcement. That's not your job. That's a governance problem.

This guide shows the full workflow. One engineer sets up edikt, commits it, and every engineer on the team gets a governed session from that point forward.


1. One engineer sets up, commits — the whole team gets governance

Elena is the engineering lead. The team builds a support-ticketing API — Go, Chi, PostgreSQL. She opens the project in Claude Code.

Claude Code — tickets-api
/edikt:init
[1/3] Scanning project...
Code: Go project, 234 files. Chi framework, PostgreSQL.
Build: make build | Test: make test | Lint: golangci-lint
Commits: conventional commits detected
[2/3] Configuring...
Rules (✓ = recommended for your stack):
[x] code-quality — naming, correctness guardrails
[x] testing — TDD, tautological test prevention
[x] security — multi-tenant surface detected, timing attacks
[x] error-handling — typed errors, context wrapping
[x] go — error handling, data races, interfaces
[x] chi — thin handlers, middleware
[ ] architecture, api, database, frontend, observability, seo...
Agents: architect, backend, dba, qa, security, docs, api, sre
SDLC: conventional commits, PR template
Toggle items by name, or say "looks good" to proceed.
add architecture — we want the service layer pattern enforced
[x] architecture — layer boundaries, import discipline
Toggle more, or say "looks good" to proceed.
looks good
[3/3] Installing...
✓ Config, project context, 7 rules, 8 agents, 9 behaviors, CLAUDE.md
Start building — describe what you want. Rules and agents are active.

Elena commits:

bash
git add .claude/ .edikt/ docs/
git commit -m "chore: add edikt governance"
git push

That commit is the only setup step. Every engineer who pulls this branch gets the full governance — rules, agents, hooks, project identity. No Slack message saying "hey, update your CLAUDE.md." No wiki page nobody reads. One commit, done.


2. New engineer opens the project — governance is already there

Next morning. Tomasz pulls the latest changes and opens the project in Claude Code. He's never heard of edikt. He doesn't need to.

Claude Code — tickets-api
[SessionStart] Loading project context...
Ticket Management API — Go + Chi + PostgreSQL
Active governance:
7 rules, 8 agents, 0 compiled directives
0 ADRs, 0 invariants (new project)
Recent changes since your last session:
- chore: add edikt governance (Elena, 14h ago)
- New files: .claude/rules/, .claude/agents/, docs/project-context.md
What would you like to work on?
I need to add a search endpoint for tickets. GET /tickets with filters for status, date range, and assignee ID.
I'll build the search endpoint following your service layer pattern.
Approach:
- Handler: Parse query params, validate, call service. No business logic.
- Service: TicketSearchService.Search(ctx, filters) — builds query criteria, calls repository
- Repository: TicketRepository.Search(ctx, criteria) — PostgreSQL query with parameterized filters
I'll use typed errors: ErrInvalidDateRange, ErrInvalidStatus.
Pagination via cursor-based approach (not offset) for consistency at scale.
Starting with the service layer.

Tomasz didn't mention the service layer pattern. He didn't mention typed errors. He didn't mention keeping business logic out of handlers. The model read .claude/rules/architecture.md, .claude/rules/error-handling.md, and .claude/rules/chi.md — automatically, before writing anything.

This is the moment for teams. Every engineer's agent session produces the same architecture, the same patterns, the same error handling. Not because they prompt the same way. Because the governance is in the repo.


3. The code review shift — standards are already followed

Tomasz opens his PR. Elena reviews it.

The handler parses query parameters, validates them, and calls the service. No database queries. No business logic. The service builds filter criteria and calls the repository. The repository runs parameterized SQL — no string interpolation. The errors are typed with context wrapping.

Elena's review comments:

Elena: "Nice cursor-based pagination choice. One thought — should we
       add a max page size to prevent someone requesting 10,000 tickets?
       Maybe cap at 100 with a default of 20."

Elena: "The TicketSearchCriteria value object is clean. Consider adding
       a Validate() method so the service doesn't need to check each
       field individually."

Two comments. Both about design decisions. Neither about standards violations.

Before edikt, Elena's review of a similar PR would have looked like this:

Elena: "Move the SQL query out of the handler — it should go through
       the service layer."

Elena: "Don't use fmt.Errorf here — we use typed errors. See how
       the assignment handler does it."

Elena: "This string interpolation in the WHERE clause is a SQL injection
       risk. Use parameterized queries."

Elena: "The handler is doing too much — parsing, validation, querying,
       formatting. Split it up."

Elena: "We discussed using cursor-based pagination in standup last week.
       This is offset-based."

Five comments. All standards enforcement. All things the model should have gotten right on its own.

The shift isn't incremental. The category of review comment changes. Elena stops catching basics and starts evaluating design. The team's code review actually improves the codebase instead of policing it.


4. Quality gate fires — the model blocks a security issue

Two weeks in. The team has captured 5 ADRs and compiled them into governance directives. Priya is building the ticket closure endpoint.

Claude Code — tickets-api
Build the ticket closure endpoint. POST /tickets/{id}/close. Should check that the ticket is in a closeable state and update the status.

The model builds the endpoint. Handler, service, repository — following the pattern. Then:

Claude Code — tickets-api
GATE: security — critical finding
SQL injection risk in ticket/infrastructure/repository.go:89
— ticket ID from URL path used in raw SQL string
query := "UPDATE tickets SET status = 'closed' WHERE id = '" + ticketID + "'"
This gate must be resolved before proceeding.
Override this gate? (y/n)
Note: override will be logged with your git identity.
No, fix it.
Fixed. Using parameterized query:
query := "UPDATE tickets SET status = $1 WHERE id = $2"
_, err := r.db.Exec(ctx, query, StatusClosed, ticketID)
Gate cleared. Continuing.

The security agent caught raw string interpolation in a SQL query. It didn't just warn — it blocked. Priya couldn't proceed until the issue was resolved or she logged an override with her identity.

This is what quality gates do for teams. The security standard isn't a suggestion someone reads in a wiki. It's enforcement that runs on every session, for every engineer.

If Priya had overridden:

Claude Code — tickets-api
Override logged.
Gate: security — SQL injection risk
Engineer: Priya Sharma
Timestamp: 2026-03-18T14:32:00Z
File: ticket/infrastructure/repository.go:89
Proceeding. This override is visible in the governance dashboard.

The override is recorded. Elena sees it when she checks governance status. It's not surveillance — it's a record that a trade-off was made consciously, not accidentally.


5. Status dashboard — governance health at a glance

End of sprint. Elena checks the state of governance.

Claude Code — tickets-api
What's our status?
EDIKT STATUS — tickets-api
GOVERNANCE HEALTH
Rules: 7 active
code-quality, testing, security, error-handling,
architecture, go, chi
Agents: 7 installed
Decisions: 5 ADRs, 1 invariant
Directives: 7 compiled (from 5 ADRs + 1 invariant)
GOVERNANCE CHAIN
PRD-001 accepted -> SPEC-001 accepted -> implemented (0 drift)
PRD-002 accepted -> SPEC-002 accepted -> PLAN-002 Phase 4/5
GATE ACTIVITY (last 14 days)
security: 1 critical finding (resolved — Priya, Mar 18)
dba: 2 warnings (both resolved)
qa: 0 findings
api: 1 advisory (acknowledged — Tomasz, Mar 20)
ADR SUMMARY
ADR-001: Cursor-based pagination for all list endpoints
ADR-002: Typed domain errors, no sentinel errors
ADR-003: Event-driven ticket state transitions
ADR-004: Soft delete for tickets, hard delete for drafts
ADR-005: UTC timestamps everywhere, convert at API boundary
All governance is current. Last compiled: Mar 19.

Seven rules active. Five ADRs compiled into directives. One security gate fired and was resolved. One plan in progress. Elena can see exactly what's enforced, what decisions the team has made, and whether any gates were overridden.

She reads it out in the sprint review — two seconds, no prep, nothing to have written in advance.

Command reference: /edikt:status


6. New hire onboarding — zero setup, same governance from minute one

Week 6. A new engineer joins — James, straight out of a bootcamp. His first day, he clones the repo, opens it in Claude Code.

Claude Code — tickets-api
[SessionStart] Loading project context...
Ticket Management API — Go + Chi + PostgreSQL
Active governance:
7 rules, 8 agents, 7 compiled directives
5 ADRs, 1 invariant
Architecture: Service layer (handler -> service -> repository)
Key decisions:
- Cursor-based pagination on all list endpoints (ADR-001)
- Typed domain errors, no sentinel errors (ADR-002)
- Event-driven ticket state transitions (ADR-003)
- Soft delete for tickets (ADR-004)
- UTC timestamps, convert at API boundary (ADR-005)
Invariant: All database migrations require DOWN migrations.
What would you like to work on?
I need to add a field to the ticket model for tracking the SLA due date. A nullable timestamp.
I'll add the sla_due_at field. Here's my approach:
Domain:
- Add SLADueAt *time.Time to the Ticket entity
- Per ADR-005, this will be stored and returned in UTC. Frontend converts to local time at the API boundary.
Database:
- Migration 012: ALTER TABLE tickets ADD COLUMN sla_due_at TIMESTAMPTZ
- DOWN migration: ALTER TABLE tickets DROP COLUMN sla_due_at (per your migration rollback invariant)
Repository:
- Update TicketRepository.Save and .Find to include the new column
- Soft delete (ADR-004) is unaffected — field is nullable
Shall I proceed?

The model referenced three ADRs and one invariant. It knew about UTC timestamps, migration rollbacks, and soft deletes — not because James told it, but because the team's governance was committed to the repo six weeks ago.

James's first PR arrives with the right patterns. Elena's review is about the feature, not the formatting. The onboarding checklist just got shorter.

Before edikt, James would have spent his first week learning patterns by getting them wrong in code review. "We don't do it that way — check the wiki." Except the wiki is six pages and half of it is outdated.

With edikt, the governance is the wiki. It's enforced, not referenced. It's current, not aspirational. And it works from minute one.


What changes

The dynamic shifts in three places.

Code review stops being standards enforcement. When every engineer's agent reads the same rules and follows the same ADRs, the baseline is handled before the PR opens. What's left is the work that actually matters: evaluating design decisions, questioning trade-offs, catching logic errors.

Onboarding stops being knowledge transfer. The new engineer's agent session is as governed as the senior engineer's. The patterns are enforced, the decisions are documented, the invariants are active. Tribal knowledge becomes committed governance.

Consistency stops being a function of who's prompting. The engineer who writes great prompts and the engineer who writes "add a search endpoint" both get architecture-compliant output. The rules don't care how you ask. They fire every time.

One commit to set up. Zero per-engineer configuration. The governance accumulates as the team makes decisions and compiles them. Every ADR makes the next session smarter. Every invariant prevents the same mistake from happening twice.

Command reference: /edikt:init, /edikt:status, /edikt:adr, /edikt:gov:compile

Released under the Elastic License 2.0. Free to use, not for resale.