feat: public example of life-project gates system
Public demo of Factory 4 Life project management: - Gate definitions S0 (spec), S1 (build), S2 (integration), S3 (production ready) - Hardware project template with PlatformIO, KiCad CI, and Makefile for fab packages - JSON schema for .kill-life.yaml manifest - CLI scripts: create-project, validate-gates, dashboard with color-coded status output - Example project: led-controller (S0+S1 passed, S2 in progress)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
.DS_Store
|
||||
*.swp
|
||||
*.swo
|
||||
tmp/
|
||||
.omc/
|
||||
@@ -0,0 +1,67 @@
|
||||
# life-project-example
|
||||
|
||||
Example repo for Factory 4 Life project management: gates S0-S3, templates, cross-repo orchestration.
|
||||
|
||||
> This is a **public demo** of the project management system. See the gate definitions, templates, and scripts below.
|
||||
|
||||
## Concept
|
||||
|
||||
Chaque projet hardware/firmware/software suit un pipeline de **gates** (S0 -> S3) inspiré du stage-gate process. Ce repo centralise :
|
||||
|
||||
- **Gates** : definitions, criteres, checklists (`gates/`)
|
||||
- **Schemas** : validation YAML des manifests `.kill-life.yaml` (`schemas/`)
|
||||
- **Templates** : squelettes de repos prets a l'emploi (`templates/`)
|
||||
- **Scripts** : outils CLI pour creer, valider et suivre les projets (`scripts/`)
|
||||
- **Projects** : registre des projets actifs (`projects/`)
|
||||
|
||||
## Pipeline de gates
|
||||
|
||||
| Gate | Nom | Objectif |
|
||||
|------|-----|----------|
|
||||
| **S0** | Spec Ready | Specs testables, architecture definie, plan de validation |
|
||||
| **S1** | Build OK | Firmware compile, ERC/DRC green, tests unitaires passent |
|
||||
| **S2** | Integration | Tests integration, BOM valide, prototype fonctionnel |
|
||||
| **S3** | Production Ready | Fab package complet, compliance, documentation livree |
|
||||
|
||||
## Structure d'un projet
|
||||
|
||||
Chaque repo projet contient un `.kill-life.yaml` a la racine :
|
||||
|
||||
```yaml
|
||||
kill_life:
|
||||
version: 1
|
||||
project: "mon-projet"
|
||||
client: "client-name"
|
||||
gates:
|
||||
s0: { status: passed, date: "2026-03-15" }
|
||||
s1: { status: in_progress }
|
||||
s2: { status: pending }
|
||||
s3: { status: pending }
|
||||
hardware:
|
||||
pcb_dir: hardware/pcb
|
||||
bom_dir: hardware/bom
|
||||
firmware:
|
||||
framework: platformio
|
||||
src_dir: firmware/src
|
||||
```
|
||||
|
||||
## Utilisation
|
||||
|
||||
```bash
|
||||
# Creer un nouveau projet depuis le template
|
||||
./scripts/create-project.sh mon-projet electron-rare
|
||||
|
||||
# Valider les gates d'un projet
|
||||
./scripts/validate-gates.sh electron-rare/mon-projet
|
||||
|
||||
# Dashboard de tous les projets
|
||||
./scripts/dashboard.sh
|
||||
```
|
||||
|
||||
## Repos exemple
|
||||
|
||||
- [electron-rare/hypnoled](https://github.com/electron-rare/hypnoled) — HypnoLED animations LED ESP32
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@@ -0,0 +1,43 @@
|
||||
# Gate S0 — Spec Ready
|
||||
|
||||
## Objectif
|
||||
|
||||
Valider que les specifications sont completes, testables, et que l'architecture est definie.
|
||||
|
||||
## Criteres obligatoires
|
||||
|
||||
- [ ] `docs/specs/01_spec.md` existe avec des criteres d'acceptation testables
|
||||
- [ ] `docs/specs/02_arch.md` definit les interfaces et contrats
|
||||
- [ ] `docs/specs/03_plan.md` liste les commandes de validation et l'evidence attendue
|
||||
- [ ] `constraints.yaml` present et valide (si applicable)
|
||||
- [ ] `.kill-life.yaml` present a la racine avec `version: 1`
|
||||
|
||||
## Criteres optionnels
|
||||
|
||||
- [ ] Diagramme d'architecture (Excalidraw, Draw.io, ou Mermaid)
|
||||
- [ ] Estimation de BOM preliminaire
|
||||
- [ ] Identification des risques techniques
|
||||
|
||||
## Artifacts produits
|
||||
|
||||
| Artifact | Chemin | Format |
|
||||
|----------|--------|--------|
|
||||
| Spec | `docs/specs/01_spec.md` | Markdown |
|
||||
| Architecture | `docs/specs/02_arch.md` | Markdown |
|
||||
| Plan de validation | `docs/specs/03_plan.md` | Markdown |
|
||||
| Contraintes | `constraints.yaml` | YAML |
|
||||
|
||||
## Validation automatique
|
||||
|
||||
```bash
|
||||
# Verifie la presence des fichiers requis
|
||||
./scripts/validate-gates.sh <org>/<repo> --gate s0
|
||||
```
|
||||
|
||||
## Passage de gate
|
||||
|
||||
Le passage de S0 est formalise par :
|
||||
1. Une PR avec label `gate:s0`
|
||||
2. Review par au moins 1 personne
|
||||
3. Tous les checks CI passent
|
||||
4. Mise a jour du `.kill-life.yaml` : `s0.status: passed`
|
||||
@@ -0,0 +1,49 @@
|
||||
# Gate S1 — Build OK
|
||||
|
||||
## Objectif
|
||||
|
||||
Valider que le projet compile, que les checks hardware passent, et que les tests unitaires sont en place.
|
||||
|
||||
## Criteres par type
|
||||
|
||||
### Firmware (si `firmware/` existe)
|
||||
|
||||
- [ ] `pio run` compile sans erreur (au moins 1 environment)
|
||||
- [ ] `pio test -e native` passe (ou justification documentee)
|
||||
- [ ] Pas de warnings critiques (-Werror ou equivalent)
|
||||
|
||||
### Hardware (si `hardware/` existe)
|
||||
|
||||
- [ ] ERC (Electrical Rules Check) green — artifact JSON
|
||||
- [ ] DRC (Design Rules Check) green — artifact JSON
|
||||
- [ ] Netlist exportable
|
||||
- [ ] BOM genere (`hardware/bom/`)
|
||||
|
||||
### Software (si pas firmware/hardware)
|
||||
|
||||
- [ ] Build reussi (`npm run build` / `cargo build` / etc.)
|
||||
- [ ] Tests unitaires passent
|
||||
- [ ] Linter/formatter OK
|
||||
|
||||
## Criteres communs
|
||||
|
||||
- [ ] CI GitHub Actions configure et green
|
||||
- [ ] README.md a jour avec instructions de build
|
||||
- [ ] Gate S0 passee
|
||||
|
||||
## Validation automatique
|
||||
|
||||
```bash
|
||||
./scripts/validate-gates.sh <org>/<repo> --gate s1
|
||||
```
|
||||
|
||||
Le workflow `.github/workflows/kill-life-ci.yml` execute automatiquement :
|
||||
- ERC/DRC via `kicad-cli` (container `ghcr.io/inti-cmnb/kicad8_auto`)
|
||||
- Build firmware via PlatformIO
|
||||
- Validation BOM
|
||||
|
||||
## Passage de gate
|
||||
|
||||
1. CI green sur `main`
|
||||
2. PR avec label `gate:s1`
|
||||
3. Mise a jour `.kill-life.yaml` : `s1.status: passed`
|
||||
@@ -0,0 +1,45 @@
|
||||
# Gate S2 — Integration
|
||||
|
||||
## Objectif
|
||||
|
||||
Valider que les composants (firmware + hardware + software) fonctionnent ensemble sur un prototype.
|
||||
|
||||
## Criteres obligatoires
|
||||
|
||||
- [ ] Gate S1 passee
|
||||
- [ ] Prototype physique assemble et fonctionnel
|
||||
- [ ] Tests d'integration documentes et passes
|
||||
- [ ] Communication inter-composants validee (I2C, SPI, UART, WiFi...)
|
||||
- [ ] Consommation electrique mesuree et dans les specs
|
||||
|
||||
### Hardware
|
||||
|
||||
- [ ] PCB fabrique et assemble (au moins 1 exemplaire)
|
||||
- [ ] Tous les composants soudes et testes
|
||||
- [ ] Photos/videos du prototype dans `docs/prototype/`
|
||||
|
||||
### Firmware
|
||||
|
||||
- [ ] Flash reussi sur le hardware reel
|
||||
- [ ] Tests fonctionnels sur cible (pas seulement native)
|
||||
- [ ] OTA fonctionnel (si applicable)
|
||||
|
||||
### Software
|
||||
|
||||
- [ ] Integration API/backend testee end-to-end
|
||||
- [ ] Deployment staging fonctionnel
|
||||
|
||||
## Artifacts produits
|
||||
|
||||
| Artifact | Chemin | Format |
|
||||
|----------|--------|--------|
|
||||
| Rapport d'integration | `docs/integration_report.md` | Markdown |
|
||||
| Photos prototype | `docs/prototype/` | Images |
|
||||
| Mesures electriques | `docs/measurements/` | CSV/Markdown |
|
||||
| Test results | `tests/integration/` | Code + logs |
|
||||
|
||||
## Passage de gate
|
||||
|
||||
1. PR avec label `gate:s2`
|
||||
2. Review incluant validation physique (photos/videos)
|
||||
3. Mise a jour `.kill-life.yaml` : `s2.status: passed`
|
||||
@@ -0,0 +1,54 @@
|
||||
# Gate S3 — Production Ready
|
||||
|
||||
## Objectif
|
||||
|
||||
Valider que le projet est pret pour la fabrication en serie / le deploiement en production.
|
||||
|
||||
## Criteres obligatoires
|
||||
|
||||
- [ ] Gate S2 passee
|
||||
- [ ] Fab package complet et valide
|
||||
|
||||
### Fab Package (hardware)
|
||||
|
||||
- [ ] Gerbers exportes et verifies
|
||||
- [ ] Drill files generes
|
||||
- [ ] BOM finale avec references fournisseurs et prix
|
||||
- [ ] Pick & place file (si SMD)
|
||||
- [ ] `acceptance_gates` dans `.kill-life.yaml` : `erc_ok`, `drc_ok`, `bom_review_ok`, `artifacts_complete`
|
||||
|
||||
### Firmware release
|
||||
|
||||
- [ ] Version taguee (semver)
|
||||
- [ ] Binary release sur GitHub Releases
|
||||
- [ ] Changelog a jour
|
||||
- [ ] OTA path teste depuis version N-1 (si applicable)
|
||||
|
||||
### Documentation
|
||||
|
||||
- [ ] Guide utilisateur (`docs/user_guide.md`)
|
||||
- [ ] Procedure de fabrication (`docs/manufacturing.md`)
|
||||
- [ ] Nomenclature complete et a jour
|
||||
- [ ] Licences verifiees (SPDX)
|
||||
|
||||
### Compliance
|
||||
|
||||
- [ ] Conformite CE / FCC documentee (si applicable)
|
||||
- [ ] Tests EMC references (si applicable)
|
||||
- [ ] WEEE / RoHS compliance (si applicable)
|
||||
|
||||
## Artifacts produits
|
||||
|
||||
| Artifact | Chemin | Format |
|
||||
|----------|--------|--------|
|
||||
| Fab package | `releases/<version>/` | Gerbers, drill, BOM, netlist |
|
||||
| Firmware binary | GitHub Release | `.bin` / `.uf2` |
|
||||
| User guide | `docs/user_guide.md` | Markdown |
|
||||
| Manufacturing guide | `docs/manufacturing.md` | Markdown |
|
||||
|
||||
## Passage de gate
|
||||
|
||||
1. PR avec label `gate:s3`
|
||||
2. Review par au moins 2 personnes
|
||||
3. Tag de release cree
|
||||
4. Mise a jour `.kill-life.yaml` : `s3.status: passed`
|
||||
@@ -0,0 +1,16 @@
|
||||
kill_life:
|
||||
version: 1
|
||||
project: "example-led-controller"
|
||||
repo: "electron-rare/life-project-example"
|
||||
client: "demo"
|
||||
gates:
|
||||
s0: { status: passed, date: "2026-03-01" }
|
||||
s1: { status: passed, date: "2026-03-15" }
|
||||
s2: { status: in_progress }
|
||||
s3: { status: pending }
|
||||
hardware:
|
||||
pcb_dir: hardware/pcb
|
||||
bom_dir: hardware/bom
|
||||
firmware:
|
||||
framework: platformio
|
||||
src_dir: firmware/src
|
||||
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "kill-life.yaml",
|
||||
"description": "Manifest de projet Factory 4 Life",
|
||||
"type": "object",
|
||||
"required": ["kill_life"],
|
||||
"properties": {
|
||||
"kill_life": {
|
||||
"type": "object",
|
||||
"required": ["version", "project"],
|
||||
"properties": {
|
||||
"version": { "type": "integer", "const": 1 },
|
||||
"project": { "type": "string", "minLength": 1 },
|
||||
"client": { "type": "string" },
|
||||
"mascarade_url": { "type": "string", "format": "uri" },
|
||||
"agents": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"mcp_servers": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"gates": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"s0": { "$ref": "#/$defs/gate_status" },
|
||||
"s1": { "$ref": "#/$defs/gate_status" },
|
||||
"s2": { "$ref": "#/$defs/gate_status" },
|
||||
"s3": { "$ref": "#/$defs/gate_status" }
|
||||
}
|
||||
},
|
||||
"acceptance_gates": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"erc_ok": { "type": "boolean" },
|
||||
"drc_ok": { "type": "boolean" },
|
||||
"bom_review_ok": { "type": "boolean" },
|
||||
"artifacts_complete": { "type": "boolean" }
|
||||
}
|
||||
},
|
||||
"hardware": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pcb_dir": { "type": "string" },
|
||||
"simulation_dir": { "type": "string" },
|
||||
"bom_dir": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"firmware": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"framework": { "type": "string", "enum": ["platformio", "esp-idf", "arduino", "zephyr"] },
|
||||
"src_dir": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"software": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"framework": { "type": "string" },
|
||||
"src_dir": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"gate_status": {
|
||||
"type": "object",
|
||||
"required": ["status"],
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["pending", "in_progress", "passed", "blocked", "skipped"]
|
||||
},
|
||||
"date": { "type": "string", "format": "date" },
|
||||
"notes": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+95
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Create a new Factory 4 Life project from template
|
||||
# Usage: ./scripts/create-project.sh <project-name> <github-org> [--client <name>]
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
TEMPLATE_DIR="${REPO_ROOT}/templates/hardware-project"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: create-project.sh <project-name> <github-org> [options]
|
||||
|
||||
Options:
|
||||
--client <name> Client name for the project
|
||||
--public Create as public repo (default: private)
|
||||
--no-clone Don't clone locally after creation
|
||||
-h, --help Show this help
|
||||
|
||||
Example:
|
||||
./scripts/create-project.sh my-led-project electron-rare --client acme
|
||||
EOF
|
||||
}
|
||||
|
||||
PROJECT=""
|
||||
ORG=""
|
||||
CLIENT=""
|
||||
VISIBILITY="--private"
|
||||
CLONE=true
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--client) CLIENT="$2"; shift 2 ;;
|
||||
--public) VISIBILITY="--public"; shift ;;
|
||||
--no-clone) CLONE=false; shift ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*)
|
||||
if [[ -z "$PROJECT" ]]; then PROJECT="$1"
|
||||
elif [[ -z "$ORG" ]]; then ORG="$1"
|
||||
else usage >&2; exit 2
|
||||
fi
|
||||
shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -n "$PROJECT" ]] || { echo "Error: project name required" >&2; usage >&2; exit 1; }
|
||||
[[ -n "$ORG" ]] || { echo "Error: github org required" >&2; usage >&2; exit 1; }
|
||||
|
||||
echo "Creating project: ${ORG}/${PROJECT}"
|
||||
|
||||
# Create GitHub repo
|
||||
gh repo create "${ORG}/${PROJECT}" ${VISIBILITY} \
|
||||
--description "Factory 4 Life project — ${PROJECT}" \
|
||||
--clone
|
||||
|
||||
cd "${PROJECT}"
|
||||
|
||||
# Copy template
|
||||
cp -r "${TEMPLATE_DIR}/"* .
|
||||
cp -r "${TEMPLATE_DIR}/".[!.]* . 2>/dev/null || true
|
||||
|
||||
# Generate .kill-life.yaml
|
||||
cat > .kill-life.yaml <<YAML
|
||||
kill_life:
|
||||
version: 1
|
||||
project: "${PROJECT}"
|
||||
client: "${CLIENT:-tbd}"
|
||||
gates:
|
||||
s0: { status: in_progress }
|
||||
s1: { status: pending }
|
||||
s2: { status: pending }
|
||||
s3: { status: pending }
|
||||
agents:
|
||||
- forge
|
||||
- firmware-agent
|
||||
- qa-agent
|
||||
mcp_servers:
|
||||
- kicad
|
||||
hardware:
|
||||
pcb_dir: hardware/pcb
|
||||
bom_dir: hardware/bom
|
||||
firmware:
|
||||
framework: platformio
|
||||
src_dir: firmware/src
|
||||
YAML
|
||||
|
||||
# Initial commit
|
||||
git add -A
|
||||
git commit -m "feat: init ${PROJECT} from life-project template"
|
||||
git push -u origin main
|
||||
|
||||
echo ""
|
||||
echo "Project created: https://github.com/${ORG}/${PROJECT}"
|
||||
echo "Next: edit .kill-life.yaml and start working on S0 specs"
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Display dashboard of all Factory 4 Life projects
|
||||
# Reads project registry from projects/ directory
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
PROJECTS_DIR="${REPO_ROOT}/projects"
|
||||
|
||||
echo "============================================"
|
||||
echo " Factory 4 Life — Project Dashboard"
|
||||
echo " $(date '+%Y-%m-%d %H:%M')"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
|
||||
printf "%-25s %-10s %-10s %-10s %-10s %-12s\n" "PROJECT" "S0" "S1" "S2" "S3" "CLIENT"
|
||||
printf "%-25s %-10s %-10s %-10s %-10s %-12s\n" "-------" "--" "--" "--" "--" "------"
|
||||
|
||||
for project_file in "${PROJECTS_DIR}"/*.yaml; do
|
||||
[[ -f "$project_file" ]] || continue
|
||||
|
||||
name=$(grep 'project:' "$project_file" | head -1 | awk '{print $2}' | tr -d '"')
|
||||
client=$(grep 'client:' "$project_file" | head -1 | awk '{print $2}' | tr -d '"')
|
||||
# Parse inline YAML: s0: { status: passed, date: "..." }
|
||||
extract_status() { grep " $1:" "$2" | sed 's/.*status: *//;s/[,} ].*//' | tr -d '"'; }
|
||||
s0=$(extract_status s0 "$project_file")
|
||||
s1=$(extract_status s1 "$project_file")
|
||||
s2=$(extract_status s2 "$project_file")
|
||||
s3=$(extract_status s3 "$project_file")
|
||||
|
||||
# Color-code status
|
||||
format_status() {
|
||||
case "$1" in
|
||||
passed) printf "\033[32m%-10s\033[0m" "PASSED" ;;
|
||||
in_progress) printf "\033[33m%-10s\033[0m" "WIP" ;;
|
||||
blocked) printf "\033[31m%-10s\033[0m" "BLOCKED" ;;
|
||||
skipped) printf "\033[90m%-10s\033[0m" "SKIP" ;;
|
||||
*) printf "%-10s" "PENDING" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
printf "%-25s " "${name:-unknown}"
|
||||
format_status "$s0"
|
||||
format_status "$s1"
|
||||
format_status "$s2"
|
||||
format_status "$s3"
|
||||
printf "%-12s\n" "${client:-—}"
|
||||
done
|
||||
|
||||
echo ""
|
||||
Executable
+147
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Validate gate criteria for a Factory 4 Life project
|
||||
# Usage: ./scripts/validate-gates.sh <org/repo> [--gate s0|s1|s2|s3] [--local <path>]
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: validate-gates.sh <org/repo> [options]
|
||||
|
||||
Options:
|
||||
--gate <s0|s1|s2|s3> Validate specific gate (default: all)
|
||||
--local <path> Use local directory instead of cloning
|
||||
--json Output as JSON
|
||||
-h, --help Show this help
|
||||
|
||||
Example:
|
||||
./scripts/validate-gates.sh electron-rare/hypnoled --gate s1
|
||||
./scripts/validate-gates.sh electron-rare/hypnoled --local ~/hypnoled
|
||||
EOF
|
||||
}
|
||||
|
||||
REPO=""
|
||||
GATE=""
|
||||
LOCAL_PATH=""
|
||||
JSON_OUTPUT=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--gate) GATE="$2"; shift 2 ;;
|
||||
--local) LOCAL_PATH="$2"; shift 2 ;;
|
||||
--json) JSON_OUTPUT=true; shift ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) REPO="$1"; shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -n "$REPO" ]] || { echo "Error: repo required" >&2; usage >&2; exit 1; }
|
||||
|
||||
# Get project directory
|
||||
if [[ -n "$LOCAL_PATH" ]]; then
|
||||
PROJECT_DIR="$LOCAL_PATH"
|
||||
else
|
||||
TMPDIR=$(mktemp -d)
|
||||
trap "rm -rf $TMPDIR" EXIT
|
||||
gh repo clone "$REPO" "$TMPDIR/repo" -- --depth 1 2>/dev/null
|
||||
PROJECT_DIR="$TMPDIR/repo"
|
||||
fi
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
WARN=0
|
||||
|
||||
check() {
|
||||
local gate="$1" desc="$2" result="$3"
|
||||
if [[ "$result" == "pass" ]]; then
|
||||
echo " [PASS] $desc"
|
||||
((PASS++))
|
||||
elif [[ "$result" == "warn" ]]; then
|
||||
echo " [WARN] $desc"
|
||||
((WARN++))
|
||||
else
|
||||
echo " [FAIL] $desc"
|
||||
((FAIL++))
|
||||
fi
|
||||
}
|
||||
|
||||
validate_s0() {
|
||||
echo "=== Gate S0: Spec Ready ==="
|
||||
check s0 ".kill-life.yaml exists" \
|
||||
"$([[ -f "$PROJECT_DIR/.kill-life.yaml" ]] && echo pass || echo fail)"
|
||||
check s0 "docs/specs/01_spec.md exists" \
|
||||
"$([[ -f "$PROJECT_DIR/docs/specs/01_spec.md" ]] && echo pass || echo fail)"
|
||||
check s0 "docs/specs/02_arch.md exists" \
|
||||
"$([[ -f "$PROJECT_DIR/docs/specs/02_arch.md" ]] && echo pass || echo fail)"
|
||||
check s0 "docs/specs/03_plan.md exists" \
|
||||
"$([[ -f "$PROJECT_DIR/docs/specs/03_plan.md" ]] && echo pass || echo fail)"
|
||||
check s0 "README.md exists" \
|
||||
"$([[ -f "$PROJECT_DIR/README.md" ]] && echo pass || echo fail)"
|
||||
}
|
||||
|
||||
validate_s1() {
|
||||
echo "=== Gate S1: Build OK ==="
|
||||
|
||||
# Firmware checks
|
||||
if [[ -f "$PROJECT_DIR/firmware/platformio.ini" ]]; then
|
||||
check s1 "platformio.ini exists" pass
|
||||
check s1 "firmware/src/ has source files" \
|
||||
"$(find "$PROJECT_DIR/firmware/src" -name '*.cpp' -o -name '*.c' -o -name '*.h' 2>/dev/null | head -1 | grep -q . && echo pass || echo fail)"
|
||||
fi
|
||||
|
||||
# Hardware checks
|
||||
if [[ -d "$PROJECT_DIR/hardware/pcb" ]]; then
|
||||
check s1 "KiCad schematic exists" \
|
||||
"$(find "$PROJECT_DIR/hardware/pcb" -name '*.kicad_sch' 2>/dev/null | head -1 | grep -q . && echo pass || echo fail)"
|
||||
check s1 "KiCad PCB exists" \
|
||||
"$(find "$PROJECT_DIR/hardware/pcb" -name '*.kicad_pcb' 2>/dev/null | head -1 | grep -q . && echo pass || echo fail)"
|
||||
check s1 "BOM exists" \
|
||||
"$([[ -d "$PROJECT_DIR/hardware/bom" ]] && find "$PROJECT_DIR/hardware/bom" -name '*.csv' 2>/dev/null | head -1 | grep -q . && echo pass || echo warn)"
|
||||
fi
|
||||
|
||||
# CI check
|
||||
check s1 "GitHub Actions CI configured" \
|
||||
"$(find "$PROJECT_DIR/.github/workflows" -name '*.yml' -o -name '*.yaml' 2>/dev/null | head -1 | grep -q . && echo pass || echo fail)"
|
||||
}
|
||||
|
||||
validate_s2() {
|
||||
echo "=== Gate S2: Integration ==="
|
||||
check s2 "Integration report exists" \
|
||||
"$([[ -f "$PROJECT_DIR/docs/integration_report.md" ]] && echo pass || echo fail)"
|
||||
check s2 "Prototype documentation exists" \
|
||||
"$([[ -d "$PROJECT_DIR/docs/prototype" ]] && echo pass || echo warn)"
|
||||
check s2 "Integration tests exist" \
|
||||
"$(find "$PROJECT_DIR/tests/integration" -type f 2>/dev/null | head -1 | grep -q . && echo pass || echo warn)"
|
||||
}
|
||||
|
||||
validate_s3() {
|
||||
echo "=== Gate S3: Production Ready ==="
|
||||
check s3 "User guide exists" \
|
||||
"$([[ -f "$PROJECT_DIR/docs/user_guide.md" ]] && echo pass || echo fail)"
|
||||
check s3 "Manufacturing guide exists" \
|
||||
"$([[ -f "$PROJECT_DIR/docs/manufacturing.md" ]] && echo pass || echo warn)"
|
||||
check s3 "Release directory exists" \
|
||||
"$([[ -d "$PROJECT_DIR/releases" ]] && echo pass || echo fail)"
|
||||
|
||||
if [[ -d "$PROJECT_DIR/hardware/pcb" ]]; then
|
||||
check s3 "Gerbers generated" \
|
||||
"$(find "$PROJECT_DIR/releases" -name '*.gbr' -o -name '*.gtl' 2>/dev/null | head -1 | grep -q . && echo pass || echo fail)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Run validations
|
||||
echo "Validating: $REPO"
|
||||
echo ""
|
||||
|
||||
case "$GATE" in
|
||||
s0) validate_s0 ;;
|
||||
s1) validate_s0; validate_s1 ;;
|
||||
s2) validate_s0; validate_s1; validate_s2 ;;
|
||||
s3) validate_s0; validate_s1; validate_s2; validate_s3 ;;
|
||||
"") validate_s0; validate_s1; validate_s2; validate_s3 ;;
|
||||
*) echo "Unknown gate: $GATE" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo "Results: ${PASS} passed, ${FAIL} failed, ${WARN} warnings"
|
||||
[[ "$FAIL" -eq 0 ]] && exit 0 || exit 1
|
||||
@@ -0,0 +1,58 @@
|
||||
name: Kill_LIFE CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
erc-drc:
|
||||
name: KiCad ERC/DRC
|
||||
runs-on: ubuntu-latest
|
||||
if: hashFiles('hardware/pcb/*.kicad_sch') != ''
|
||||
container:
|
||||
image: ghcr.io/inti-cmnb/kicad8_auto:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Run ERC
|
||||
run: |
|
||||
for sch in $(find hardware/pcb -name '*.kicad_sch' -not -path '*/libs/*'); do
|
||||
echo "--- ERC: $sch ---"
|
||||
kicad-cli sch erc --exit-code-violations "$sch" || exit 1
|
||||
done
|
||||
|
||||
- name: Run DRC
|
||||
run: |
|
||||
for pcb in $(find hardware/pcb -name '*.kicad_pcb'); do
|
||||
echo "--- DRC: $pcb ---"
|
||||
kicad-cli pcb drc --exit-code-violations "$pcb" || exit 1
|
||||
done
|
||||
|
||||
bom-check:
|
||||
name: BOM Validation
|
||||
runs-on: ubuntu-latest
|
||||
if: hashFiles('hardware/bom/*.csv') != ''
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Validate BOM
|
||||
run: |
|
||||
if [ -f hardware/bom/bom.csv ]; then
|
||||
echo "BOM found: $(wc -l < hardware/bom/bom.csv) lines"
|
||||
else
|
||||
echo "WARNING: No BOM at hardware/bom/bom.csv"
|
||||
fi
|
||||
|
||||
firmware-build:
|
||||
name: Firmware Build
|
||||
runs-on: ubuntu-latest
|
||||
if: hashFiles('firmware/platformio.ini') != ''
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- run: pip install platformio
|
||||
- name: Build firmware
|
||||
run: cd firmware && pio run
|
||||
@@ -0,0 +1,21 @@
|
||||
kill_life:
|
||||
version: 1
|
||||
project: "TEMPLATE"
|
||||
client: "tbd"
|
||||
gates:
|
||||
s0: { status: in_progress }
|
||||
s1: { status: pending }
|
||||
s2: { status: pending }
|
||||
s3: { status: pending }
|
||||
agents:
|
||||
- forge
|
||||
- firmware-agent
|
||||
- qa-agent
|
||||
mcp_servers:
|
||||
- kicad
|
||||
hardware:
|
||||
pcb_dir: hardware/pcb
|
||||
bom_dir: hardware/bom
|
||||
firmware:
|
||||
framework: platformio
|
||||
src_dir: firmware/src
|
||||
@@ -0,0 +1,32 @@
|
||||
PROJECT_DIR := hardware/pcb
|
||||
TS ?= $(shell date +%Y%m%d-%H%M%S)
|
||||
REL := releases/$(TS)
|
||||
|
||||
.PHONY: kicad-check kicad-fab firmware-build
|
||||
|
||||
kicad-check:
|
||||
@command -v kicad-cli >/dev/null || (echo "kicad-cli manquant" && exit 1)
|
||||
@for sch in $$(find $(PROJECT_DIR) -name '*.kicad_sch' -not -path '*/libs/*'); do \
|
||||
echo "--- ERC: $$sch ---"; \
|
||||
kicad-cli sch erc "$$sch"; \
|
||||
done
|
||||
@for pcb in $$(find $(PROJECT_DIR) -name '*.kicad_pcb'); do \
|
||||
echo "--- DRC: $$pcb ---"; \
|
||||
kicad-cli pcb drc "$$pcb"; \
|
||||
done
|
||||
|
||||
kicad-fab:
|
||||
@command -v kicad-cli >/dev/null || (echo "kicad-cli manquant" && exit 1)
|
||||
mkdir -p "$(REL)/gerbers" "$(REL)/drill" "$(REL)/bom" "$(REL)/netlist"
|
||||
@for pcb in $$(find $(PROJECT_DIR) -name '*.kicad_pcb'); do \
|
||||
kicad-cli pcb export gerbers "$$pcb" --output "$(REL)/gerbers"; \
|
||||
kicad-cli pcb export drill "$$pcb" --output "$(REL)/drill"; \
|
||||
done
|
||||
@for sch in $$(find $(PROJECT_DIR) -name '*.kicad_sch' -maxdepth 1); do \
|
||||
kicad-cli sch export bom "$$sch" --output "$(REL)/bom/bom.csv"; \
|
||||
kicad-cli sch export netlist "$$sch" --output "$(REL)/netlist/project.net"; \
|
||||
done
|
||||
@echo "Artefacts dans $(REL)"
|
||||
|
||||
firmware-build:
|
||||
cd firmware && pio run
|
||||
@@ -0,0 +1,38 @@
|
||||
# PROJECT_NAME
|
||||
|
||||
> Factory 4 Life project — description here
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
.kill-life.yaml # Project manifest & gate status
|
||||
firmware/ # ESP32 firmware (PlatformIO)
|
||||
hardware/
|
||||
pcb/ # KiCad schematics & PCB
|
||||
bom/ # Bill of Materials
|
||||
docs/
|
||||
specs/ # Specifications (S0 gate)
|
||||
releases/ # Fab packages & binaries
|
||||
```
|
||||
|
||||
## Gates
|
||||
|
||||
| Gate | Status |
|
||||
|------|--------|
|
||||
| S0 — Spec Ready | in_progress |
|
||||
| S1 — Build OK | pending |
|
||||
| S2 — Integration | pending |
|
||||
| S3 — Production Ready | pending |
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
# Firmware
|
||||
cd firmware && pio run
|
||||
|
||||
# Hardware checks
|
||||
make kicad-check
|
||||
|
||||
# Fab package
|
||||
make kicad-fab
|
||||
```
|
||||
@@ -0,0 +1,8 @@
|
||||
[env:esp32]
|
||||
platform = espressif32
|
||||
board = esp32dev
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
|
||||
[env:native]
|
||||
platform = native
|
||||
@@ -0,0 +1,10 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Factory 4 Life — Project init");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// TODO: implement
|
||||
}
|
||||
Reference in New Issue
Block a user