diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 12dfff9..8d923a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,9 +1,11 @@ name: ci +# Host always for the spine package. Metal plane is GCU-side (plates); see +# silico/knowledge/ci-host-metal.md (tig/silico#101). on: push: - branches: [main, master, develop] pull_request: + workflow_dispatch: jobs: host-gate: diff --git a/AGENTS.md b/AGENTS.md index 6e66d3b..3e952b0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -345,9 +345,9 @@ Before the first push of first ship (or when locking the product workspace), **i | Issues | `gh issue list --limit 5`; issue count / activity | | Default branch protection / required checks | only if already visible; do not dig for policy theater | -##### Maintainer practice GCUs: always `main` (hard override) +##### Maintainer practice GCUs (xuss family) -These remotes are **first-ship / harness practice trees**, not product teams with a merge queue. **Always use the direct-to-`main` strategy** — even if `gh pr list` shows past PR history (prior harness runs leave noise). +These remotes are **first-ship / harness practice trees**, not product teams with a merge queue. | Remote | Role | |--------|------| @@ -355,9 +355,16 @@ These remotes are **first-ship / harness practice trees**, not product teams wit | [tig/xuss-c](https://github.com/tig/xuss-c) | C / ESP-IDF demo GCU | | [tig/xuss-lame](https://github.com/tig/xuss-lame) | Thin/messy interview practice GCU | -**Do not** open a “repo workflow” chooser offering `pr` vs `main` for these three. **Do not** invent a PR path because “this repo has recent PR history.” Land first ship as **individual commits on `main`** (watch CI on push). +**Two surfaces (target — [tig/silico#101](https://github.com/tig/silico/issues/101)):** -**Clean start point (before mutating Stage A–C):** for the practice GCU you are shipping, verify **`origin`’s default branch tip is the product-only clean start**, not a mid-flight scaffold from a previous agent. +| Surface | Role | +|---------|------| +| **`main`** | Prefer **product-only clean start** (public template face). Do not leave everyday harness firmware here. | +| **Lab / `session/*` (or a named eval branch)** | Harness runway: first-ship commits, host CI, optional metal later. Archive with `attempt-*` tags; restore `main` to clean-start after eval (operator go on destructive remote update). | + +**Do not** invent multi-PR product theater because leftover harness PR history exists. Prefer **few reviewable commits on one lab branch** (or direct `main` only when the operator explicitly continues a dirty tree / golden demo). **Do not** push firmware to `main` “so CI runs” — plate **host-gate runs on every branch** (host always). Metal is a separate opt-in self-hosted plane. Detail: [silico/knowledge/ci-host-metal.md](silico/knowledge/ci-host-metal.md). + +**Clean start point (before mutating Stage A–C):** for the practice GCU you are shipping, verify **`origin`’s default branch tip is the product-only clean start** (or you are intentionally on a lab branch that started from it), not a mid-flight scaffold from a previous agent left as if it were cold start. | Check | How | |-------|-----| @@ -371,18 +378,20 @@ If tip is **not** clean start (e.g. prior “Scaffold plate…”, half-done fir 2. Tell the operator in plain language: remote is past clean start; options are **reset to clean start** (only with explicit operator go — destructive) or **continue this tree** as an update path. 3. Do **not** silently open a PR “to keep main clean” as a substitute for checking clean start. -When the operator’s goal is a **fresh first-ship harness run**, all three practice remotes they care about should be at clean start **before** go (at least the one cwd targets; if they named the whole Xuss family, check each). +When the operator’s goal is a **fresh first-ship harness run**, all three practice remotes they care about should be at clean start **before** go (at least the one cwd targets; if they named the whole Xuss family, check each) — or the session must start from clean-start onto a **lab branch**, not invent that dirty `main` is cold start. + +**Anti-pattern:** Stage C green → “repo has PR history → recommend multi-PR product workflow” on `tig/xuss-c` (or xuss / xuss-lame). Harness noise is not a merge queue. -**Anti-pattern:** Stage C green → “repo has PR history → recommend `pr`” on `tig/xuss-c` (or xuss / xuss-lame). That is the failure this override exists to prevent. +**Anti-pattern:** force mid-flight firmware onto practice `main` only so GitHub Actions runs. Host CI is **host always** (all branches); see [ci-host-metal.md](silico/knowledge/ci-host-metal.md). -**Simple repo** (typical early GCU / first-ship practice tree — **and always the three practice GCUs above**): few or no extra branches, little or no PR history, thin issue tracker. +**Simple repo** (typical early GCU / first-ship practice tree — **and the three practice GCUs above**): few or no extra branches, little or no real product PR history, thin issue tracker. -1. **Recommend committing and pushing straight to `main`** (or the default branch). For **xuss / xuss-c / xuss-lame**, this is **required**, not optional. -2. Say in plain language: PRs can wait until the product is more active; direct-to-main keeps CI on every push without merge-queue overhead. -3. Use a **structured chooser** if they might prefer PRs anyway; recommended option first: **commit to main**. **Skip the chooser** for the three practice GCUs (already decided: main). -4. Land first ship as **individual, reviewable commits** on that branch. Issues stage intent; **issues ≠ a PR each**. +1. **Evaluation / clean-start harness:** recommend a **lab branch** from clean-start (e.g. `session/…`) so public `main` stays template-clean; host CI still runs on push. +2. **Operator wants main anyway** (or continues a dirty tree): individual reviewable commits on that branch; say the template face is dirty until restore. +3. Skip multi-option free-text “PR vs main” walls; use a short structured chooser only when the operator might choose lab vs main vs continue-dirty. +4. Issues stage intent; **issues ≠ a PR each**. -**Well-used repo** (active branches, open or recent PRs, non-trivial issue history, or operator already works via PR) — **does not apply** to tig/xuss, tig/xuss-c, tig/xuss-lame (see override above): +**Well-used repo** (active branches, open or recent PRs, non-trivial issue history, or operator already works via PR) — **does not apply** to the three practice GCUs’ *harness* default (use lab/clean-start rules above). For real product GCUs: 1. **Do not silently force direct-to-main.** 2. **Verify with the operator** (structured chooser) whether changes should go through **PRs** or **direct commits to the default branch**. @@ -391,10 +400,12 @@ When the operator’s goal is a **fresh first-ship harness run**, all three prac **Only open multiple PRs when** the operator clearly asks to stage, split review, or stack work (e.g. “separate PRs per issue,” Graphite stack). Then say how they relate and merge order — do not invent a multi-PR plan unprompted. -**Anti-pattern:** invent a PR workflow on a quiet repo that only needed `main` commits. -**Anti-pattern:** invent a PR workflow on **tig/xuss**, **tig/xuss-c**, or **tig/xuss-lame** because of leftover harness PR history. +**Anti-pattern:** invent a PR workflow on a quiet repo that only needed `main` or one lab branch. +**Anti-pattern:** invent multi-PR theater on **tig/xuss**, **tig/xuss-c**, or **tig/xuss-lame** because of leftover harness PR history. **Anti-pattern:** five open PRs titled variations of “first ship scaffold,” “L0 product face,” … with no operator request to split. **Anti-pattern:** choose PRs and never watch CI — that skips the reason PRs exist for that team. +**Anti-pattern:** first-ship mutate on a practice GCU as if cold start when `origin/main` is mid-flight, without telling the operator (or preparing a lab branch from clean-start). +**Anti-pattern:** tip is clean start (docs only) but the agent cherry-picks or replays a previous attempt’s display/audio/UI firmware from older commits / other branches / “last session worked” — that is **past-HEAD salvage** (see **Product truth is HEAD**). Verboten. ##### Push rejected: workflow scope @@ -406,8 +417,6 @@ with a token lacking the `workflow` scope is **refused by GitHub** (clear `git push "https://x-access-token:${KT}@github.com//.git" ` — and scrub the tokenized remote/upstream afterwards. Do not drop the workflow file to make the push pass. -**Anti-pattern:** first-ship mutate on a practice GCU whose `origin/main` tip is not the product-only clean start, without telling the operator. -**Anti-pattern:** tip is clean start (docs only) but the agent cherry-picks or replays a previous attempt’s display/audio/UI firmware from older commits / other branches / “last session worked” — that is **past-HEAD salvage** (see **Product truth is HEAD**). Verboten. ### Product truth is HEAD (no past-HEAD salvage) @@ -696,7 +705,7 @@ When a human must install or approve something, use **Big steps: why + where** ( 2. If cwd is already a git GCU with `origin`, use it. 3. If create new: confirm a product or codename (**not** `silico`); `gh` create private; clone **or** init in the empty cwd. 4. Remind them: silico stays `github.com/tig/silico`; **their product** is the GCU repo. -5. Once the GCU remote is known: **inspect repo workflow** (branches / PRs / issues) and apply **Repo workflow: inspect, then choose main vs PRs** before the first push. If remote is **tig/xuss**, **tig/xuss-c**, or **tig/xuss-lame**: **main only** (no PR chooser); **verify clean start** tip before mutating (see that section). +5. Once the GCU remote is known: **inspect repo workflow** (branches / PRs / issues) and apply **Repo workflow** before the first push. If remote is **tig/xuss**, **tig/xuss-c**, or **tig/xuss-lame**: verify **clean start** (or lab branch from clean-start); do not invent multi-PR theater; host CI runs on every branch (see practice GCUs + [ci-host-metal.md](silico/knowledge/ci-host-metal.md)). ### Stage C - Local silico checkout, pin, and scaffold the GCU @@ -803,7 +812,7 @@ Then: do **not** claim the product is fully specified; implement the current sli **Anti-patterns:** block forever on a perfect spec; invent vertical moat without judgment; free-text choice walls; skip metal poll when metal is in scope; leave recovery only in chat. -**Practice GCU (maintainers / interview dry-run):** private [tig/xuss-lame](https://github.com/tig/xuss-lame) is a thin, messy first-draft product tree (not labeled as a test in-repo). When first ship is aimed at that checkout, product truth is **only** that tree + the operator — do **not** open `tig/xuss` or `tig/xuss-c` (or use prior knowledge of them) to “complete” the contract. Detail: [silico/knowledge/spec-interview.md](silico/knowledge/spec-interview.md). Repo workflow for **xuss / xuss-c / xuss-lame**: always **direct-to-`main`**; confirm **product-only clean start** on `origin/main` before first-ship mutates (see **Repo workflow** → Maintainer practice GCUs). +**Practice GCU (maintainers / interview dry-run):** private [tig/xuss-lame](https://github.com/tig/xuss-lame) is a thin, messy first-draft product tree (not labeled as a test in-repo). When first ship is aimed at that checkout, product truth is **only** that tree + the operator — do **not** open `tig/xuss` or `tig/xuss-c` (or use prior knowledge of them) to “complete” the contract. Detail: [silico/knowledge/spec-interview.md](silico/knowledge/spec-interview.md). Repo workflow for **xuss / xuss-c / xuss-lame**: clean-start (or lab branch from it); prefer not leaving harness residue on public `main` (see **Repo workflow** → Maintainer practice GCUs, #101). ### Stage D - Talk to real hardware (hello metal) @@ -910,12 +919,14 @@ Non-Python deploy assets (e.g. audio riffs) may appear in `[deploy].core`; host ### Stage E - CI proves metal change 1. Ask the human to open a GitHub Issue on the **GCU** repo (or create with `gh` after approve). Title example: `Change the firmware blink pattern (distinct A vs B)`. -2. Implement: firmware behavior change **and** host tests/CI green. Domain follows product `spec.md` when present. -3. Push commits to the **existing** first-ship branch/PR (or open the first PR if none yet). Do **not** open a new PR per stage. Watch CI; fix red builds. -4. Deploy only after operator confirmation again if overwriting; confirm the visible/audible acceptance matches the issue. -5. Close the issue with a short note linking the commit/PR. +2. Implement: firmware behavior change **and** host tests green. Domain follows product `spec.md` when present. +3. Push commits to the **existing** first-ship branch/PR (or open the first PR if none yet). Do **not** open a new PR per stage. Prefer a **lab branch** for practice GCUs so host CI runs without dirtying template `main` ([#101](https://github.com/tig/silico/issues/101), [ci-host-metal.md](silico/knowledge/ci-host-metal.md)). +4. **Host always:** watch the **`host-gate`** job green on that push (all branches). That is the always-on CI plane. Do **not** claim metal from host alone. +5. **Metal later:** when a self-hosted `metal-bench` (or equivalent) exists and is green, claim only the layers it proves (deployed / machine accept). Operator see/hear **product face** still needs Stage D1 / field unless instrumented. Detail: [ci-host-metal.md](silico/knowledge/ci-host-metal.md). +6. Deploy only after operator confirmation again if overwriting; confirm the visible/audible acceptance matches the issue. +7. Close the issue with a short note linking the commit/PR and the **layer** proven. -Closed loop: **issue → agent → host gate → CI → metal**. +Closed loop: **issue → agent → host gate → host CI → (optional metal CI) → metal observe**. ### Stage F - Domain work (still first ship) @@ -1015,7 +1026,8 @@ Default plate stays MicroPython. Arduino is not this path (see issue #59). First - Do **not** notice GPIO / product face mismatch and skip asking the operator to clarify (honesty note or issue alone is not a clarify gate). - Do **not** invent short forms of lexicon terms (e.g. bare “face” for **product face**) in operator-facing prose. - Do **not** present stage forks as free-text `1. / 2. / 3.` menus in chat when a structured chooser exists (or `bedside ask` is available). -- Do **not** invent a PR workflow on a quiet/simple GCU without asking — recommend **main** first (see **Repo workflow**). Do **not** invent a PR path on **tig/xuss**, **tig/xuss-c**, or **tig/xuss-lame** (always main; check clean start first). Do **not** open a fan-out of PRs for sequential first ship / same-session work unless the operator asked to stage multi-PR. +- Do **not** invent multi-PR theater on a quiet/simple GCU without asking. Do **not** force practice GCUs (**tig/xuss**, **xuss-c**, **xuss-lame**) onto dirty `main` just for CI — host-gate is host always; prefer lab branch + clean-start (see **Repo workflow**, #101). Do **not** open a fan-out of PRs for sequential first ship / same-session work unless the operator asked to stage multi-PR. +- Do **not** narrate host-only CI green as metal-accepted or Stage E metal closed (see [ci-host-metal.md](silico/knowledge/ci-host-metal.md)). - Do **not** implement or claim product face / domain by **past-HEAD salvage**: cherry-pick, replay, or copy firmware from older commits, other branches, stashes, or prior attempts without explicit operator go to continue that line (see **Product truth is HEAD**). Applies to every GCU clean start and abort-and-retry, not only practice trees. - Do **not** treat a board still showing a full app while HEAD is docs/plate-only as “code already landed” — re-flash **this** build after implementing in **this** tree. - Do **not** deploy, soft-reset, or otherwise drive metal that may play loud/long audio, flash bright patterns, or move actuators without a clear operator-facing forewarning of what will happen and for how long. diff --git a/silico/knowledge/INDEX.md b/silico/knowledge/INDEX.md index 54da086..55ae1e7 100644 --- a/silico/knowledge/INDEX.md +++ b/silico/knowledge/INDEX.md @@ -14,5 +14,6 @@ | board-profiles | first-ship product-face pin packs; board-profile seed → defaults.py | [board-profiles.md](board-profiles.md) | | spec-interview | Thin/contradictory product specs; interview vs interactive path | [spec-interview.md](spec-interview.md) | | macos-codex-esp-idf | macOS Codex PowerShell/export.ps1 + IDF Python env | [macos-codex-esp-idf.md](macos-codex-esp-idf.md) | +| ci-host-metal | Host-always CI vs opt-in self-hosted metal plane (#101) | [ci-host-metal.md](ci-host-metal.md) | Add a row when you add a file. diff --git a/silico/knowledge/README.md b/silico/knowledge/README.md index 5582c23..76b027f 100644 --- a/silico/knowledge/README.md +++ b/silico/knowledge/README.md @@ -20,6 +20,7 @@ Product domain (idle control, drone songs, vehicle acceptance) stays in the **GC | First-flash UF2 vs esptool | [first-flash.md](first-flash.md) | | Thin / contradictory product `spec.md` (interview mode) | [spec-interview.md](spec-interview.md) | | macOS Codex + ESP-IDF (PowerShell, Python env) | [macos-codex-esp-idf.md](macos-codex-esp-idf.md) | +| Host-always CI / self-hosted metal plane later | [ci-host-metal.md](ci-host-metal.md) | | Index of topics | [INDEX.md](INDEX.md) | Do **not** load every knowledge file into context. Open only the topic needed for the current task. diff --git a/silico/knowledge/ci-host-metal.md b/silico/knowledge/ci-host-metal.md new file mode 100644 index 0000000..804c1be --- /dev/null +++ b/silico/knowledge/ci-host-metal.md @@ -0,0 +1,111 @@ +# CI: host always, metal later (self-hosted) + +**Issue:** [tig/silico#101](https://github.com/tig/silico/issues/101). +**Horizon:** optional self-hosted metal CI is beta-class ([silicov1](../../specs/silicov1.md)); host CI is **always** first ship / every GCU. + +Open this file when wiring Actions, claiming “CI green,” or designing practice-GCU / session-branch workflows. Do **not** load for ordinary firmware domain work. + +## Two planes + +| Plane | Runner | When | Green means | +|-------|--------|------|-------------| +| **Host** | `ubuntu-latest` (GitHub-hosted) | **Every** push and PR (all branches), plus `workflow_dispatch` | Plate hygiene, sim, product-path, host tests / cmake host_test / `silico gate` | +| **Metal** | Self-hosted, labeled (e.g. `self-hosted`, `silico-metal`, board class) | **Opt-in** after host green: session/lab branches, dispatch, nightly — not the default merge bar | Board talks, flash **this** SHA, identity / version match, machine-checkable accept probes | + +```text +Cloud host-gate = always-on spine proof +Self-hosted metal = optional bench proof (never implied by host alone) +``` + +**Hard rule:** host red → do not run metal. Host green alone → claim **host**, not **metal-accepted**. +Layered DoD stays in root [AGENTS.md](../../AGENTS.md): deployed ≠ product face; CI green alone ≠ metal claim. + +## Host always (shipped) + +Plate workflows (`silico/plates/gcu` and `gcu-c` `.github/workflows/ci.yml`) and the silico package workflow: + +1. **Trigger on all branch pushes** — not only `main` / `master` / `develop`. +2. **`pull_request`** — host gate on PRs. +3. **`workflow_dispatch`** — re-run host (and later metal) without a dummy commit. + +Why: practice / evaluation work should land on **lab branches** (`session/*`, feature branches) and still get honest host CI **without** forcing firmware onto public `main` (template face). That is the #101 dual-surface prep: pristine `main` vs harness runway. + +### What agents claim + +| Check name / job | Allowed claim | +|------------------|---------------| +| `host-gate` green | Host layer (tests / gate / product-path as configured) | +| No metal job / metal skipped | **Not** “on the metal”; **not** Stage E metal closed | +| Future `metal-bench` green | Only the layers that job prints (deployed / metal-io / instrumented face) — never silent upgrade to human product-face accept | + +## Metal later (prep, not required for this doc’s host changes) + +Self-hosted ESP32 (or other) on a runner is a **bench**, not a general build farm. + +### Runner shape + +- Labels: e.g. `[self-hosted, silico-metal, esp32]` or board-specific (`m5go`). +- One physical board → **concurrency group** so two jobs never flash the same port. +- Tools: same as human host path (`silico`, esptool / IDF / mpremote, Python). +- Non-interactive only (`deploy --yes`, no TTY operator gates). Pin board identity in runner env (`SILICO_METAL_PORT`, expected USB id). +- **Never** metal on untrusted fork PRs (brick + secret risk). Prefer same-repo `session/*`, `workflow_dispatch`, or owner branches. Plate `metal-bench` jobs enforce this in YAML: `SILICO_METAL_CI == true` **and** (`event_name != pull_request` **or** `pull_request.head.repo.full_name == github.repository`). + +### Metal job ladder (when enabled) + +1. `wait-device` / fixed port +2. `inspect` (REPL or C identity knock) +3. Optional recover once (first-flash / stock) if not talkative +4. `deploy --yes --verify` (+ reset) for **this** commit → **deployed** +5. App running (not parked forever in raw REPL) +6. Machine accept suite (probes below) +7. Tear down / release lock; park stock or last-known-good on failure + +### Enabling the stub + +Plate `ci.yml` may include a `metal-bench` job gated by repository variable **and** a same-repo guard: + +```text +SILICO_METAL_CI=true +# plus: not a fork pull_request (see plate workflow `if:`) +``` + +Until steps are wired, that job is a **documented stub** (fails closed with a pointer here, or stays disabled when the var is unset — default). Fork PRs never schedule metal even when the var is true. + +### Acceptance layers (spec / suite) + +| Class | CI? | Example | +|-------|-----|---------| +| Host / sim | Yes (host plane) | Product-path on shipped defaults | +| Deployed | Metal plane | `fw_name` / `fw_version` match commit | +| Metal I/O | Metal plane | Pin/telem/UART knock | +| Product face (instrumented) | Metal plane if harness exists | Mic energy, camera ROI, device `face_status` | +| Product face (human eyes/ears) | **No** by default | “Side LEDs chase”; “boot riff ~15s” — Stage D1 / field | + +Prefer cheap **device self-report** knocks (`face_status`, identity) over brittle vision first. Human see/hear remains first-class for first ship; CI must not claim it without instruments. + +Eyes/ears **bench observer** hardware (separate DUT-facing camera/mic board hung off the same runner) is a follow-on product spike — [tig/silico#102](https://github.com/tig/silico/issues/102), not host-CI scope. + +## Practice GCUs (`xuss` / `xuss-c` / `xuss-lame`) + +| Surface | Role | +|---------|------| +| `main` | Prefer **product-only clean start** (public template). | +| Lab / `session/*` | Harness runway; host CI must still run (host always). | +| `tags/clean-start`, `attempt-*` | Baseline + archives (see #101). | + +Do **not** require “push firmware to main so Actions runs.” Host always removes that excuse. Full prepare/archive/restore-main CLI is #101 follow-on; this knowledge is the CI contract those tools will assume. + +## Anti-patterns + +- Host-only green narrated as “CI proves metal” or “Stage E done.” +- Main-only `on.push.branches` so lab branches never get host gate. +- Metal job on every fork PR. +- Two concurrent metal jobs on one USB board. +- Claiming product-face accept from version string alone. +- Skipping host plane because “we have a board on the desk.” + +## Related + +- Root [AGENTS.md](../../AGENTS.md) — layered DoD, Stage E, practice GCU clean start +- [first-flash.md](first-flash.md), [esp32-usb-serial.md](esp32-usb-serial.md) — prep / duplex when metal is wired +- Plate workflows: `silico/plates/gcu/.github/workflows/ci.yml`, `gcu-c` twin diff --git a/silico/plates/gcu-c/.github/workflows/ci.yml b/silico/plates/gcu-c/.github/workflows/ci.yml index 5d6a62c..72fb80b 100644 --- a/silico/plates/gcu-c/.github/workflows/ci.yml +++ b/silico/plates/gcu-c/.github/workflows/ci.yml @@ -1,9 +1,10 @@ name: ci +# Host always / metal later — silico/knowledge/ci-host-metal.md (tig/silico#101) on: push: - branches: [main, master, develop] pull_request: + workflow_dispatch: # C plate host path on CI: # 1) cmake host_test (pure C; no silico required for that step) @@ -39,3 +40,38 @@ jobs: run: cmake --build build/host --target host_test - name: silico gate (include hygiene + host.gate) run: silico gate + + # Opt-in self-hosted metal plane. Unset SILICO_METAL_CI → job skipped (host always is enough). + # When true: register a runner labeled [self-hosted, silico-metal] and replace the stub step + # with wait-device → inspect → idf-flash/deploy → machine accept (see ci-host-metal.md). + metal-bench: + name: metal-bench + needs: host-gate + # Opt-in var + never untrusted fork PRs (brick/secret risk; see ci-host-metal.md). + if: >- + ${{ vars.SILICO_METAL_CI == 'true' + && (github.event_name != 'pull_request' + || github.event.pull_request.head.repo.full_name == github.repository) }} + runs-on: [self-hosted, silico-metal] + concurrency: + group: silico-metal-${{ github.repository }} + cancel-in-progress: false + defaults: + run: + working-directory: gcu + steps: + - name: Checkout GCU + uses: actions/checkout@v4 + with: + path: gcu + - name: Checkout silico (sibling host spine) + uses: actions/checkout@v4 + with: + repository: tig/silico + path: silico + - name: Metal plane stub (wire me) + run: | + echo "::error::SILICO_METAL_CI is true but metal-bench steps are not wired yet." + echo "See tig/silico silico/knowledge/ci-host-metal.md — host-gate alone is the always-on plane." + echo "Replace this step with: wait-device, inspect, idf build/flash or silico deploy, machine accept." + exit 1 diff --git a/silico/plates/gcu/.github/workflows/ci.yml b/silico/plates/gcu/.github/workflows/ci.yml index 68d981f..07d7b40 100644 --- a/silico/plates/gcu/.github/workflows/ci.yml +++ b/silico/plates/gcu/.github/workflows/ci.yml @@ -1,9 +1,10 @@ name: ci +# Host always / metal later — silico/knowledge/ci-host-metal.md (tig/silico#101) on: push: - branches: [main, master, develop] pull_request: + workflow_dispatch: # Layout matches first ship host pin in requirements-dev.txt (-e ../silico): # $GITHUB_WORKSPACE/gcu ← this product repo @@ -35,3 +36,38 @@ jobs: python -m pip install -e ../silico/third_party/bedside - name: Host gate run: python -m pytest -q + + # Opt-in self-hosted metal plane. Unset SILICO_METAL_CI → job skipped (host always is enough). + # When true: register a runner labeled [self-hosted, silico-metal] and replace the stub step + # with wait-device → inspect → deploy --yes --verify → machine accept (see ci-host-metal.md). + metal-bench: + name: metal-bench + needs: host-gate + # Opt-in var + never untrusted fork PRs (brick/secret risk; see ci-host-metal.md). + if: >- + ${{ vars.SILICO_METAL_CI == 'true' + && (github.event_name != 'pull_request' + || github.event.pull_request.head.repo.full_name == github.repository) }} + runs-on: [self-hosted, silico-metal] + concurrency: + group: silico-metal-${{ github.repository }} + cancel-in-progress: false + defaults: + run: + working-directory: gcu + steps: + - name: Checkout GCU + uses: actions/checkout@v4 + with: + path: gcu + - name: Checkout silico (sibling host spine) + uses: actions/checkout@v4 + with: + repository: tig/silico + path: silico + - name: Metal plane stub (wire me) + run: | + echo "::error::SILICO_METAL_CI is true but metal-bench steps are not wired yet." + echo "See tig/silico silico/knowledge/ci-host-metal.md — host-gate alone is the always-on plane." + echo "Replace this step with: wait-device, inspect, deploy --yes --verify, machine accept." + exit 1 diff --git a/silico/plates/gcu/AGENTS.md b/silico/plates/gcu/AGENTS.md index dc1f88b..9c291f7 100644 --- a/silico/plates/gcu/AGENTS.md +++ b/silico/plates/gcu/AGENTS.md @@ -42,7 +42,7 @@ Assume low ops literacy. Prefer doing over dumping shell. **Poll USB** after ask **Next-step forks:** use a structured chooser (`bedside ask` or host picker). Never a free-text `1. / 2.` menu in chat. -**Repo workflow:** inspect the GCU remote (branches / PRs / issues). **Simple** tree → recommend commits on `main` (PRs later). **Well-used** → ask whether to use PRs (note: CI/CD proof is then an explicit PR step). Never PR-spam: one branch/PR per first-ship path max unless the operator asked to split. Issues stage work; commits stage history. Full rule: silico root AGENTS **Repo workflow**. +**Repo workflow:** inspect the GCU remote (branches / PRs / issues). **Simple** product tree → recommend commits on `main` (PRs later). **Practice / evaluation harness** (xuss family or clean-start eval) → prefer a **lab branch** from clean-start (`session/…`) so public `main` stays template-clean; plate **host-gate runs on every branch** (host always) — do not force firmware onto `main` “so Actions runs.” **Well-used** product repos → ask whether to use PRs (CI/CD proof is then an explicit PR step). Never PR-spam: one branch/PR per first-ship path max unless the operator asked to split. Issues stage work; commits stage history. Full rule: silico root AGENTS **Repo workflow** + knowledge `ci-host-metal.md`. **Metal acceptance:** first ship is not “on the metal” until the operator can **see or hear** the documented **product face** after deploy. Wrong plate pin / product face LED confusion is work to finish with the operator — not an honesty footnote under a done banner. Trackers do not replace observe. **If the pin / product face mapping is unclear, ask the operator to clarify (structured chooser) in that turn** — do not only file an issue or assume GPIO16 is fine. Prefer **`silico board-profile`** pin packs (link `profile = "…"` in `parts.toml`; `seed` into `defaults.py` after confirm) over inventing M5 side/speaker pins from memory. diff --git a/specs/silicov1.md b/specs/silicov1.md index b6f6f0f..ed896b2 100644 --- a/specs/silicov1.md +++ b/specs/silicov1.md @@ -34,7 +34,7 @@ v1 succeeds when: 1. **first ship:** non-software human + agent on a Mac → real USB device (+ sim) end-to-end, host gate green. 2. **update path (alpha):** same verified build can leave as a **prototype** with a **real customer for alpha** feedback — not beta polish; not full integrity suite. -3. **~1 month later (beta horizon):** more customers can get beta-class units; **update integrity** (definition TBD — [spike](https://github.com/tig/silico/issues/4)) and optional self-hosted metal CI belong here, not on update path. +3. **~1 month later (beta horizon):** more customers can get beta-class units; **update integrity** (definition TBD — [spike](https://github.com/tig/silico/issues/4)) and optional self-hosted metal CI belong here, not on update path. **Host CI is always-on** (every branch); metal is opt-in later — see [silico/knowledge/ci-host-metal.md](../silico/knowledge/ci-host-metal.md) and [tig/silico#101](https://github.com/tig/silico/issues/101). 4. Ongoing: silico remains how that company updates edge (foundational, not a demo). 5. All three GCUs share the spine without private folklore dumps into silico.