Skip to content
Open
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
37 changes: 34 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,36 @@
uv run ruff check tooling/
uv run ruff format --check tooling/

# Job 2b: Lint GitHub Actions workflows
# Job 2b: Lint Protobuf files
lint-proto:
name: Lint Protobuf
runs-on: ubuntu-latest
timeout-minutes: 5

steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # Need full history for breaking change detection

- name: Install buf
uses: bufbuild/buf-setup-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}

- name: Lint protobuf files
run: cd proto && buf lint

- name: Check protobuf formatting
run: cd proto && buf format --diff --exit-code

- name: Check for breaking changes
if: github.event_name == 'pull_request'
run: |
cd proto
buf breaking --against "../.git#branch=origin/main,subdir=proto"

# Job 2c: Lint GitHub Actions workflows
lint-github-actions:
name: Lint GitHub Actions
runs-on: ubuntu-latest
Expand Down Expand Up @@ -179,7 +208,7 @@
path: pkg/plugin/gen/

- name: Install golangci-lint
run: |

Check warning on line 211 in .github/workflows/ci.yml

View workflow job for this annotation

GitHub Actions / Lint GitHub Actions

[actionlint] reported by reviewdog 🐶 shellcheck reported issue in this script: SC2046:warning:2:17: Quote this to prevent word splitting [shellcheck] Raw Output: w:.github/workflows/ci.yml:211:9: shellcheck reported issue in this script: SC2046:warning:2:17: Quote this to prevent word splitting [shellcheck]
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \
| sh -s -- -b $(go env GOPATH)/bin v2.5.0

Expand Down Expand Up @@ -445,7 +474,7 @@
name: Build All Components
runs-on: ubuntu-latest
timeout-minutes: 20
needs: [generate-proto, lint-rust, lint-python, lint-github-actions, lint-go, test-proxy, test-patterns, test-integration]
needs: [generate-proto, lint-rust, lint-python, lint-proto, lint-github-actions, lint-go, test-proxy, test-patterns, test-integration]

steps:
- name: Checkout
Expand Down Expand Up @@ -573,15 +602,16 @@
name: CI Status Check
runs-on: ubuntu-latest
timeout-minutes: 5
needs: [lint-rust, lint-python, lint-github-actions, lint-go, test-proxy, test-patterns, test-integration, validate-docs, build]
needs: [lint-rust, lint-python, lint-proto, lint-github-actions, lint-go, test-proxy, test-patterns, test-integration, validate-docs, build]
if: always()

steps:
- name: Check all jobs status
id: ci_check
run: |

Check warning on line 611 in .github/workflows/ci.yml

View workflow job for this annotation

GitHub Actions / Lint GitHub Actions

[actionlint] reported by reviewdog 🐶 shellcheck reported issue in this script: SC2086:info:16:28: Double quote to prevent globbing and word splitting [shellcheck] Raw Output: i:.github/workflows/ci.yml:611:9: shellcheck reported issue in this script: SC2086:info:16:28: Double quote to prevent globbing and word splitting [shellcheck]

Check warning on line 611 in .github/workflows/ci.yml

View workflow job for this annotation

GitHub Actions / Lint GitHub Actions

[actionlint] reported by reviewdog 🐶 shellcheck reported issue in this script: SC2086:info:12:28: Double quote to prevent globbing and word splitting [shellcheck] Raw Output: i:.github/workflows/ci.yml:611:9: shellcheck reported issue in this script: SC2086:info:12:28: Double quote to prevent globbing and word splitting [shellcheck]
if [[ "${{ needs.lint-rust.result }}" != "success" ]] || \
[[ "${{ needs.lint-python.result }}" != "success" ]] || \
[[ "${{ needs.lint-proto.result }}" != "success" ]] || \
[[ "${{ needs.lint-github-actions.result }}" != "success" ]] || \
[[ "${{ needs.lint-go.result }}" != "success" ]] || \
[[ "${{ needs.test-proxy.result }}" != "success" ]] || \
Expand All @@ -608,6 +638,7 @@
# Build job results summary
JOBS="Lint Rust: ${{ needs.lint-rust.result == 'success' && '✅' || '❌' }}
Lint Python: ${{ needs.lint-python.result == 'success' && '✅' || '❌' }}
Lint Protobuf: ${{ needs.lint-proto.result == 'success' && '✅' || '❌' }}
Lint GitHub Actions: ${{ needs.lint-github-actions.result == 'success' && '✅' || '❌' }}
Lint Go: ${{ needs.lint-go.result == 'success' && '✅' || '❌' }}
Test Proxy: ${{ needs.test-proxy.result == 'success' && '✅' || '❌' }}
Expand Down
35 changes: 33 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ watch-proxy: ## Watch and rebuild proxy on changes (requires cargo-watch)
watch-test: ## Watch and rerun tests on changes (requires cargo-watch)
@cd prism-proxy && cargo watch -x test

fmt: fmt-rust fmt-go fmt-python ## Format all code
fmt: fmt-rust fmt-go fmt-python fmt-proto ## Format all code

fmt-rust: ## Format Rust code
$(call print_blue,Formatting Rust code...)
Expand All @@ -476,7 +476,13 @@ fmt-python: ## Format Python code with ruff
@uv run ruff format tooling/
$(call print_green,Python code formatted)

lint: lint-rust lint-go lint-python lint-workflows ## Lint all code and workflows
fmt-proto: ## Format protobuf files with buf
$(call print_blue,Formatting protobuf files...)
@command -v buf >/dev/null 2>&1 || { echo "⚠️ buf not installed. Install with: brew install bufbuild/buf/buf"; exit 1; }
@cd proto && buf format -w
$(call print_green,Protobuf files formatted)

lint: lint-rust lint-go lint-python lint-proto lint-workflows ## Lint all code, proto, and workflows

lint-rust: ## Lint Rust code with clippy
$(call print_blue,Linting Rust code...)
Expand Down Expand Up @@ -524,6 +530,30 @@ lint-parallel-critical: lint-rust lint-python ## Lint critical categories only i
lint-parallel-list: ## List all available linter categories
@uv run tooling/parallel_lint.py --list

lint-proto: lint-proto-buf lint-proto-breaking ## Lint protobuf files and check for breaking changes

lint-proto-buf: ## Lint protobuf files with buf
$(call print_blue,Linting protobuf files with buf...)
@command -v buf >/dev/null 2>&1 || { echo "⚠️ buf not installed. Install with: brew install bufbuild/buf/buf"; exit 1; }
@cd proto && buf lint
$(call print_green,Protobuf linting complete)

lint-proto-breaking: ## Check for breaking changes in protobuf files against main branch
$(call print_blue,Checking for protobuf breaking changes...)
@command -v buf >/dev/null 2>&1 || { echo "⚠️ buf not installed. Install with: brew install bufbuild/buf/buf"; exit 1; }
@if git rev-parse --verify origin/main >/dev/null 2>&1; then \
cd proto && buf breaking --against "../.git#branch=origin/main,subdir=proto" && \
printf "$(GREEN)✓ No breaking changes detected$(NC)\n"; \
else \
printf "$(YELLOW)⚠️ Cannot check breaking changes: origin/main not found$(NC)\n"; \
fi

lint-proto-format: ## Check protobuf file formatting with buf
$(call print_blue,Checking protobuf file formatting...)
@command -v buf >/dev/null 2>&1 || { echo "⚠️ buf not installed. Install with: brew install bufbuild/buf/buf"; exit 1; }
@cd proto && buf format --diff --exit-code
$(call print_green,Protobuf formatting check complete)

lint-workflows: ## Lint GitHub Actions workflows with actionlint
$(call print_blue,Linting GitHub Actions workflows...)
@command -v actionlint >/dev/null 2>&1 || { echo "⚠️ actionlint not installed. Install with: brew install actionlint"; exit 1; }
Expand All @@ -537,6 +567,7 @@ lint-fix: ## Auto-fix linting issues where possible
@cd prism-proxy && cargo clippy --fix --allow-dirty -- -D warnings
@uv run ruff check --fix tooling/
@uv run ruff format tooling/
@command -v buf >/dev/null 2>&1 && cd proto && buf format -w || echo "⚠️ buf not installed, skipping proto format"
$(call print_green,Auto-fix complete)

##@ Podman & Compose
Expand Down
12 changes: 10 additions & 2 deletions proto/buf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ lint:
- ENUM_VALUE_PREFIX
- RPC_REQUEST_STANDARD_NAME
- RPC_RESPONSE_STANDARD_NAME
# Allow reusing common response types like HeartbeatAck across multiple RPCs
- RPC_REQUEST_RESPONSE_UNIQUE
# Allow unversioned packages for options and common types
- PACKAGE_VERSION_SUFFIX
# Allow "Interface" suffix for backend interface services (Layer 1)
Expand All @@ -25,8 +27,14 @@ breaking:
use:
- FILE
except:
# Allow adding fields (backward compatible)
- FIELD_SAME_LABEL
# Allow adding fields (backward compatible) - using new rules
- FIELD_SAME_CARDINALITY
- FIELD_WIRE_COMPATIBLE_CARDINALITY
- FIELD_WIRE_JSON_COMPATIBLE_CARDINALITY
# Allow enum value name changes (for UNSPECIFIED zero value migration)
- ENUM_VALUE_SAME_NAME
Comment on lines +34 to +35
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disabling ENUM_VALUE_SAME_NAME allows changing enum value names, which is dangerous for API stability. This exception appears to be added specifically to allow the breaking enum renumbering changes in launcher.proto. This undermines the breaking change detection that buf provides and could hide legitimate breaking changes in future updates.

Suggested change
# Allow enum value name changes (for UNSPECIFIED zero value migration)
- ENUM_VALUE_SAME_NAME

Copilot uses AI. Check for mistakes.
# Allow file deletions (control_plane.proto was already removed)
- FILE_NO_DELETE

# Code generation inputs
build:
Expand Down
86 changes: 43 additions & 43 deletions proto/prism/common/errors.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ syntax = "proto3";

package prism.common;

option go_package = "github.com/jrepp/prism-data-layer/proto/gen/prism/common";

import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";

option go_package = "github.com/jrepp/prism-data-layer/proto/gen/prism/common";

// Error represents a structured error response with rich context for distributed systems.
//
// Design Philosophy:
Expand Down Expand Up @@ -58,7 +58,7 @@ message Error {
map<string, string> metadata = 13;

// Stack trace (for debugging, should be filtered in production responses)
optional string stack_trace = 14 [deprecated = true]; // Use debug_info instead
optional string stack_trace = 14 [deprecated = true]; // Use debug_info instead

// Debug information (only populated in debug mode)
optional DebugInfo debug_info = 15;
Expand All @@ -72,57 +72,57 @@ enum ErrorCode {
ERROR_CODE_OK = 200;

// 4xx Client Errors (caller should fix)
ERROR_CODE_BAD_REQUEST = 400; // Invalid request syntax/parameters
ERROR_CODE_UNAUTHORIZED = 401; // Authentication required
ERROR_CODE_FORBIDDEN = 403; // Authenticated but not authorized
ERROR_CODE_NOT_FOUND = 404; // Resource doesn't exist
ERROR_CODE_METHOD_NOT_ALLOWED = 405; // Operation not supported
ERROR_CODE_CONFLICT = 409; // Resource state conflict
ERROR_CODE_GONE = 410; // Resource permanently deleted
ERROR_CODE_PRECONDITION_FAILED = 412; // Precondition not met (e.g., CAS)
ERROR_CODE_PAYLOAD_TOO_LARGE = 413; // Request exceeds size limits
ERROR_CODE_UNPROCESSABLE_ENTITY = 422; // Validation failed
ERROR_CODE_TOO_MANY_REQUESTS = 429; // Rate limit exceeded
ERROR_CODE_BAD_REQUEST = 400; // Invalid request syntax/parameters
ERROR_CODE_UNAUTHORIZED = 401; // Authentication required
ERROR_CODE_FORBIDDEN = 403; // Authenticated but not authorized
ERROR_CODE_NOT_FOUND = 404; // Resource doesn't exist
ERROR_CODE_METHOD_NOT_ALLOWED = 405; // Operation not supported
ERROR_CODE_CONFLICT = 409; // Resource state conflict
ERROR_CODE_GONE = 410; // Resource permanently deleted
ERROR_CODE_PRECONDITION_FAILED = 412; // Precondition not met (e.g., CAS)
ERROR_CODE_PAYLOAD_TOO_LARGE = 413; // Request exceeds size limits
ERROR_CODE_UNPROCESSABLE_ENTITY = 422; // Validation failed
ERROR_CODE_TOO_MANY_REQUESTS = 429; // Rate limit exceeded

// 5xx Server Errors (caller should retry)
ERROR_CODE_INTERNAL_ERROR = 500; // Unexpected internal error
ERROR_CODE_NOT_IMPLEMENTED = 501; // Feature not implemented
ERROR_CODE_BAD_GATEWAY = 502; // Upstream backend error
ERROR_CODE_SERVICE_UNAVAILABLE = 503; // Temporarily unavailable
ERROR_CODE_GATEWAY_TIMEOUT = 504; // Upstream timeout
ERROR_CODE_INSUFFICIENT_STORAGE = 507; // Backend storage full
ERROR_CODE_INTERNAL_ERROR = 500; // Unexpected internal error
ERROR_CODE_NOT_IMPLEMENTED = 501; // Feature not implemented
ERROR_CODE_BAD_GATEWAY = 502; // Upstream backend error
ERROR_CODE_SERVICE_UNAVAILABLE = 503; // Temporarily unavailable
ERROR_CODE_GATEWAY_TIMEOUT = 504; // Upstream timeout
ERROR_CODE_INSUFFICIENT_STORAGE = 507; // Backend storage full

// Custom Prism Errors (600+)
ERROR_CODE_BACKEND_ERROR = 600; // Backend-specific error
ERROR_CODE_PATTERN_ERROR = 601; // Pattern-level semantic error
ERROR_CODE_BACKEND_ERROR = 600; // Backend-specific error
ERROR_CODE_PATTERN_ERROR = 601; // Pattern-level semantic error
ERROR_CODE_INTERFACE_NOT_SUPPORTED = 602; // Backend doesn't implement interface
ERROR_CODE_SLOT_ERROR = 603; // Pattern slot configuration error
ERROR_CODE_CIRCUIT_BREAKER_OPEN = 604; // Circuit breaker preventing requests
ERROR_CODE_SLOT_ERROR = 603; // Pattern slot configuration error
ERROR_CODE_CIRCUIT_BREAKER_OPEN = 604; // Circuit breaker preventing requests
}

// ErrorCategory classifies errors for metrics, alerting, and handling
enum ErrorCategory {
ERROR_CATEGORY_UNSPECIFIED = 0;
ERROR_CATEGORY_CLIENT_ERROR = 1; // User/application error
ERROR_CATEGORY_SERVER_ERROR = 2; // Internal service error
ERROR_CATEGORY_BACKEND_ERROR = 3; // Backend storage error
ERROR_CATEGORY_NETWORK_ERROR = 4; // Network connectivity issue
ERROR_CATEGORY_TIMEOUT_ERROR = 5; // Operation timed out
ERROR_CATEGORY_RATE_LIMIT_ERROR = 6; // Quota/rate limit exceeded
ERROR_CATEGORY_CLIENT_ERROR = 1; // User/application error
ERROR_CATEGORY_SERVER_ERROR = 2; // Internal service error
ERROR_CATEGORY_BACKEND_ERROR = 3; // Backend storage error
ERROR_CATEGORY_NETWORK_ERROR = 4; // Network connectivity issue
ERROR_CATEGORY_TIMEOUT_ERROR = 5; // Operation timed out
ERROR_CATEGORY_RATE_LIMIT_ERROR = 6; // Quota/rate limit exceeded
ERROR_CATEGORY_AUTHORIZATION_ERROR = 7; // Permission denied
ERROR_CATEGORY_VALIDATION_ERROR = 8; // Input validation failed
ERROR_CATEGORY_RESOURCE_ERROR = 9; // Resource not found/unavailable
ERROR_CATEGORY_CONCURRENCY_ERROR = 10; // Concurrent modification conflict
ERROR_CATEGORY_VALIDATION_ERROR = 8; // Input validation failed
ERROR_CATEGORY_RESOURCE_ERROR = 9; // Resource not found/unavailable
ERROR_CATEGORY_CONCURRENCY_ERROR = 10; // Concurrent modification conflict
}

// ErrorSeverity indicates impact level for prioritization
enum ErrorSeverity {
ERROR_SEVERITY_UNSPECIFIED = 0;
ERROR_SEVERITY_DEBUG = 1; // Informational, no action needed
ERROR_SEVERITY_INFO = 2; // Notable but expected (e.g., cache miss)
ERROR_SEVERITY_WARNING = 3; // Degraded but functional
ERROR_SEVERITY_ERROR = 4; // Operation failed, action may be needed
ERROR_SEVERITY_CRITICAL = 5; // Severe failure, immediate action required
ERROR_SEVERITY_DEBUG = 1; // Informational, no action needed
ERROR_SEVERITY_INFO = 2; // Notable but expected (e.g., cache miss)
ERROR_SEVERITY_WARNING = 3; // Degraded but functional
ERROR_SEVERITY_ERROR = 4; // Operation failed, action may be needed
ERROR_SEVERITY_CRITICAL = 5; // Severe failure, immediate action required
}

// RetryPolicy provides guidance on how/when to retry
Expand All @@ -149,11 +149,11 @@ message RetryPolicy {
// BackoffStrategy for retry timing
enum BackoffStrategy {
BACKOFF_STRATEGY_UNSPECIFIED = 0;
BACKOFF_STRATEGY_IMMEDIATE = 1; // Retry immediately
BACKOFF_STRATEGY_LINEAR = 2; // Linear backoff (delay * attempt)
BACKOFF_STRATEGY_EXPONENTIAL = 3; // Exponential backoff (delay * multiplier^attempt)
BACKOFF_STRATEGY_JITTER = 4; // Exponential with random jitter
BACKOFF_STRATEGY_NEVER = 5; // Don't retry (permanent failure)
BACKOFF_STRATEGY_IMMEDIATE = 1; // Retry immediately
BACKOFF_STRATEGY_LINEAR = 2; // Linear backoff (delay * attempt)
BACKOFF_STRATEGY_EXPONENTIAL = 3; // Exponential backoff (delay * multiplier^attempt)
BACKOFF_STRATEGY_JITTER = 4; // Exponential with random jitter
BACKOFF_STRATEGY_NEVER = 5; // Don't retry (permanent failure)
}

// ErrorDetail provides structured error information
Expand Down
12 changes: 6 additions & 6 deletions proto/prism/common/metadata.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ syntax = "proto3";

package prism.common;

option go_package = "github.com/jrepp/prism-data-layer/proto/gen/prism/common";

import "prism/common/types.proto";

option go_package = "github.com/jrepp/prism-data-layer/proto/gen/prism/common";

// Metadata for stored items
message ItemMetadata {
// Compression info
Expand All @@ -26,13 +26,13 @@ message ItemMetadata {
}

message EncryptionInfo {
string algorithm = 1; // e.g., "AES-256-GCM"
string key_id = 2; // Key version for rotation
string algorithm = 1; // e.g., "AES-256-GCM"
string key_id = 2; // Key version for rotation
}

message ChunkMetadata {
int32 total_chunks = 1;
int32 chunk_size_bytes = 2;
string hash_algorithm = 3; // e.g., "SHA256"
bytes hash = 4; // Hash of complete data
string hash_algorithm = 3; // e.g., "SHA256"
bytes hash = 4; // Hash of complete data
}
8 changes: 4 additions & 4 deletions proto/prism/common/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@ message Timestamp {

// UUID (standard 128-bit UUID)
message UUID {
string value = 1; // UUID string format
string value = 1; // UUID string format
}

// Cursor for pagination
message Cursor {
bytes token = 1; // Opaque pagination token
bytes token = 1; // Opaque pagination token
}

// Time range
message TimeRange {
int64 start_millis = 1; // Inclusive
int64 end_millis = 2; // Exclusive
int64 start_millis = 1; // Inclusive
int64 end_millis = 2; // Exclusive
}

// Tags for filtering/indexing
Expand Down
Loading
Loading