# smoke

## Identity

- **Name**: `ai.stravica.platform.smoke`
- **Alias**: `smoke`
- **Purpose**: Fresh-clone smoke suite for the Dave platform. Simulates a clean clone and validates structure, server, APIs, and CLI tools before shipping.

## Status

Implemented: all 52 assertions from the legacy `test/smoke.sh` (path existence, workspace server boot, HTML page HTTP 200s, JSON API validity, CLI `--help` invocations, `secrets` execution, `dave.sh --help`).

Deferred: none. This is a direct Node.js port with no scope change.

## Install / invoke

From the repo root:

```sh
npm run test:smoke
```

Or invoke the binary directly:

```sh
node core/tools/smoke/bin/smoke
```

## Commands

The tool has a single default command; `npm run test:smoke` is the canonical invocation.

| Flag            | Purpose                                                         |
|-----------------|-----------------------------------------------------------------|
| `--help, -h`    | Print usage and exit 0                                          |
| `--root <path>` | Repo root to smoke-test (default: auto-detected from the binary location) |
| `--port <n>`    | HTTP port for the workspace server during the run (default: 4201) |

No verbs, no subcommands — this is a single-purpose runner.

## Exit codes

| Code | Meaning                                                              |
|------|----------------------------------------------------------------------|
| `0`  | All checks passed                                                    |
| `1`  | One or more checks failed, or a usage error (bad argument)           |
| `2`  | Internal error (uncaught exception in the tool itself)               |
| `3`  | IO / transport failure during setup (tempdir, copy, or server launch)|

## Operational discipline rules

- **No side effects on the source tree.** The suite copies the repo into a temp directory (`rm -rf`'d on exit) and performs every assertion against the copy. The source tree is only read.
- **Single chokepoint for external effects.** `src/runner.mjs` owns every process spawn, HTTP fetch, and filesystem mutation. Commands and checks delegate to it; no other module may spawn or fetch.
- **Server lifecycle is bracketed.** The workspace server is launched once per run, killed in `finally`, and never leaks across invocations. If a check throws, cleanup still runs.
- **HTTP / JSON checks are gated.** If the server fails to come up within the ready-probe budget, downstream HTTP and JSON assertions are skipped (not failed) — matching the legacy behaviour.
- **Zero npm dependencies.** The tool uses only `node:` built-ins (`util`, `child_process`, `fs/promises`, `path`, `os`, `test`, `assert/strict`) plus the global `fetch`.

## Project layout

```
core/tools/smoke/
  bin/smoke                # node shebang shim — one line
  package.json             # name, version, type: module, no dependencies
  README.md                # this file
  src/
    cli.mjs                # parseArgs, --help, exit-code translation
    checks.mjs             # pure check plan: path, http, json, cli classes
    runner.mjs             # external-effects chokepoint (spawn, fetch, fs)
    commands/
      run.mjs              # orchestrates copy → launch → check → cleanup
  test/
    checks.test.mjs        # plan shape, parity with legacy smoke.sh
    cli.test.mjs           # arg parsing and exit-code cases
    run.test.mjs           # orchestration via fake runner
```

Per-entry rationale:

- **`bin/smoke`** — one-line shim; logic lives in `src/cli.mjs`.
- **`src/cli.mjs`** — the only module that calls `process.exit`. Commands return numbers or throw; this wrapper translates.
- **`src/checks.mjs`** — pure data + helpers. Defines the ordered plan. No I/O.
- **`src/runner.mjs`** — chokepoint for every external effect. Tests inject fakes here.
- **`src/commands/run.mjs`** — the single command that orchestrates the suite.
- **`test/`** — `node:test` files. No separate config.

## Check plan (parity with legacy `test/smoke.sh`)

| Section | Count | Class        | What                                                          |
|---------|-------|--------------|---------------------------------------------------------------|
| Step 3  | 1     | `execFile`   | `dave.sh` is an executable file                               |
| Step 4  | 1     | `file`       | `dave.ps1` exists                                             |
| Step 5  | 33    | `path`       | Expected-path existence (roles, retrieval, desk, workspace app, .gitkeeps, top-level docs) |
| Step 6-7| 1     | `serverReady`| Workspace server becomes reachable on the test port          |
| Step 8  | 7     | `httpStatus` | `/`, `home.html`, `onboarding.html`, `tasks.html`, `knowledge.html`, `secrets.html`, `viewer.html` all return 200 |
| Step 9  | 3     | `jsonValid`  | `/__api/overview`, `/__api/projects`, `/__api/work` return parseable JSON |
| Step 10-13 | 4  | `cliExit`    | `backlink-indexer.mjs --help`, `graph-builder.mjs --help`, `query.mjs --help`, `desk --help` exit 0 |
| Step 14 | 1     | `vaultExit`  | `node core/tools/secrets/bin/secrets` exits 0 or 1           |
| Step 15 | 1     | `cliShell`   | `bash dave.sh --help` exits 0                                 |
| **Total** | **52** |            |                                                               |

Labels match the bash `PASS:` lines verbatim — CI log comparisons stay valid.

## Tests

```sh
node --test core/tools/smoke/test/*.test.mjs
```

The test suite exercises the plan, the CLI wrapper, and the orchestration layer via a fake runner. The real `runner.mjs` is exercised end-to-end by `npm run test:smoke` against the live repo.

## The tooling standard

Governed by [`core/knowledge/standards/tooling.md`](../../knowledge/standards/tooling.md). This tool follows sections §1–§16 as written: identity (§1), Node-only (§2), zero-dep (§3), project structure (§4), CLI conventions (§5), exit codes (§6), chokepoint (§7), testing (§8), README-as-contract (§9), operational discipline (§10), JSON camelCase (§11).
