Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions .specify/memory/constitution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<!--
SYNC IMPACT REPORT
==================
Version change: N/A (initial creation) → 1.0.0
Modified principles: N/A — first version
Added sections:
- Core Principles (I–V)
- Technology Stack
- Development Workflow
- Governance
Removed sections: N/A
Templates reviewed:
- .specify/templates/plan-template.md ✅ Constitution Check section compatible
- .specify/templates/spec-template.md ✅ User story / requirements structure aligns
- .specify/templates/tasks-template.md ✅ Phase structure aligns with principle V (test-driven)
Follow-up TODOs: None — all placeholders resolved
-->

# Observātiō Constitution

## Core Principles

### I. Observability-First

Every cluster component MUST expose structured health and status data. Metrics, conditions,
events, and logs MUST be collected across all infrastructure layers — from the management
cluster down to individual Machines. Data that cannot be observed cannot be managed.

- All new API handlers MUST include structured logging with request context.
- Cluster, MachineDeployment, and Machine resources MUST expose condition-level health status.
- Error states MUST propagate to the dashboard with actionable, human-readable descriptions.

### II. Real-Time Visibility

Data presented to operators MUST reflect the current cluster state without requiring a manual
page refresh. WebSocket streaming is the primary delivery mechanism for live data.

- UI components displaying cluster state MUST consume WebSocket events, not poll REST endpoints.
- Watchers for Cluster, Machine, and MachineDeployment MUST emit delta updates on every change.
- Dashboard render latency from server event to UI update MUST stay below 2 seconds under
normal operating load.

### III. ClusterAPI Resource Model Compliance

All features and domain models MUST align with the ClusterAPI resource hierarchy:
Cluster → MachineDeployment → Machine, with optional ClusterClass support.
No proprietary infrastructure-specific abstractions are permitted at the core domain layer.

- Models in `webserver/internal/infra/models/` MUST map directly to CAPI CRDs.
- Infrastructure provider details (vSphere, AWS, etc.) MUST be treated as opaque annotations,
not first-class types in the core domain.
- CAPI resource watchers MUST handle both creation and deletion lifecycle events.

### IV. AI-Augmented Troubleshooting

The platform MUST provide AI-assisted diagnosis for cluster failure scenarios. LLM integration
MUST be grounded in actual cluster state — it MUST NOT generate advice disconnected from the
specific resource conditions currently observed.

- AI analysis MUST receive structured condition data from the affected resource before generating
output; generic prompts without cluster context are not permitted.
- LLM tool definitions MUST be scoped to specific cluster resources to reduce hallucination risk.
- AI-generated recommendations MUST be presented as operator suggestions, never as automated
remediation actions.
- Conversation history MUST be scoped to a single troubleshooting session and MUST NOT persist
across sessions without explicit user consent.

### V. Test-Driven Quality

All backend and frontend changes MUST be accompanied by tests. Tests MUST be written before or
alongside implementation — not as an afterthought appended after the feature is complete.

- Backend Go packages MUST maintain `_test.go` coverage for all exported functions and handlers.
- Frontend components containing business logic MUST include Jest tests.
- Integration tests for cluster watchers and processors MUST use CAPI fake clients that faithfully
represent the real API contract — mocks that diverge from actual CRD behaviour are not permitted.
- The full test suite (`make run-tests-backend` and `make run-tests-frontend`) MUST pass before
any PR is merged.

## Technology Stack

**Backend**: Go 1.23+ with CAPI controller-runtime client
**Frontend**: Next.js 15, React 19, TypeScript 5, Mantine UI 7, TailwindCSS 4, XYFlow 12
**Real-time transport**: WebSocket — backend connection pool + frontend `react-use-websocket`
**AI integration**: Anthropic Claude API via `webserver/internal/infra/llm`
**Build tooling**: GNU Make — canonical targets: `build`, `run-backend`, `run-frontend`,
`run-tests-backend`, `run-tests-frontend`
**Frontend package manager**: pnpm

No new runtime dependency may be introduced without updating this section and providing
justification in the feature plan's Complexity Tracking table.

## Development Workflow

1. Features MUST be developed on a dedicated branch following the naming convention
`###-feature-name` (e.g., `001-cluster-health-dashboard`).
2. The **Constitution Check** gate in `plan.md` MUST be completed and pass before Phase 0
research begins; re-check MUST occur after Phase 1 design.
3. Backend and frontend components are developed and tested independently but MUST integrate
successfully before a feature is marked complete.
4. `make build` MUST succeed before a pull request is opened.
5. All automated tests MUST pass before merge; test failures block merging with no exceptions.

## Governance

This constitution supersedes all other project practices and guidelines. In case of conflict,
the constitution wins.

Amendments require:
1. A documented rationale explaining why the change is necessary.
2. A semantic version bump: MAJOR for breaking governance changes or principle removals,
MINOR for new principles or materially expanded guidance, PATCH for clarifications.
3. An updated `Last Amended` date.
4. A migration plan when the amendment invalidates existing features or workflows.

All feature plans MUST include a **Constitution Check** section that explicitly validates
compliance with each of the five principles. Any violation MUST be justified in the
Complexity Tracking table before the plan is approved.

**Version**: 1.0.0 | **Ratified**: 2026-04-25 | **Last Amended**: 2026-04-25
98 changes: 59 additions & 39 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,74 +15,94 @@
SHELL := /usr/bin/env bash
.DEFAULT_GOAL := help

BINARY_PATH="${PWD}/output/observatio"
BUILD_PATH="../webserver/internal/web/handlers/build/"
BINARY_PATH := $(PWD)/output/observatio
FRONTEND_DIR := front
FRONTEND_OUT := $(FRONTEND_DIR)/output
EMBED_TARGET := webserver/internal/web/handlers/build
PREREQS_SCRIPT := scripts/check-prereqs.sh

## --------------------------------------
## Help
## --------------------------------------
##@ help:

help: ## Display this help
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)

## --------------------------------------
## Prerequisites
## --------------------------------------
##@ prereqs:

.PHONY: check-prereqs
check-prereqs: ## Validate all required tools (go ≥1.24, node ≥22, pnpm)
@bash $(PREREQS_SCRIPT)

## --------------------------------------
## Linters
## --------------------------------------
##@ lint:

.PHONY: lint-backend
lint-backend: ## Lint codebase
lint-backend: ## Lint backend codebase
docker run --rm -v $(PWD)/webserver:/app -w /app golangci/golangci-lint golangci-lint run -v --fix

.PHONY: lint-frontend
lint-frontend: ## Lint codebase
cd front; \
npm run lint

lint-frontend: ## Lint frontend codebase
cd $(FRONTEND_DIR) && pnpm run lint

## --------------------------------------
## Development targets
## Development
## --------------------------------------
##@ run-backend:
##@ development:

.PHONY: run-backend
run-backend: ## Build the binary using local golang
pushd webserver; \
go run . $(what) --dev; \
popd;

.PHONY: run-tests-backend
run-tests-backend: ## Run backend tests
pushd webserver; \
go test ./... -v -cover ;\
popd;
run-backend: ## Run backend server in development mode (no static hosting)
pushd webserver && go run . $(what) --dev && popd

.PHONY: run-frontend
run-frontend: ## Run the frontend locally
pushd front; \
pnpm run dev; \
popd;
run-frontend: ## Run frontend dev server (http://localhost:3000)
pushd $(FRONTEND_DIR) && pnpm run dev && popd

## --------------------------------------
## Tests
## --------------------------------------
##@ test:

.PHONY: run-tests-backend
run-tests-backend: ## Run backend test suite
pushd webserver && go test ./... -v -cover && popd

.PHONY: run-tests-frontend
run-tests-frontend: ## Run frontend tests
pushd front; \
npm run test; \
popd;
run-tests-frontend: ## Run frontend test suite
pushd $(FRONTEND_DIR) && pnpm run test && popd

.PHONY: test
test: run-tests-backend run-tests-frontend ## Run all tests (backend + frontend)

## --------------------------------------
## Building
## Build — independent stages
## --------------------------------------
##@ build:

.PHONY: build
build: ## Build the full stack
pushd front; \
npm run build; \
find ${BUILD_PATH} ! -name 'index.html' ! -name 'build' -type "f,d" -exec rm -fr {} +; \
mv output/* ${BUILD_PATH} ; \
popd;
pushd webserver; \
CGO_ENABLED=0 go build -o ${BINARY_PATH} .; \
popd;
.PHONY: build-frontend
build-frontend: ## Build frontend bundle independently (requires node ≥22 + pnpm)
@bash $(PREREQS_SCRIPT) --node
pushd $(FRONTEND_DIR) && pnpm install --frozen-lockfile && pnpm run build && popd

.PHONY: build-backend
build-backend: ## Build backend binary independently (requires go ≥1.24; run build-frontend first for full embed)
@bash $(PREREQS_SCRIPT) --go
mkdir -p output
pushd webserver && CGO_ENABLED=0 go build -o $(BINARY_PATH) . && popd

.PHONY: build
build: check-prereqs build-frontend ## Build full stack — validates prereqs, builds frontend, copies assets, compiles binary
@echo "[build] Copying frontend assets to embed target..."
rm -rf $(EMBED_TARGET)
mkdir -p $(EMBED_TARGET)
cp -r $(FRONTEND_OUT)/. $(EMBED_TARGET)/
@echo "[build] Compiling backend binary..."
mkdir -p output
pushd webserver && CGO_ENABLED=0 go build -o $(BINARY_PATH) . && popd
@echo "[build] Done — binary at $(BINARY_PATH)"
111 changes: 111 additions & 0 deletions scripts/check-prereqs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#!/usr/bin/env bash
# Validates required tools before any build step runs.
# Usage:
# check-prereqs.sh — check all tools
# check-prereqs.sh --go — check Go only
# check-prereqs.sh --node — check Node + pnpm only
#
# Exit 0 if all requested tools pass; non-zero otherwise.

set -euo pipefail

MIN_GO_MAJOR=1
MIN_GO_MINOR=24
MIN_NODE_MAJOR=22

CHECK_GO=false
CHECK_NODE=false

# If no flags given, check everything
if [[ $# -eq 0 ]]; then
CHECK_GO=true
CHECK_NODE=true
else
for arg in "$@"; do
case "$arg" in
--go) CHECK_GO=true ;;
--node) CHECK_NODE=true ;;
*) echo "[check] Unknown flag: $arg" >&2; exit 1 ;;
esac
done
fi

FAILED=false

# --- version helpers ---

version_gte() {
# Returns 0 (true) if $1 >= $2 using sort -V
printf '%s\n%s\n' "$2" "$1" | sort -V -C
}

# --- Go ---
check_go() {
if ! command -v go >/dev/null 2>&1; then
echo "[check] go MISSING — install from https://go.dev/doc/install"
return 1
fi

local raw
raw=$(go version 2>&1)
# "go version go1.24.1 linux/amd64" → "1.24.1"
local ver
ver=$(echo "$raw" | grep -oE 'go[0-9]+\.[0-9]+(\.[0-9]+)?' | head -1 | sed 's/go//')

local required="${MIN_GO_MAJOR}.${MIN_GO_MINOR}"
if version_gte "$ver" "$required"; then
echo "[check] go ${ver} ✓"
else
echo "[check] go ${ver} ✗ (need ≥ ${required} — update at https://go.dev/doc/install)"
return 1
fi
}

# --- Node ---
check_node() {
if ! command -v node >/dev/null 2>&1; then
echo "[check] node MISSING — install Node ${MIN_NODE_MAJOR} LTS from https://nodejs.org or via nvm"
return 1
fi

local ver
ver=$(node --version 2>&1 | sed 's/v//')
local major
major=$(echo "$ver" | cut -d. -f1)

if [[ "$major" -ge "$MIN_NODE_MAJOR" ]]; then
echo "[check] node ${ver} ✓"
else
echo "[check] node ${ver} ✗ (need ≥ ${MIN_NODE_MAJOR} — https://nodejs.org)"
return 1
fi
}

# --- pnpm ---
check_pnpm() {
if ! command -v pnpm >/dev/null 2>&1; then
echo "[check] pnpm MISSING — run: npm install -g pnpm"
return 1
fi
local ver
ver=$(pnpm --version 2>&1)
echo "[check] pnpm ${ver} ✓"
}

# --- run requested checks ---
if $CHECK_GO; then
check_go || FAILED=true
fi

if $CHECK_NODE; then
check_node || FAILED=true
check_pnpm || FAILED=true
fi

if $FAILED; then
echo ""
echo "[check] One or more prerequisites are missing or outdated. Resolve the issues above and retry."
exit 1
fi

echo "[check] All prerequisites satisfied."
Loading
Loading