Skip to content

Latest commit

 

History

History
160 lines (131 loc) · 10.5 KB

File metadata and controls

160 lines (131 loc) · 10.5 KB

CLAUDE.md

Project Standards

Files matching **

Database migration

The devcontainer has two databases: seed (postgres) and prod-like (prodlike), controlled by SIPPY_DATA_MODE (default: seed).

To migrate both databases:

go run ./cmd/sippy migrate --database-dsn "$SIPPY_SEED_DATABASE_DSN"
go run ./cmd/sippy migrate --database-dsn "$SIPPY_PRODLIKE_DATABASE_DSN"

If no env vars are set, the dev default is: postgresql://postgres:password@localhost:5432/postgres

Restore prod-like from a backup: scripts/restore_prodlike_db.sh or MCP restore_prodlike_db. Instructions and slash-command text are defined in .apm/prompts/sippy-dev-restore-prodlike.prompt.md; run make apm after changing that file so apm install redeploys generated command files. Stop sippy serve first.

Linting

Run lint: make lint

Testing

Run unit tests: make test

This runs Go tests via gotestsum and sippy-ng Vitest tests.

Run integration tests: make integration

This runs SQL-level tests against a real PostgreSQL instance using testcontainers-go. Requires Docker or Podman.

  • When your change alters setup steps, usage instructions, or architecture, update the relevant README (root README.md, sippy-ng/README.md, mcp/README.md, etc.) in the same PR.
  • When API endpoints are added, removed, or modified, update pkg/api/README.md (or create it if it doesn't exist).
  • When configuration options, environment variables, or CLI flags change, update config/README.md or the relevant section of the root README.md.
  • When new conventions, workflows, or tooling are introduced, consider adding or updating an .apm/instructions/ file so that AI coding assistants stay aligned with the project's practices.
  • When your change affects a feature documented in docs/features/, update the relevant feature doc in the same PR. Feature docs describe purpose, data flow, and key code locations; keep them accurate as the implementation evolves. If you add a new major feature, create a new doc in docs/features/.
  • Documentation and code belong in the same PR; never treat a docs update as a follow-up task.
  • Do not use em dashes when writing docs. Use commas, parentheses, or periods instead.
  • Files under pkg/**/jobrunscan**/, pkg/**/jobrunannotator**/, pkg/api/jobartifacts/**, and sippy-ng/src/component_readiness/JobArtifactQuery.js are part of the symptoms feature documented in docs/features/job-analysis-symptoms.md. Update this document when there are changes to data models, API surface, or data flow in the symptoms feature.

APM context generation

Sources under .apm/ (instructions, prompts, apm.yml, etc.) drive generated agent context. After editing those files, run make apm to regenerate AGENTS.md, CLAUDE.md, GEMINI.md, and the integrated copies under .claude/, .cursor/, .gemini/, and .opencode/. CI enforces freshness with make verify-apm.

Slash / agent commands: content under .apm/prompts/*.prompt.md is the single source of truth. apm install (part of make apm) copies each prompt into editor command targets (e.g. .claude/commands/, .opencode/commands/, .gemini/commands/). Do not add those generated paths by hand or installs will skip them as unmanaged duplicates.

Sippy (CIPI - Continuous Integration Private Investigator) is a tool used within the OpenShift engineering organization to analyze CI job results. Its primary goals are to:

  • Provide insights into job and test statistics.
  • Monitor release health and detect regressions.
  • Support release management decisions through statistical analysis (e.g., Component Readiness).

The system consists of:

  • A Go-based API backend.
  • A React/Material-UI frontend (located in sippy-ng).
  • Data sources including PostgreSQL, and BigQuery
  • A headless daemon for asynchronous processing

Favor clarity and maintainability over cleverness. Comments should be minimal, helpful, and explain the "why" not the "what".

Files matching **/*.go

  • Follow idiomatic Go practices.
  • Choose names carefully using concise but appropriately descriptive words; in the scope of a package, a name need only describe its function relative to that package. Provide docstring for every package-level name explaining why it exists and describing any parameter whose purpose is not completely obvious from name and context.
  • Keep packages, structs, and methods focused on a single clear conceptual "chunk": a package should represent one cohesive concept, a struct should represent a single entity in the scope of its package, and a method should operate on a single level of abstraction. Prefer descriptive names that evoke a specific concept (e.g., "TestResultAggregator" not "Manager"). Avoid generic names like "Manager", "Handler", "Util" without specific context, and method names that don't indicate what they do (e.g., "Process", "Handle", "Do"). Structs with more than about 7 top-level fields should be refactored into focused sub-types.
  • When adding or updating APIs, use HATEOAS in responses to support discoverability and consistent client interaction.
  • Use k8s.io/apimachinery/pkg/util/sets (e.g. sets.New[string]()) to deduplicate or collect unique strings. Do not use map[string]bool as a hand-rolled set.
  • Prefer structured logging where it makes sense; particularly for names and IDs, and often for counts, a log.WithField() call is preferred over formatting values into a string.
  • When modifying any data provider (BigQuery or PostgreSQL), ensure parity between both implementations. Changes to query logic, filtering, or returned data in one provider must be reflected in the other.
  • Timestamps and dates: Use proper types, never epoch integers.
    • PostgreSQL columns: use TIMESTAMP WITH TIME ZONE for timestamps and DATE for date-only values. All timestamps are UTC.
    • Go structs: use time.Time for timestamps. Use civil.Date (cloud.google.com/go/civil) for date-only values (e.g., GA dates, development start dates). Do not use string for date or timestamp fields; let JSON marshaling produce the correct format.
    • API responses: timestamps serialize as RFC 3339 strings, dates as YYYY-MM-DD strings. Never return epoch millisecond integers. Never manually format with time.Format() into a string field.
    • Materialized views: project timestamp columns directly from the source table. Do not convert to epoch with EXTRACT(epoch FROM ...).
    • GORM model tags: include gorm:"type:date" on date-only columns so GORM and migrations use the correct PostgreSQL type.
  • Check pkg/util/ for existing helper functions before adding inline utility logic. Avoid calling the same utility function multiple times with identical arguments in the same code path.
  • Never ignore returned errors as _ without clear justification. Errors should be wrapped with context using fmt.Errorf with %w. Avoid panic() except in init() or fatal conditions. Check for nil before dereferencing pointers.
  • Never concatenate or format SQL queries with values directly from user input. Always use placeholders for parameters in queries, preferably named (@Name).
  • Structs used with GORM that have fields not backed by database columns (such as computed or API-only fields) must include the gorm:"-" tag to explicitly exclude them from GORM operations. BigQuery struct fields must have bigquery:"column_name" tags that exactly match the BigQuery query result schema names, whether from table columns or aliases (give computed fields aliases).
  • After making changes, always run gofmt -w on modified files to ensure proper formatting.

Files matching **/*_test.go

  • Use go vet and go test ./pkg/... to validate changes before resorting to a full e2e run.
  • Run make e2e 2>&1 | tee e2e-test.log to verify your changes don't break end-to-end tests. You MUST read the log file (e2e-test.log) for results. Do not re-run e2e just to grep for different things. All output is already in the log file.
  • Do not mock storage clients (BigQuery, GCS, Postgres, etc.) when constructing golang unit tests. Go does not make it easy to substitute mocks for concrete SDK clients, and mock-heavy tests tend to verify the mock implementation rather than real behavior. Instead, structure code to separate pure logic from client calls. Unit test the logic functions directly (validation, data transformation, result aggregation and analysis). Enable testing against real storage systems with functional tests that skip unless the user supplies connection credentials via environment variables (see releasesync_functional_test.go for the pattern).
  • When a struct method needs a single, narrow query or RPC that would otherwise force a database connection in tests, extract that call behind a function type field on the struct (e.g. type counterFunc func(id uint) (int, error)). This is a thin adapter seam, not a general-purpose mock — the function type replaces one specific call, not an entire storage client. The production constructor wires the real implementation; tests supply a closure. See regressiontracker.go (failureCounterFunc) for the pattern. This does not override the rule above: do not mock or stub BigQuery, GCS, Postgres clients, or any broad storage interface.
  • Prefer table-driven tests with descriptive case names. Search the same package for existing test patterns before writing new ones.
  • New or modified functionality must include test coverage: new Go functions and methods should have corresponding unit tests, bug fixes should include a regression test that fails without the fix, and pure functions (no DB/external dependencies) should always be tested. If a function is hard to test, consider refactoring it into simpler functions (disentangle separate conceptual chunks). Exceptions: trivial changes (renaming, formatting, comments), generated code or configuration-only changes, and refactors already covered by existing tests.

This file was generated by APM CLI. Do not edit manually. To regenerate: apm compile