---
name: smoke-generation
description: Validate CLI templates by checking that every imported component, type, and method signature documented in /templates/skills/ actually exists in SmartStack.app (develop). Catches the "documented-but-missing-symbol" class of drift (e.g. the historical EntityLookup paradox, now resolved by the ui-primitives scaffolder).
argument-hint: "[--quick|--full] [--scope=frontend|backend|all] [--app-path=<path>] [--report-only]"
allowed-tools: Read, Grep, Glob, Bash, Write
---

## Current state (auto-injected)

- CLI version: !`node -p "require('./package.json').version" 2>/dev/null || echo "unknown"`
- App branch: !`git -C "D:/01 - projets/SmartStack.app/02-Develop" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown"`
- App last commit: !`git -C "D:/01 - projets/SmartStack.app/02-Develop" log --oneline -1 2>/dev/null || echo "unknown"`
- Last smoke run: !`cat .claude/cache/smoke-generation.json 2>/dev/null | head -5 || echo "no previous run"`

<objective>
Detect "documentation lies": patterns/imports/types that the CLI documents as canonical (and may even enforce via post-checks) but that do NOT exist in SmartStack.app. The historical canonical example was `EntityLookup` (documented in 4 CLI files and enforced by check C7, while the component existed nowhere); that one is now resolved — `scaffold-ui-primitives` scaffolds `src/components/ui/EntityLookup.tsx` into the client project at Phase 3.0, and audit DEV-UI-022 enforces its usage on FK fields. The skill still detects future paradoxes of the same shape: any new `import { Foo } from '@/…'` that resolves nowhere — neither in `SmartStack.app` (npm SDK) nor in the CLI's `templates/skills/development/frontend/ui-primitives/` (scaffolded primitives) — is flagged.

Two modes :
- **`--quick` (default)** — pure static analysis (~30s). Greps imports / types / method signatures in `templates/skills/**/*.md`, verifies each one resolves to a real file or symbol in `D:/01 - projets/SmartStack.app/02-Develop/`.
- **`--full`** — runs an actual scaffold + `dotnet build` + `npm run build` in a sandbox. Heavier (~5-10 min) but catches runtime errors not visible by static analysis.
</objective>

<quick_start>
```bash
/smoke-generation                        # Quick static analysis on all CLI templates
/smoke-generation --quick                # Same as above (explicit)
/smoke-generation --full                 # Full sandbox scaffold + build (slow, thorough)
/smoke-generation --scope=frontend       # Static analysis on frontend imports only
/smoke-generation --scope=backend        # Static analysis on backend types/signatures only
/smoke-generation --report-only          # Skip writing cache, print to stdout only
/smoke-generation --app-path=<path>      # Override default develop path
```
</quick_start>

<paths>

```
APP_ROOT      = D:/01 - projets/SmartStack.app/02-Develop   # default
APP_FRONTEND  = $APP_ROOT/web/smartstack-web/src
APP_BACKEND   = $APP_ROOT/src/SmartStack.Domain   (+ Application + Infrastructure + Api)

CLI_TEMPLATES = D:/01 - projets/SmartStack.cli/features/version-5-optimisation/templates/skills
CACHE_FILE    = D:/01 - projets/SmartStack.cli/features/version-5-optimisation/.claude/cache/smoke-generation.json
```

If `--app-path=<path>` is provided, override `APP_ROOT`.

</paths>

<workflow>

## Step 0 — Preflight

```bash
APP_ROOT="${APP_PATH:-D:/01 - projets/SmartStack.app/02-Develop}"
[ -d "$APP_ROOT/src/SmartStack.Domain" ] || { echo "ERROR: APP_ROOT not found at $APP_ROOT"; exit 1; }
[ -d "$APP_ROOT/web/smartstack-web/src" ] || { echo "ERROR: app frontend not found"; exit 1; }
mkdir -p "D:/01 - projets/SmartStack.cli/features/version-5-optimisation/.claude/cache"
```

## Step 1 — Frontend static checks (`--scope=frontend|all`)

### 1.1 Extract every documented frontend import

Grep all `import { X } from '@/components/...'` (and similar) across `templates/skills/**/*.md` :

```bash
TEMPLATES_ROOT="D:/01 - projets/SmartStack.cli/features/version-5-optimisation/templates/skills"
APP_FRONTEND="$APP_ROOT/web/smartstack-web/src"

# Collect imports of the form: import { Foo, Bar } from '@/{path}'
grep -rPnho "import\s+\{[^}]+\}\s+from\s+['\"]@/[^'\"]+['\"]" "$TEMPLATES_ROOT" \
  --include='*.md' --include='*.tsx' --include='*.ts' \
  | sort -u > /tmp/smoke-imports.txt
```

### 1.2 Resolve each import against the real file system

For each line in `/tmp/smoke-imports.txt`:
- Extract the path part : `@/components/ui/EntityLookup` → `components/ui/EntityLookup`
- Extract the symbol(s) : `EntityLookup`, `EntityCard`, etc.
- Test : does `$APP_FRONTEND/components/ui/EntityLookup.tsx` exist ?
- If file exists, optional deeper check : grep the symbol export in the file (`export.*EntityLookup`)

```bash
while IFS= read -r LINE; do
  # Parse the path
  PATH_PART=$(echo "$LINE" | sed -E "s/.*from\s+['\"]@\/([^'\"]+)['\"].*/\1/")
  # Resolve to .ts / .tsx / .ts (index.ts)
  FOUND=""
  for EXT in ".tsx" ".ts" "/index.tsx" "/index.ts"; do
    if [ -f "$APP_FRONTEND/${PATH_PART}${EXT}" ]; then
      FOUND="$APP_FRONTEND/${PATH_PART}${EXT}"
      break
    fi
  done
  if [ -z "$FOUND" ]; then
    echo "[BROKEN] @/${PATH_PART}  ← documented in CLI, NOT in app"
  fi
done < /tmp/smoke-imports.txt
```

### 1.3 Hook signatures (useTheme, useAuth, etc.)

Grep documented hook usages and verify the destructured field exists in the source :

```bash
# Documented usages like : const { user, login, logout } = useAuth();
grep -rPnh "const\s+\{([^}]+)\}\s*=\s*use\w+\(\)" "$TEMPLATES_ROOT" \
  --include='*.md' \
  | sort -u > /tmp/smoke-hook-usages.txt
```

For each `useX()` call, locate the matching context in `$APP_FRONTEND/contexts/{X}Context.tsx` (e.g. `useAuth` → `AuthContext.tsx`), extract the context value type, and verify each destructured field is exported. Symbols missing from the context value → `[BROKEN]`.

### 1.4 Component props (DataTable, EntityCard, etc.)

For each documented component prop usage in templates (e.g. `<DataTable data={...} columns={...} searchable />`), open the component source file and verify the prop is declared in its `Props` interface. Mismatches → `[BROKEN]`.

## Step 2 — Backend static checks (`--scope=backend|all`)

### 2.1 Extract documented C# types and signatures

Grep all `using SmartStack.{X}` and explicit type names referenced in code blocks of `templates/skills/**/*.md` :

```bash
APP_BACKEND_ROOT="$APP_ROOT/src"
grep -rPnh "^\s*using\s+SmartStack\.[A-Za-z.]+;" "$TEMPLATES_ROOT" --include='*.md' \
  | sort -u > /tmp/smoke-usings.txt
```

For each `using SmartStack.X.Y.Z;`, verify a corresponding directory or file exists under `$APP_BACKEND_ROOT/SmartStack.{Layer}/...`.

### 2.2 Domain entity references

Grep documented C# class names (like `NavigationApplication`, `AiTool`, `WorkflowVersion`) in templates, then verify each one exists in `$APP_BACKEND_ROOT/SmartStack.Domain/`.

```bash
# Pattern : public class Foo : BaseEntity
grep -rPnho "(?:public\s+)?(?:class|record|interface)\s+([A-Z][A-Za-z0-9]+)\s*:?\s*(?:BaseEntity|IRequest|I[A-Z][A-Za-z]+Entity)" "$TEMPLATES_ROOT" \
  --include='*.md' \
  | sort -u > /tmp/smoke-types.txt
```

For each extracted type, run a Glob in `$APP_BACKEND_ROOT/**/*.cs` for `class TypeName` or `record TypeName`. No match → `[BROKEN]`.

### 2.3 Method signatures

For commonly cited methods (`NavigationApplication.Create(...)`, `Permission.CreateForModule(...)`, etc.), grep the signature in the documented template and the actual signature in the source file. Compare parameter list (count + names). Diff → `[DRIFT]`.

Critical method signatures to cross-check :
- `NavigationApplication.Create` (currently `code, label, description?, icon?, iconType, route?, displayOrder, isOpen=false, isPersonal=false`)
- `Permission.CreateForModule`, `Permission.CreateForSection`, `Permission.CreateForResource`
- `BaseEntity.GetExtensionValue<T>` / `SetExtensionValue<T>` / `RemoveExtensionValue` / etc.

## Step 3 — Cross-cut checks (always run)

### 3.1 i18n namespace consistency
Already covered by `frontend-checks.sh` C28 — call it directly :
```bash
# frontend-checks.sh was removed in v5 — use /audit-dev-frontend DEV-UI-004 instead
```

### 3.2 Theme tokens documented vs exposed
Compare the CSS variables documented in `application/references/themes-db-driven.md` against `$APP_FRONTEND/index.css`. Variables in doc but not in CSS → `[BROKEN]`.

### 3.3 Layouts count
Verify exactly 4 layout files in `$APP_FRONTEND/layouts/` (`AppLayout`, `AuthLayout`, `DocsLayout`, `PublicLayout`).

### 3.4 Core catalogue ↔ extension whitelist (the Company→Organisation class of drift)

`lib/core-catalog.ts` (`CORE_CATALOG_V1`) is the SINGLE SOURCE the BA→dev pipeline trusts for
"which Core entity is FK-able". When the app renames or merges a whitelist aggregate — e.g. the
`Company → TenantOrganisation` unification that DROPPED `ref_Companies` and moved the config to
`tenant_TenantOrganisations` — the catalogue silently keeps pointing a `scope core` FK at a deleted
type + an empty namespace → a compile break at the next generation. The unit `core-catalog-drift.test.ts`
only pins the catalogue to its own inline SKILL.md copies, NEVER to the app, so this drift is invisible
to the test suite. Close that gap here.

The authoritative whitelist is `SmartStackExtensionDbContext` (the base class every extension inherits):
each `public DbSet<TYPE> … => Set<TYPE>();` is one FK-able Core entity; its table comes from the
entity's `IEntityTypeConfiguration` (`builder.ToTable("…", SchemaConstants.Core)`).

```bash
EXT_CTX="$APP_ROOT/src/SmartStack.Infrastructure/Persistence/Extensions/SmartStackExtensionDbContext.cs"
CATALOG="$CLI_TEMPLATES/lib/core-catalog.ts"

# (a) entity TYPES the extension context actually exposes (the real whitelist)
grep -oP 'DbSet<\K[A-Za-z0-9]+' "$EXT_CTX" | sort -u > /tmp/smoke-ext-whitelist.txt
# (b) FK-able names the catalogue claims — ONLY CORE_CATALOG_V1 rows, discriminated by
#     `qualifiedTable:` (the CORE_RESERVED rows carry `useInstead:` and are NOT FK-able)
grep -P 'qualifiedTable:' "$CATALOG" | grep -oP "name:\s*'\K[A-Za-z0-9]+" | sort -u > /tmp/smoke-catalog-names.txt

# A V1 catalogue name NOT exposed as a DbSet<name> on the extension context = DRIFT
comm -23 /tmp/smoke-catalog-names.txt /tmp/smoke-ext-whitelist.txt   # any line printed → [DRIFT]
```

For each catalogue entry that survives (a)∩(b), also confirm its `table:` / `qualifiedTable:` still
matches the entity's `builder.ToTable("<table>", SchemaConstants.Core)` in
`$APP_ROOT/src/SmartStack.Infrastructure/Persistence/Configurations/**/<Name>Configuration.cs`
(an empty/`// intentionally empty` configuration is itself a drift signal — the table moved). A name in
(b) absent from (a), or a `table:` that no longer matches the app's `ToTable(…)`, is a `[DRIFT]`:
fix `lib/core-catalog.ts` (names + `table`/`qualifiedTable` + `CORE_WHITELIST_V1_NAMESPACES` +
`CORE_PROJECTABLE_FIELDS` + aliases), then run `vitest run templates/skills/lib` so
`core-catalog-drift.test.ts` forces the matching update of the inline SKILL.md tables.

### 3.5 Capability symbols ↔ platform services (the "documented-service-vanished" class of drift)

`lib/capability-catalog.ts` + `development/backend/data-layer/references/file-storage.md` teach the
BA→dev pipeline that transverse platform SERVICES exist (`IFileStorageService`, `AddExtensionSearch`,
`AddExtensionTimeEntryRefs`, `IEmailService`) — knowledge with NO import the frontend scan or the
DbSet check above could verify. If the app renames or removes one of these seams, the skills keep
steering generated code at a phantom symbol. Same mechanic as 3.4: verify each documented symbol
still exists in the app source.

```bash
# Symbols the capability catalogue + file-storage reference claim (keep in sync when adding entries)
for SYM in IFileStorageService StorageType UploadAsync DownloadAsync GetSecureUrlAsync \
           AddExtensionSearch AddExtensionTimeEntryRefs IEmailService; do
  grep -rql "$SYM" "$APP_ROOT/src/SmartStack.Application" "$APP_ROOT/src/SmartStack.Infrastructure" \
    > /dev/null || echo "[DRIFT] capability symbol $SYM not found in the app"
done
# The DI registration must survive too (IFileStorageService is Scoped via AddSmartStack)
grep -rq 'IFileStorageService' "$APP_ROOT/src/SmartStack.Infrastructure/DependencyInjection.cs" \
  || echo "[DRIFT] IFileStorageService no longer registered in DependencyInjection.cs"
```

Any `[DRIFT]` line → fix `lib/capability-catalog.ts` (+ `references/file-storage.md` and the
`platform-capabilities:v1` inline tables — `capability-catalog-drift.test.ts` forces the carriers),
never the call sites.

## Step 4 — Build the report

```
================================================================================
              SMOKE GENERATION REPORT — {date}
================================================================================
APP : develop @ {commit}
CLI : v{version} @ {branch}
SCOPE : {scope}   MODE : {quick|full}

[FRONTEND]
  imports scanned    : {N}
  imports broken     : {B}
  hooks scanned      : {N}
  hooks broken       : {B}
  component props    : {N}    drift : {D}

[BACKEND]
  usings scanned     : {N}    broken : {B}
  types scanned      : {N}    missing : {M}
  signatures scanned : {N}    drift : {D}

[CROSS-CUT]
  i18n consistency   : {OK|FAIL}
  theme tokens       : {OK|FAIL — {n} missing}
  layouts count      : {OK|FAIL — found {n}, expected 4}
  core catalogue     : {OK|FAIL — {n} entity/table drift vs SmartStackExtensionDbContext}

--------------------------------------------------------------------------------
DETAILED ISSUES
--------------------------------------------------------------------------------

[BROKEN] @/components/ui/SomeMissingPrimitive
  → Referenced in :
    - templates/skills/development/frontend/component/cli/scaffold-component/generate.ts:NNN
    - (apex/references removed in v5 — check development/frontend/*/SKILL.md)
  → Action : either add the component to SmartStack.app SDK,
             OR scaffold it into the client project via a new
             primitive in templates/skills/development/frontend/ui-primitives/,
             OR remove the dead reference from the CLI templates.

[DRIFT] NavigationApplication.Create signature
  → CLI docs : Create(code, label, ..., isOpen=false, isPersonal=false)
  → App impl : Create(code, label, ..., isOpen=false, isPersonal=false, isHidden=false)
  → Action : update templates to include `isHidden` parameter.

...

--------------------------------------------------------------------------------
SUMMARY : {totalIssues} issues  ({broken} BROKEN, {drift} DRIFT, {missing} MISSING)
================================================================================
```

## Step 5 — Persist cache (unless `--report-only`)

```bash
cat > "$CACHE_FILE" << EOF
{
  "lastRun": "$(date -Iseconds)",
  "appCommit": "$(git -C "$APP_ROOT" rev-parse --short HEAD)",
  "appBranch": "$(git -C "$APP_ROOT" rev-parse --abbrev-ref HEAD)",
  "scope": "{scope}",
  "mode": "{quick|full}",
  "totalIssues": {N},
  "broken": {B},
  "drift": {D},
  "missing": {M}
}
EOF
```

## Step 6 — Exit code

- 0 issues → exit 0
- ≥ 1 issues → exit 1 (so the skill can be called as a CI quality gate, e.g. inside a pre-commit hook).

</workflow>

<full_mode>

## `--full` mode (sandbox + real build)

This mode is documented for completeness but is OPTIONAL — `--quick` covers ~80 % of drifts at a tiny fraction of the cost.

### Steps (full mode only)

1. Create a sandbox under `~/.smartstack-smoke/run-{timestamp}/`
2. Run `ss init smoke-test --provider=azure-devops --no-prompt` in the sandbox
3. Generate a representative module via the development skills (`/development/backend/entity`, `/development/backend/controller`, …) : 1 entity (`Product`), 1 FK (`CategoryId`), one CRUD page set (List, Detail, Create, Edit)
4. Run :
   - `cd src/SmartStack.Api && dotnet restore && dotnet build`
   - `cd web/smartstack-web && npm install && npm run build`
5. Capture stderr/stdout, parse for errors, fail if any compile error or missing import
6. Cleanup the sandbox unless `--keep-sandbox` flag is passed

### When to prefer `--full` over `--quick`

- Before publishing a new CLI version (release gate)
- After a non-trivial refactor of the scaffolding CLIs in `templates/skills/development/`
- When `--quick` reports 0 issues but you suspect a runtime-only problem

### Cost
- Network : ~50-150 MB (npm install)
- Disk : ~500 MB (sandbox + node_modules)
- Time : 5-10 min (mostly npm install + dotnet restore)

</full_mode>

<integration>

### Pre-commit hook
Add to `.git/hooks/pre-commit` (or via husky) :

```bash
#!/bin/bash
# Block commits that introduce documentation drift
bash -c '/smoke-generation --quick --report-only' && exit 0 || {
  echo "[smoke-generation] BROKEN imports / DRIFT detected — fix before commit."
  exit 1
}
```

### Manual workflow
- After enriching any `templates/skills/**/*.md` with new imports/types : run `/smoke-generation --quick`
- Before opening a PR that touches multiple skills : run `/smoke-generation --quick` and paste the report into the PR description
- After bumping the SmartStack.app target branch : run `/smoke-generation --quick` to surface drifts triggered by the new version

</integration>

<known_limitations>

- **Quick mode misses runtime-only errors** : a bad TS generic, a wrong async return shape, a missing CSS class — `--full` mode is needed for these.
- **Heuristic regex** : may produce false positives on unusual import syntaxes (rare in practice). Verify each `[BROKEN]` manually if in doubt.
- **Single source of truth** : assumes the develop worktree (02-Develop) IS the source of truth. Override via `--app-path` if testing against a different branch (e.g. for forward-compat testing on a feature branch).
- **Does not check scaffolder output** : the C# / TS code emitted by `templates/skills/development/**/cli/scaffold-*/` CLIs is not validated by this skill. Use `--full` for that path.

</known_limitations>

<success_criteria>
- Quick mode runs in < 60 seconds on a clean install
- Detects the "documented-but-missing-symbol class of drift" reliably (any import that resolves nowhere — neither in `SmartStack.app` SDK nor in `templates/skills/development/frontend/ui-primitives/`). EntityLookup was the historical exemplar; the same detector now catches its successors.
- Detects method signature drift (parameters added/removed in app but doc not updated)
- Detects missing CSS theme variables (doc claims a token, app doesn't expose it)
- Detects Core-catalogue drift (an entity/table in `CORE_CATALOG_V1` that no longer matches the app's `SmartStackExtensionDbContext` whitelist — the `Company → TenantOrganisation` class of break)
- Reports are deterministic, scannable, and actionable
- Exit code reflects the number of issues — usable as a quality gate
</success_criteria>
