Warren is an AI agent and Slack-based security alert management tool that processes security alerts using LLM (Gemini) and manages incident response through Slack.
go test ./...- Run all testsgo test ./pkg/path/to/package- Run tests for specific packagetask- Run default tasks (mock generation and GraphQL)task mock(alias:task m) - Generate all mock filestask graphql- Generate GraphQL code from schema
cd frontend && pnpm install- Install frontend dependenciespnpm run dev- Start development serverpnpm run build- Build frontend for productionpnpm run codegen- Generate GraphQL types from schema
go tool moq- Generate mocks (handled by task commands)go tool gqlgen generate- Generate GraphQL resolvers and types
The application follows Domain-Driven Design (DDD) with clean architecture:
pkg/domain/- Domain layer with business logic, interfaces, and modelspkg/service/- Application services implementing business operationspkg/controller/- Interface adapters (HTTP, GraphQL, Slack)pkg/adapter/- Infrastructure adapters (storage, external APIs)pkg/repository/- Data persistence implementationspkg/usecase/- Application use cases orchestrating domain operations
Pipeline stages in pkg/usecase/alert_pipeline.go:
- Ingest Policy Evaluation - Transform raw alert data into Alert objects
- Tag Conversion - Convert tag names to tag IDs
- Metadata Generation - Fill missing titles/descriptions using LLM
- Enrich Policy Evaluation - Execute enrichment tasks (query/agent)
- Triage Policy Evaluation - Apply final metadata and determine publish type
serve (HTTP/Slack/GraphQL), run (CLI), chat (interactive), tool (utilities), test (testing)
- NEVER leave incomplete implementations, TODOs, or placeholder code
- NEVER skip implementation because it's complex or lengthy
- ALWAYS complete the full implementation in one go
- If a task seems too complex, break it down into smaller steps, but complete ALL steps
- Complexity is not an excuse - implement everything thoroughly
- Long code is acceptable - incomplete code is NOT
- Warren is designed to run as multiple concurrent instances (horizontal scaling). Any design that assumes single-instance will break in production
- NEVER hold cross-request state in process memory. State that must survive across separate requests, goroutines that originated elsewhere, or instance boundaries MUST be persisted to a shared backend (Firestore / GCS / Pub/Sub / Redis)
- Allowed in-memory state: only within a single continuous processing flow (e.g. variables within one HTTP request, one goroutine's local variables, one WebSocket connection's live buffer for the duration of that connection). As soon as the flow ends, the state must be gone or persisted
- Forbidden patterns:
- In-memory registry/map keyed by ID that other requests lookup (e.g.
map[SessionID]*Handlerat package level) - Singleton caches of business data without a shared backend
- Cross-goroutine coordination via channels at package scope
- Assuming a WebSocket client is always on the same instance as the goroutine publishing to it
- In-memory registry/map keyed by ID that other requests lookup (e.g.
- Required patterns:
- Firestore (or equivalent) as source of truth for all persistent state
- Pub/Sub or Firestore snapshot listener for cross-instance event fan-out
- Design reviews must explicitly verify multi-instance correctness for any new stateful component
- EVERY code change MUST be accompanied by tests that verify the change
- When adding new functionality, write tests that cover the new behavior
- When fixing a bug, write a test that reproduces the bug and verifies the fix
- When refactoring, ensure existing tests still pass and add tests if coverage gaps are found
- Do NOT consider a task complete until tests are written and passing
- Use
github.com/m-mizutani/goerr/v2for error handling - Must wrap errors with
goerr.Wrapto maintain error context - Add helpful variables with
goerr.Vfor debugging - NEVER check error messages using
strings.Contains(err.Error(), ...) - ALWAYS use
errors.Is(err, targetErr)orerrors.As(err, &target)for error type checking - Error discrimination must be done by error types, not by parsing error messages
- Tag errors with
goerr.T(errutil.TagXxx)frompkg/utils/errutilwhere appropriate (see existing code for examples) - Use
errutil.Handle(ctx, err)for error logging in background goroutines and fire-and-forget contexts — it logs the error and sends it to Sentry in one call- BAD:
logger.Error("failed to do X", "error", err) - GOOD:
errutil.Handle(ctx, goerr.Wrap(err, "failed to do X", goerr.V("id", id)))
- BAD:
- ALWAYS use
safe.Close(ctx, closer)frompkg/utils/safeto closeio.Closerresources - NEVER use
_ = x.Close()or barex.Close()— usesafe.Closeinstead for nil-safe, error-logged cleanup- BAD:
defer func() { _ = client.Close() }(),defer client.Close() - GOOD:
defer safe.Close(ctx, client)
- BAD:
- Use
github.com/m-mizutani/gtpackage for type-safe testing - Prefer Helper Driven Testing style over Table Driven Tests
- Use Memory repository from
pkg/repositoryinstead of mocks for repository testing - Use mock implementations from
pkg/domain/mock - NEVER comment out test assertions - if a test doesn't work, fix it or delete it
- NEVER use length-only checks - always verify individual IDs/values explicitly
- BAD:
gt.A(t, toDelete).Length(3)with commented out ID checks - GOOD: Check each expected ID explicitly with
gt.True(t, deleteMap[id])
- BAD:
- Test files should have
package {name}_test. Do not use same package name - Test file name convention is:
xyz.go->xyz_test.go. Other test file names (e.g.,xyz_e2e_test.go) are not allowed - Test Skip Policy:
- NEVER use
t.Skip()for anything other than missing environment variables - If a test requires infrastructure (like Firestore index), fix the infrastructure, don't skip the test
- If a feature is not implemented, write the code, don't skip the test
- The only acceptable skip pattern: checking for missing environment variables at the beginning of a test
- NEVER use
Before creating or modifying tests:
- Is there a corresponding source file for this test file?
- Does the test file name match exactly? (
xyz.go->xyz_test.go) - Are all tests for a source file in ONE test file?
- No standalone feature/e2e/integration test files?
- Do not expose unnecessary methods, structs, and variables
- Assume that exposed items will be changed. Never expose fields that would be problematic if changed
- Use
export_test.gofor items that need to be exposed for testing purposes
When making changes, before finishing the task, always:
- Run
go vet ./...,go fmt ./...to format the code - Run
golangci-lint run ./...to check lint error - Run
gosec -exclude-generated -quiet ./...to check security issue - Run tests to ensure no impact on other code
NEVER run go build to verify code. Use go vet ./... instead to check for compile errors.
- All comment and character literal in source code must be in English
- All git commit messages must be in English
- All PR titles and descriptions must be in English
- Commit messages must be a single line. No body paragraphs. State the change in one sentence. Explanation goes in the PR description, not the commit.
- When you are mentioned about
tmpdirectory, you SHOULD NOT see/tmp. You need to check./tmpdirectory from root of the repository.