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