Files
create/docs/src/silo-server/PROJECTS_AND_APPROVALS.md
forbes ceaa3acffe
Some checks failed
Build and Test / build (pull_request) Failing after 2m18s
docs: update server docs
2026-03-03 13:35:35 -06:00

8.4 KiB

Projects & Approvals — Implementation Reference

Projects

Database Schema

Migration 006 + 009

-- projects table
CREATE TABLE projects (
    id          UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    code        VARCHAR(10) UNIQUE NOT NULL,
    name        TEXT,
    description TEXT,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    created_by  TEXT                        -- added in migration 009
);

-- many-to-many junction
CREATE TABLE item_projects (
    id         UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    item_id    UUID NOT NULL REFERENCES items(id) ON DELETE CASCADE,
    project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE(item_id, project_id)
);

Items can belong to multiple projects. Project codes are immutable after creation.

API Endpoints

All gated by RequireModule("projects").

Method Path Auth Description
GET /api/projects viewer List all projects
POST /api/projects editor Create project (code: 2-10 alphanum)
GET /api/projects/{code} viewer Get single project
PUT /api/projects/{code} editor Update name/description
DELETE /api/projects/{code} editor Delete project (cascades associations)
GET /api/projects/{code}/items viewer List non-archived items in project
GET /api/projects/{code}/sheet.ods viewer Export ODS workbook (items + BOMs)
GET /api/items/{pn}/projects viewer Get projects for an item
POST /api/items/{pn}/projects editor Add item to projects ({"projects":["A","B"]})
DELETE /api/items/{pn}/projects/{code} editor Remove item from project

Backend Files

File Contents
internal/db/projects.go ProjectRepository — 13 methods: List, GetByCode, GetByID, Create, Update, Delete, AddItemToProject, AddItemToProjectByCode, RemoveItemFromProject, RemoveItemFromProjectByCode, GetProjectsForItem, GetProjectCodesForItem, GetItemsForProject, SetItemProjects
internal/api/handlers.go Handlers: HandleListProjects, HandleCreateProject, HandleGetProject, HandleUpdateProject, HandleDeleteProject, HandleGetProjectItems, HandleGetItemProjects, HandleAddItemProjects, HandleRemoveItemProject
internal/api/ods.go HandleProjectSheetODS — multi-sheet ODS export with items list + per-assembly BOM sheets
internal/api/routes.go Route registration under /api/projects and /api/items/{pn}/projects

Frontend

Page: web/src/pages/ProjectsPage.tsx at route /projects

Features:

  • Sortable table (code, name, description, item count, created date)
  • Create form with code validation (2-10 chars, auto-uppercase)
  • Inline edit (name/description only, code immutable)
  • Delete with confirmation dialog
  • Item count fetched per-project in parallel
  • Catppuccin Mocha theme, conditionally shown in sidebar when module enabled

Config

modules:
  projects:
    enabled: true   # default true

Not Yet Implemented

  • Project-level permissions / ownership (all projects visible to all viewers)
  • Project-based filtering in the main items list page
  • Bulk item operations across projects
  • Project archival / soft-delete
  • Project hierarchies or team assignment

Approvals & ECO Workflows

Database Schema

Migration 018 + 019

-- item_approvals table
CREATE TABLE item_approvals (
    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    item_id       UUID NOT NULL REFERENCES items(id) ON DELETE CASCADE,
    workflow_name TEXT,          -- added in migration 019
    eco_number    TEXT,
    state         TEXT NOT NULL DEFAULT 'draft',
    updated_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_by    TEXT
);

-- approval_signatures table
CREATE TABLE approval_signatures (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    approval_id UUID NOT NULL REFERENCES item_approvals(id) ON DELETE CASCADE,
    username    TEXT NOT NULL,
    role        TEXT NOT NULL,
    status      TEXT NOT NULL DEFAULT 'pending',
    signed_at   TIMESTAMPTZ,
    comment     TEXT
);

State Machine

Approval states: draft | pending | approved | rejected

Signature states: pending | approved | rejected

Auto-transitions (evaluated after each signature):

  1. If any signature is rejected and workflow defines any_reject rule -> approval becomes rejected
  2. If all signatures on required gates are approved and workflow defines all_required_approve rule -> approval becomes approved
  3. Otherwise -> stays pending

Workflow Definitions

YAML files in workflows/ directory, loaded at server startup.

workflows/engineering-change.yaml:

workflow:
  name: engineering-change
  version: 1
  description: "Standard engineering change order with peer review and manager approval"
  states: [draft, pending, approved, rejected]
  gates:
    - role: engineer
      label: "Peer Review"
      required: true
    - role: manager
      label: "Manager Approval"
      required: true
    - role: quality
      label: "Quality Sign-off"
      required: false
  rules:
    any_reject: rejected
    all_required_approve: approved

workflows/quick-review.yaml:

workflow:
  name: quick-review
  version: 1
  description: "Single reviewer approval for minor changes"
  states: [draft, pending, approved, rejected]
  gates:
    - role: reviewer
      label: "Review"
      required: true
  rules:
    any_reject: rejected
    all_required_approve: approved

Custom workflows: add a .yaml file to the workflows directory with the same structure.

API Endpoints

Not module-gated (always available when server is running).

Method Path Auth Description
GET /api/workflows viewer List all loaded workflow definitions
GET /api/items/{pn}/approvals viewer List approvals for item (with signatures)
POST /api/items/{pn}/approvals editor Create approval with signers
POST /api/items/{pn}/approvals/{id}/sign editor Record signature (approve/reject)

Create approval request:

{
  "workflow": "engineering-change",
  "eco_number": "ECO-2026-042",
  "signers": [
    { "username": "alice", "role": "engineer" },
    { "username": "bob",   "role": "manager" },
    { "username": "carol", "role": "quality" }
  ]
}

Validation: workflow must exist, all signer roles must be defined in the workflow, all required gates must have at least one signer.

Sign request:

{
  "status": "approved",
  "comment": "Looks good"
}

Validation: approval must be in pending state, caller must be a listed signer, caller must not have already signed.

SSE Events

Published on the existing GET /api/events stream.

Event Payload Trigger
approval.created {part_number, approval_id, workflow, eco_number} Approval created
approval.signed {part_number, approval_id, username, status} Signature recorded
approval.completed {part_number, approval_id, state} All required gates resolved

Backend Files

File Contents
internal/db/item_approvals.go ItemApprovalRepository — Create, AddSignature, GetWithSignatures, ListByItemWithSignatures, UpdateState, GetSignatureForUser, UpdateSignature
internal/api/approval_handlers.go HandleGetApprovals, HandleCreateApproval, HandleSignApproval, HandleListWorkflows, evaluateApprovalState
internal/workflow/workflow.go Workflow/Gate/Rules types, Load, LoadAll, Validate, RequiredGates, HasRole
workflows/engineering-change.yaml 3-gate ECO workflow
workflows/quick-review.yaml 1-gate quick review workflow

Config

workflows:
  directory: /etc/silo/workflows   # path to workflow YAML files

Frontend

No approval UI exists. The backend API is fully functional but has no web interface. Issue #147 includes an approvals page scaffold as part of the .kc metadata web UI phase.

Not Yet Implemented

  • Web UI for creating, viewing, and signing approvals
  • Approval history / audit log view
  • Lifecycle state transitions tied to approval outcomes (e.g. auto-release on approval)
  • Email or notification integration for pending signatures
  • Delegation / proxy signing
  • Approval templates (pre-filled signer lists per workflow)
  • Bulk approval operations