Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
def70c3
feat: scaffold edge/cockpit with Vue 3 + Vuetify 3; health checks via…
bojeanson Apr 1, 2026
6d6a7c6
chore(cockpit): add ESLint 9 flat config, Vitest, and CI pipeline
bojeanson Apr 3, 2026
08f688c
feat(cockpit): OperatorView — trigger polling, last inspection, item …
bojeanson Apr 3, 2026
81850c2
feat(cockpit): TechnicianView — inline config editor with cameras and…
bojeanson Apr 3, 2026
9b19f04
feat(cockpit): TechnicianView — camera decision rules and item rule e…
bojeanson Apr 3, 2026
60d3ba6
feat(cockpit): App — collapsible navigation sidebar
bojeanson Apr 3, 2026
efedcc2
test(cockpit): unit tests for StatusBar and AdminView
bojeanson Apr 3, 2026
75b47d4
fix(camera): re-initialize backends in lifespan after hot-reload
bojeanson Apr 3, 2026
fc6d756
feat(orchestrator): refactor API routes, config management and domain…
bojeanson May 21, 2026
f2c4c09
test(orchestrator): fix functional tests — API contract, fixture path…
bojeanson May 21, 2026
502a1a5
chore(orchestrator): add docker-build and docker-push Makefile targets
bojeanson May 21, 2026
3a9bca3
fix(tflite_serving): defensive lifespan, 503 on unready state, TestCl…
bojeanson May 21, 2026
2157357
chore(tflite_serving): add update, docker-build and docker-push Makef…
bojeanson May 21, 2026
7aa1c10
feat(cockpit): feature-first architecture with TanStack Query, Pinia …
bojeanson May 21, 2026
439bcd2
fix(cockpit): port 8080 for rootless Podman and Makefile targets
bojeanson May 21, 2026
129b16a
chore(camera): add docker-build, docker-push and serve-webcam Makefil…
bojeanson May 21, 2026
b1e048d
feat(camera): align service to architecture — hot-reload fix, capture…
bojeanson May 21, 2026
6f2a87e
docs: add quality gates rule to CLAUDE.md
bojeanson May 21, 2026
7627f7c
chore: add local dev stack launcher and cockpit type_check CI step
bojeanson May 21, 2026
70188e9
chore: update .gitignore and add camera fake images test fixtures
bojeanson May 21, 2026
2d1eafd
chore: drop edge workspace — move to per-service lockfiles
bojeanson May 21, 2026
6355702
chore(camera): migrate dev to dependency-groups and generate uv.lock
bojeanson May 21, 2026
a003df9
chore(orchestrator,tflite_serving): migrate dev to dependency-groups …
bojeanson May 21, 2026
4684084
fix(camera): remove device_index from health() test — not part of the…
bojeanson May 21, 2026
b2e0a0a
chore(cockpit): commit package-lock.json and unignore it
bojeanson May 21, 2026
1141c95
feat(cockpit): add shared/lib/http — axios instance for API calls
bojeanson May 21, 2026
0d77059
docs: preserve example station configs in docs/examples/configs/
bojeanson May 21, 2026
00c95c2
refactor(orchestrator): \${VAR:-default} config interpolation + --rel…
bojeanson May 21, 2026
193ef7d
chore: remove test config toto.json + delegate vio-edge-local-up to s…
bojeanson May 21, 2026
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
42 changes: 42 additions & 0 deletions .github/workflows/ci_edge_cockpit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: CI edge-cockpit
run-name: edge-cockpit CI on branch >> ${{ github.ref_name }} <<

on:
workflow_dispatch:
push:
branches:
- main
pull_request:
paths:
- "edge/cockpit/**"

jobs:
lint_and_test_on_edge_cockpit:
name: Run JS linter and unit tests
runs-on: ubuntu-latest
permissions:
checks: write
contents: read

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
cache-dependency-path: "./edge/cockpit/package-lock.json"

- name: Install dependencies
run: make install
working-directory: ./edge/cockpit

- name: TypeScript type check
run: make type_check
working-directory: ./edge/cockpit

- name: Run unit tests
run: make unit_tests
working-directory: ./edge/cockpit
17 changes: 13 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ eggs/
.eggs/
lib/
lib64/
!edge/cockpit/src/**/lib/
parts/
sdist/
var/
Expand Down Expand Up @@ -198,11 +199,19 @@ grafvio_id_rsa
*.tfstate.*

edge/model_serving/models/**/*.tflite
edge/orchestrator/data_storage/*
edge/orchestrator/data/storage/*
edge/orchestrator/config/secrets
edge/orchestrator/fake_images/*
hub/deployment/edge/images/
*package-lock.json

hub/streamlit/config/secrets

# Claude Code local config
.claude/
skills/

# AI working directories
_bmad/
_bmad-output/

# Runtime data
data_storage/
edge/camera/vio_frames/
148 changes: 148 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# Frontend Architecture Rules

This repository contains a Vue 3 + TypeScript application inside a lightweight pnpm monorepo.

The priority is:

- maintainability
- scalability
- testability
- explicit architecture boundaries
- long-term code health

# Mandatory Stack

- Vue 3
- TypeScript strict
- Vite
- Pinia
- Vue Router
- TanStack Query
- Zod
- Vitest
- Vue Testing Library
- Playwright

# Architecture Style

The frontend follows:

- Feature-first architecture
- Clean Architecture concepts
- SOLID principles
- Domain-oriented design

# Source Structure

src/
app/
shared/
entities/
features/
pages/
processes/

# Layer Responsibilities

## shared

Generic reusable technical code only.

Never contains business logic.

## entities

Core business entities:

- models
- DTOs
- mappers
- contracts

Must not depend on features/pages.

## features

Business use cases and orchestration.

Can depend on:

- shared
- entities

Must not depend on pages.

## pages

Page composition only.

Minimal logic.

# Forbidden

- Fetch calls directly in components
- DTO leakage into UI
- Giant Pinia stores
- God composables
- Circular dependencies
- Business logic in pages
- Deep relative imports
- Generic "services" dumping grounds

# Required

- Explicit typing
- Zod runtime validation
- Strong separation of concerns
- Small focused files
- Repository pattern where relevant
- Testable business logic
- Smart/Dumb component separation

# Testing

Business logic must be testable without Vue runtime.

Prefer:

- pure functions
- composables
- isolated domain logic

# Import Rules

Allowed:
shared -> none
entities -> shared
features -> entities/shared
pages -> features/entities/shared
app -> all

Forbidden:

- shared importing business layers
- entities importing features/pages
- features importing pages

# Quality Gates

After modifying any file in a service, always run that service's quality gates before reporting the task as done.

| Service modified | Command to run |
|---|---|
| `edge/orchestrator/` | `cd edge/orchestrator && make lint check tests` |
| `edge/camera/` | `cd edge/camera && make lint check unit_tests` |
| `edge/model_serving/tflite_serving/` | `cd edge/model_serving/tflite_serving && make lint check unit_tests` |
| `edge/cockpit/` | `cd edge/cockpit && make lint type_check unit_tests` |

Run from the repo root. If a quality gate fails, fix it before considering the task complete.

# Expectations

Whenever generating or refactoring code:

- preserve architecture integrity
- improve separation of concerns
- reduce coupling
- avoid overengineering
- keep code production-ready
17 changes: 17 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,23 @@ vio-edge-up:
vio-up:
BUILDOS=${BUILDOS} docker compose --profile hub --profile edge up -d --build

.PHONY: install-tools ## 🔧 Install all edge services as uv tools (run once before vio-edge-local-up)
install-tools:
uv tool install --editable edge/camera
uv tool install --editable edge/model_serving/tflite_serving
uv tool install --editable edge/orchestrator
cd edge/cockpit && npm install

.PHONY: vio-edge-local-up ## 💻 Start all edge services locally (no Docker, fake cameras)
vio-edge-local-up:
lsof -ti :8000 -ti :8001 -ti :8501 -ti :3000 | xargs kill -9 2>/dev/null || true
trap 'kill 0' SIGINT SIGTERM
CAMERA_CONFIG_FILE=$(PWD)/edge/camera/camera_config.yml $(MAKE) -C edge/camera serve &
MODELS_PATH=$(PWD)/edge/model_serving/models $(MAKE) -C edge/model_serving/tflite_serving serve &
CAMERA_SERVICE_URL=http://localhost:8001 SERVING_MODEL_URL=http://localhost:8501 CONFIG_DIR=$(PWD)/edge/orchestrator/config $(MAKE) -C edge/orchestrator serve &
$(MAKE) -C edge/cockpit serve &
wait

.PHONY: vio-down ## ❌ Stop all services (model_serving, edge_orchestrator, ui)
vio-down:
docker compose --profile hub --profile edge down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
"camera_configs": {
"camera_1": {
"camera_id": "camera_1",
"source_directory": "/dev/video0",
"camera_type": "usb",
"position": "front"
"camera_type": "http",
"position": "front",
"service_url": "${CAMERA_SERVICE_URL:-http://localhost:8001}"
}
},
"binary_storage_config": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
"camera_configs": {
"camera_1": {
"camera_id": "camera_1",
"camera_type": "raspberry",
"position": "front"
"camera_type": "http",
"position": "front",
"service_url": "${CAMERA_SERVICE_URL:-http://localhost:8001}"
}
},
"pipeline_steps": {
"step_1": {
"camera_id": "camera_1",
"model_forwarder_config": {
"model_name": "marker_quality_control",
"model_serving_url": "http://edge_model_serving:8501/",
"model_serving_url": "${SERVING_MODEL_URL:-http://localhost:8501/}",
"model_version": "1"
},
"camera_rule_config": {
Expand All @@ -34,4 +35,4 @@
"expected_decision": "OK",
"threshold": 1
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
"camera_configs": {
"camera_1": {
"camera_id": "camera_1",
"camera_type": "usb",
"source_directory": "/dev/video0",
"position": "front"
"camera_type": "http",
"position": "front",
"service_url": "${CAMERA_SERVICE_URL:-http://localhost:8001}"
}
},
"pipeline_steps": {
"step_1": {
"camera_id": "camera_1",
"model_forwarder_config": {
"model_name": "marker_quality_control",
"model_serving_url": "http://edge_model_serving:8501/",
"model_serving_url": "${SERVING_MODEL_URL:-http://localhost:8501/}",
"model_version": "1"
},
"camera_rule_config": {
Expand All @@ -35,4 +35,4 @@
"expected_decision": "OK",
"threshold": 1
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@
"camera_id": "camera_1",
"camera_type": "http",
"position": "front",
"service_url": "http://localhost:8001"
"service_url": "${CAMERA_SERVICE_URL:-http://localhost:8001}"
},
"camera_2": {
"camera_id": "camera_2",
"camera_type": "http",
"position": "back",
"service_url": "http://localhost:8001"
"service_url": "${CAMERA_SERVICE_URL:-http://localhost:8001}"
}
},
"pipeline_steps": {
"step_1": {
"camera_id": "camera_1",
"model_forwarder_config": {
"model_name": "marker_quality_control",
"model_serving_url": "http://edge_model_serving:8501/",
"model_serving_url": "${SERVING_MODEL_URL:-http://localhost:8501/}",
"model_version": "1"
},
"camera_rule_config": {
Expand All @@ -31,7 +31,7 @@
"camera_id": "camera_2",
"model_forwarder_config": {
"model_name": "marker_quality_control",
"model_serving_url": "http://edge_model_serving:8501/",
"model_serving_url": "${SERVING_MODEL_URL:-http://localhost:8501/}",
"model_version": "1"
},
"camera_rule_config": {
Expand All @@ -53,4 +53,4 @@
"expected_decision": "OK",
"threshold": 1
}
}
}
Loading
Loading