---
name: demo-build
description: Build or rebuild the stored demo system into a Halo tenant via the system-package pipeline
---

# Demo Build

Replay the stored demo system — **package #1 of the unified system-package format** — into a Halo
tenant. Load the **`system-package`** skill (the pipeline contract) and the **`demo-build`** skill
(the scaffold's authoring contract); this command is the run sequence. There is ONE path: the
`package.py` driver. `demo_compile.py` is the emission engine underneath it, not an entry point.

## Usage

```
/grcitsm-analyst:demo-build [--run outputs/demo-rebuild-<date>] [--fresh]
```

`--fresh` means the target site has been wiped and you expect to build from zero. Without it,
guarded creates skip anything already present.

## Agents

| Agent | Purpose | When Invoked |
|-------|---------|--------------|
| `halo-pipeline-coordinator` | Role spec the MAIN THREAD loads to drive each apply loop (not dispatched — subagents cannot field the executor fleet) | Step 8, once per family |
| `halo-action-executor` | Dumb dispatcher: one action → resolve `$ref`s → one MCP write call → append progress. Carries identity confirmation + `expect` conflict guards | Each leased action |

Validation, compilation, and stamping are deterministic script work, never agent work — the driver
and its engines never call MCP.

## Sequence

```
PKG=<run>/package   SK=<plugin>/skills   DRV="python3 $SK/system-package/scripts/package.py"
```

0. **Snapshot first (read-only).** `ticket_summary`, `asset_summary`, `active_inventory`,
   `open_issues`, `closed_issues`, `vulnerability_deviations`, `system_information` →
   `<run>/snapshot/`. The only before-picture you will have; take it even on an additive build.

1. **Copy the package out of the plugin:** `cp -R $SK/demo-build/scaffold/. $PKG/` — never work
   inside the plugin directory. Then the offline gate, before any tenant contact:
   `$DRV validate --package $PKG` must pass.

2. **Resolve the tenant — blocking.** `scripts/resolve-tenant-prefix.sh` (pass the
   `.claude/grcitsm-tenant.json` pin as `--expect` when the project carries one). Several live
   GRCITSM connectors and no pin is ambiguous: show the candidates and ask. Write the result to
   `<run>/state/tenant-prefix`.

3. **Capture the tenant witness — blocking.** Under the pinned prefix only, `CF_client_details` →
   the row for the package's `target.client_name` → `<run>/state/tenant-witness.json`
   (`{"client_id": N, "client_name": "..."}`). No matching row, or a failed read, is a STOP.
   Cloned instances share ids AND names — confirm by content: `asset_summary` on the target site
   shows this scaffold's naming, or nothing at all on a fresh build.

4. **Ask: populate the KSI validation set? — blocking** (`AskUserQuestion`). The set is 226
   per-check Validation tickets plus a stored week of 1,582 run rows — roughly 2,000 apply actions
   on top of the operational build, plus the `create_validation_tickets` sweep and a snapshot
   refresh as its precondition. **Skip = simply never compile/apply the `validations` and
   `validation-runs` families in step 8** — the package still carries them, and a later run can
   add them to this same build (the families are independent; creates are guarded).

5. **Regenerate `lookups/` against the resolved tenant** (read-only MCP; the shipped lookups are
   headers-only by design — the regeneration table is in the demo-build skill). Then **stamp**:
   `$DRV stamp-lookups --package $PKG --prefix <prefix>` — it refuses if the lookups still look
   header-only or were resolved against a different tenant. No stamp → `check` refuses; this is
   the D5 anchor of the three-way tenant assertion, not paperwork.

6. **If rebuilding: the wipe.** Manual UI work — MCP cannot hard-delete. Assets AND tickets;
   Ports & Protocols is optional now (`pps-snapshot.csv` makes step-family `system` update in
   place instead of duplicating). Then re-read `ticket_summary` + `asset_summary` scoped to the
   site and confirm the expected zeros. A guard hit against data you meant to wipe means the wipe
   was incomplete.

7. **Check:** `$DRV check --package $PKG --witness <run>/state/tenant-witness.json` must pass
   clean. It runs the three-way tenant assertion plus both family engines' own checks — the only
   guard against silently-dropped lookup values.

8. **Compile and apply, per family, in dependency order** — one `$DRV compile --package $PKG
   --out <run>/state --only <family>` then one `/grcitsm-analyst:halo-apply` per family:

   | Order | Family | After it lands |
   |---|---|---|
   | 1 | `system` | **`reconcile_ssp_assets`** (it reads `Site.CFBaselines`, so it MUST follow `system`; expect a relay timeout that completes server-side — poll `asset_summary` until the count settles at the baseline's control count), then `reconcile_ssp_relationships_ci_ir` |
   | 2 | `ssp` | rel 6 fires on asset events; `reconcile_ssp_relationships_ir_is` after IS rows land |
   | 3 | `cloud-assets` | **refresh `lookups/snapshot.csv` with the returned asset ids** — the documented two-pass flow |
   | 4 | `tickets` | — |
   | 5–6 | `validations`, `validation-runs` *(Populate only)* | precondition: run the `create_validation_tickets` sweep (relay timeout = normal), wait ≥90s for the framework-CF derive, snapshot the 46 L2 ids as `ticket,<KSI id>,<ticket id>`, re-run `--only validations` compile |

   Greenfield builds omit `--write-back`; it is required only when compiled output UPDATES rows
   that already exist on the tenant (see the `system-package` skill).

9. **Verify against the tenant, not the ledger** — the ledger has fabricated dones under a clean
   chain (measured 2026-08-10). Per-type counts vs the plan, `get_ticket`/`get_asset` read-backs
   on a sample proving fields are POPULATED, and `pipeline.py verify-chain` (its fabrication
   audit flags instant dones, id-range outliers, per-owner anomalies). Count runbook-cascade
   tickets separately — correct output, not duplicates. Refuse to report success when populated
   counts diverge from done counts; report the divergence.

## Guardrails

- **Write-tool permissions must be pre-allowed** for the target tenant's `CF_*` writes before you
  start, or every apply wave stalls on approval prompts.
- **Never work inside the plugin directory** — `~/.claude/plugins/` is re-cloned by `/plugin`
  operations and un-pushed changes there are destroyed.
- **Executors must never route a blocked write around the tool layer.** A classifier denial is a
  stop-and-ask, not a prompt to reach for `curl` or a subprocess. And never append a `done` you
  did not earn — identity confirmation before every done is the executor contract.
- Every ticket is created New by design. Stage movement is the human walk-through, not seeded data.
