Skip to content

edikt for Solo Engineers

You own the full cycle. Requirements, architecture, implementation, testing, shipping. Every decision is yours. And every decision lives in your head — which means the model doesn't have it next session.

This guide shows the actual workflow. Not summaries. Not feature lists. The conversation between you and the model, from first init to the moment governance starts compounding.


1. First init — describe the project, get governance

You have a Go backend for a file-upload processing service. Chi router, PostgreSQL, hexagonal architecture. You've been running Claude Code on it for two weeks with a hand-maintained CLAUDE.md that's already out of date.

Claude Code — upload-service
/edikt:init
[1/3] Scanning project...
Code: Go project, 89 files. Chi framework, PostgreSQL.
Build: make build | Test: make test
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 — file-handling surface detected, path traversal
[x] error-handling — typed errors, context wrapping
[x] architecture — hexagonal layer enforcement
[x] go — error handling, data races, interfaces
[x] chi — thin handlers, middleware
[ ] 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.
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.

That took 30 seconds. The rules are in .claude/rules/. The agents are in .claude/agents/. The hooks are in .claude/settings.json. Everything the model needs to follow your standards is installed where it actually reads it — not in a wiki, not in a doc you'll forget to update.


2. The moment the model follows the rules without being told

Next morning. New session. You don't mention your architecture. You don't remind the model about error handling. You just describe the work.

Claude Code — upload-service
I need a new endpoint to handle scan-complete webhook events from our malware-scanning provider. POST /webhooks/scan-complete. It should verify the provider's signature, parse the event, and route to the right handler based on event type. Start with scan.completed and scan.failed.
I'll build the webhook handler following your hexagonal architecture.
Here's my approach:
- Domain: ScanEvent entity + ScanResultHandler port
- Application: ProcessScanEventUseCase orchestrates verification → parsing → routing
- Infrastructure: ScanProviderAdapter (signature verification), Chi handler (HTTP layer only)
The handler won't touch the database directly — it calls through the application layer. Errors will be typed (ErrInvalidSignature, ErrUnknownEventType) with context wrapping per your error-handling rules.
I'll use the raw request body for signature verification, not the parsed JSON — the provider requires the raw bytes.
Starting with the domain layer.

The model separated the layers correctly — domain, application, infrastructure. It kept the HTTP handler thin. It used typed errors with context wrapping. It knew about the provider's raw body requirement. It planned the work from the domain layer outward.

You didn't ask for any of that. The rules in .claude/rules/ told the model how your project works. The go.md rules told it how you write Go. The architecture.md rules told it about hexagonal layers. The error-handling.md rules told it about typed errors.

The model read your rules before you asked. No reminding. No re-explaining. The overhead stops here.


3. Writing a PRD through conversation

Two weeks in. You need scan-result delivery with retry logic — not just receiving scan events, but sending them to your own consumers. Before writing code, you capture the requirements.

Claude Code — upload-service
Write a PRD for scan-result delivery to our consumers. We need to send events when a scan completes, fails, or flags a file. Retry logic with backoff. Idempotency so consumers can safely receive the same event twice.
Before I draft this, a few questions to ground the requirements in your project:
1. Delivery target — are consumers external HTTP endpoints or internal services via a message queue?
2. Retry budget — how many attempts before marking delivery failed? And what's the max delay you'd accept between first failure and final attempt?
3. Idempotency scope — should idempotency be per-consumer (same event delivered to consumer A twice is deduplicated) or global (same event ID is never processed twice, period)?
4. Ordering — do consumers need events in order (scan.completed always after scan.started) or is out-of-order acceptable with idempotency handling it?
5. I see your existing ScanResultHandler port in the domain layer. Should scan-result delivery extend this port or be a separate bounded context?

Notice question 5. The model read your codebase. It found the existing port and asked whether to extend it or create something new. It's not generating from a template — it's grounding the requirements in what already exists.

Claude Code — upload-service
External HTTP endpoints. 5 retries max, exponential backoff with jitter, max delay 4 hours. Idempotency per-consumer. Out-of-order is fine. Separate bounded context — delivery is its own domain.
Got it. Drafting now.
PRD-003: Scan Result Delivery System
Status: draft
Domain: Scan Result Delivery (new bounded context)
Problem:
Consumers of our upload platform have no way to receive real-time notifications when a scan event occurs. They must poll the API, which increases latency and load.
Solution:
HTTP scan-result delivery system. Consumers register endpoints. The platform delivers events with retry logic and idempotency guarantees.
Acceptance Criteria:
1. Deliver scan.completed, scan.failed, scan.flagged events to registered consumer endpoints
2. Retry failed deliveries: exponential backoff with jitter, 5 attempts max, 4-hour ceiling
3. Per-consumer idempotency: same event + same consumer = deduplicated (consumer receives it at most once after success)
4. Delivery status tracking: pending → delivering → delivered / failed
5. Consumer endpoint registration via API
6. Signature verification so consumers can authenticate the source
7. Separate bounded context from upload processing
Saved: docs/product/prds/PRD-003-scan-result-delivery.md
Review and tell me what to change, or accept it and we'll move to the spec.
Accept it.
PRD-003 accepted. Ready for spec when you are.

The requirements survive context compaction. They're in docs/product/prds/. Tomorrow's session can reference them without you re-explaining what you're building.

Command reference: /edikt:sdlc:prd


4. Spec interview — the model asks codebase-aware questions

Claude Code — upload-service
Write a spec for PRD-003
Routing to architect for spec generation.
Before I write the spec, I've scanned your codebase and have questions:
1. Your existing migrations use goose with sequential numbering. I'll follow that convention for the delivery tables. Any reason to change?
2. I see you're using pgx for database access in the infrastructure layer. The delivery system will need a transactional outbox to guarantee event delivery — should I use pgx transactions for the outbox pattern, or do you want a separate approach?
3. ADR-001 established typed errors with context wrapping. For delivery failures, I'll create DeliveryError with cause chaining (network timeout, HTTP 5xx, signature mismatch). That consistent with your intent?
4. Your Chi middleware stack includes request ID propagation. Should delivery attempts carry the originating request ID for traceability, or generate their own?
5. Testing: your existing test setup uses testcontainers-go for PostgreSQL integration tests. I'll follow that for delivery tests. Confirm?

Every question references something real in your codebase. The migration tool. The database driver. An existing ADR. The middleware stack. The test setup. The model isn't guessing — it read the project before asking.

Claude Code — upload-service
Yes to all. Use pgx transactions for the outbox. Delivery attempts should carry the originating request ID.
Spec generated.
SPEC-003: Scan Result Delivery System
Source PRD: PRD-003
Status: draft
References: ADR-001 (error handling), ADR-002 (database conventions)
Architecture:
New bounded context: delivery/
Domain: DeliveryEvent, ConsumerEndpoint, DeliveryAttempt
Application: ScheduleDeliveryUseCase, RetryDeliveryUseCase
Infrastructure: HTTPDeliverer, PostgresOutbox, ConsumerRepository
Key decisions:
- Transactional outbox pattern via pgx — events written to outbox table in same transaction as domain events
- Exponential backoff: base 30s, factor 2, jitter ±20%, cap 4h
- Per-consumer idempotency via (event_id, consumer_id) unique constraint
- HMAC-SHA256 signatures using per-consumer secrets
Database:
3 tables: consumer_endpoints, delivery_outbox, delivery_attempts
Migrations: goose sequential, following existing convention
Saved: docs/product/specs/SPEC-003-scan-result-delivery/spec.md
Review it. I'll generate artifacts once you accept.
Before I accept — does the idempotency check use 409 or 422 for duplicate deliveries?
Checked: your errors package has no 409 handling anywhere. Every other conflict in this API returns 422 Unprocessable Entity (internal/errors/http.go). The spec said 409 — that was wrong. Fixed: idempotent duplicate deliveries now return 422, matching your existing convention.
Good catch. Accept.
SPEC-003 accepted.

Command reference: /edikt:sdlc:spec PRD-003


5. Pre-flight review — specialist agents catch what you'd miss

Claude Code — upload-service
Create a plan for SPEC-003
Breaking SPEC-003 into implementation phases.
PLAN-003: Scan Result Delivery System
Phase 1: Domain layer — entities, value objects, ports
Phase 2: Database — migrations, outbox table, repositories
Phase 3: Application layer — use cases, retry scheduling
Phase 4: Infrastructure — HTTP deliverer, signature generation
Phase 5: API — consumer registration endpoints, Chi handlers
Phase 6: Integration tests
Running pre-flight review with specialist agents...
PRE-FLIGHT REVIEW
DBA
CRITICAL Migration 007_create_delivery_outbox has no DOWN migration — add rollback before executing
WARNING No index on delivery_outbox.status — queried in every retry poll cycle, will degrade at volume
WARNING delivery_attempts.created_at missing — needed for backoff calculation
SECURITY
WARNING Consumer webhook secrets must be generated server-side with crypto/rand, not accepted as user input
PASS HMAC-SHA256 signature scheme is sound
API
PASS Consumer registration endpoint contract is stable
INFO Consider pagination on GET /consumers/{id}/deliveries — could return thousands of rows
QA
WARNING No test case for jitter bounds — backoff with jitter should be tested for min/max range
SRE
INFO Consider circuit breaker for consumer endpoints that fail repeatedly — avoid retry storms
3 findings require attention before execution.
Fix now, or proceed with known gaps?

The DBA caught the missing rollback migration. The security agent caught a secret generation issue. The QA agent caught a missing test case. These are the things you'd catch in code review — except they were caught before a single line was written.

Claude Code — upload-service
Fix the migration rollback, add the index, add the created_at column, and generate secrets server-side. The rest I'll address during implementation.
Updated. Plan ready for execution.
Phase 1 is active. Start when ready.

Five minutes to catch a migration without a rollback. After implementation, that's an hour of work and a risky production deploy.

Command reference: /edikt:sdlc:plan SPEC-003


6. The drift check — does the implementation match what was decided?

Three days later. All six phases are complete. The scan-result delivery system is built. Before you open a PR, you verify.

Claude Code — upload-service
Does the implementation match the spec for SPEC-003?
Running drift detection against the full governance chain.
Comparing: PRD-003 → SPEC-003 → artifacts → implementation
DRIFT REPORT — SPEC-003
SUMMARY
14 compliant 2 likely compliant
1 diverged 0 unknown
PRD ACCEPTANCE CRITERIA
[compliant] Deliver scan events to registered endpoints
[compliant] Retry with exponential backoff, 5 attempts, 4h cap
[compliant] Per-consumer idempotency
[compliant] Delivery status tracking
[compliant] Consumer endpoint registration
[compliant] Signature verification
SPEC REQUIREMENTS
[compliant] Transactional outbox with pgx
[compliant] HMAC-SHA256 signatures
[compliant] Goose sequential migrations with rollbacks
[compliant] Typed errors with context wrapping (per ADR-001)
[diverged] Retry backoff — spec requires jitter
expected: exponential backoff with jitter, +/-20% (spec 3.2)
found: fixed exponential backoff, no jitter
file: delivery/infrastructure/retry_scheduler.go:47
action: Add jitter to calculateBackoff()
ADR COMPLIANCE
[compliant] ADR-001: typed errors with context wrapping
[compliant] ADR-002: database conventions followed
INVARIANT COMPLIANCE
[compliant] All invariants respected
1 divergence. 1 action required.

One divergence. The retry scheduler uses exponential backoff but forgot the jitter. The spec said jitter. The implementation doesn't have it. File path, line number, clear action.

Claude Code — upload-service
Fix the jitter.
Updated delivery/infrastructure/retry_scheduler.go — added +/-20% jitter to calculateBackoff() using crypto/rand for uniform distribution.
Re-running drift check...
DRIFT REPORT — SPEC-003
SUMMARY
15 compliant 2 likely compliant
0 diverged 0 unknown
Full compliance. Implementation matches the governance chain.

Zero drift. What was decided is what got built.

Command reference: /edikt:sdlc:drift SPEC-003


7. Decision capture — governance that compiles itself

During implementation, you made a choice: transactional outbox over event-driven messaging. The Stop hook noticed.

Claude Code — upload-service
Decision signal detected:
You chose transactional outbox pattern over event-driven messaging (Kafka/NATS) for scan-result delivery reliability.
Rationale detected:
- Eliminates message broker dependency
- Guarantees delivery atomicity with domain writes
- Simpler operational model for current scale
Save this as an ADR? (y/n)
Yes.
ADR-003: Transactional Outbox for Scan Result Delivery
Status: accepted
Saved: docs/architecture/decisions/ADR-003-transactional-outbox.md
Compile governance.
Reading all accepted ADRs and active invariants...
✅ Governance compiled
governance/architecture.md
← ADR-001 (§Error handling, §Layer boundaries)
← ADR-003 (§Webhook delivery)
governance/database.md
← ADR-002 (§Migrations)
← INV-001 (§Schema changes)
3 ADRs + 1 invariant → 2 topic files + index
5 total directives
Claude will follow these directives automatically in every session.

The decision you made today is now enforcement tomorrow. Not a doc someone might read. A directive the model loads automatically, every session, before writing code.

Command references: /edikt:adr, /edikt:gov:compile


8. Next session — the model already knows everything

Thursday morning. New session. You haven't opened this project since Tuesday.

Claude Code — upload-service
[SessionStart] Loading project context...
Upload Processing Service — Go + Chi + PostgreSQL
Last session: Tue Mar 18 — completed scan-result delivery system
Active governance:
7 rules, 8 agents, 5 compiled directives
3 ADRs (error handling, DB conventions, transactional outbox)
1 invariant (migration rollbacks required)
Governance chain:
PRD-003 accepted -> SPEC-003 accepted -> implemented (0 drift)
No active plan. What would you like to work on?
I need to add a dead letter queue for webhook deliveries that exhaust all retries.
Good addition. A few things I'll account for based on your existing governance:
- ADR-003 established the transactional outbox pattern — the DLQ will be a table in the same database, not a separate message broker
- Per your error-handling rules, failed deliveries will use typed errors (DeliveryExhaustedError) with the full attempt history
- Migration will include a DOWN migration per INV-001
Want me to write a PRD for this, or is it small enough to go straight to implementation?

The model referenced ADR-003 (which you captured two days ago), your error handling rules, and your migration invariant. Without being asked. Without being reminded. The governance is working.

This is the compound effect. The first session after /edikt:init is better. The tenth session is substantially better. Each ADR compounds into enforcement. Each spec becomes a drift detection baseline. Each decision you capture makes the next session smarter.

A hand-maintained CLAUDE.md decays the moment you forget to update it. edikt's governance accumulates. You still make every call. edikt makes sure the model remembers them.

Command reference: /edikt:status

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