# higgs-cloak adapter — free Higgsfield Seedance on the `seedance-direct` route

> **Now vendored in-tree.** As of [ADR 0006](../adr/0006-vendor-seedance-engine-in-tree.md)
> (which supersedes [ADR 0005](../adr/0005-free-higgsfield-seedance-via-external-adapter.md)),
> this engine's code lives inside videoclaw at **[`engines/seedance-direct/`](../../engines/seedance-direct/)**.
> After a one-time `engines/seedance-direct/bootstrap.sh`, `seedance-direct` renders **free by
> default** — no env var needed. `VCLAW_SEEDANCE_DIRECT_ADAPTER` still overrides;
> `VCLAW_SEEDANCE_DIRECT_NATIVE=1` selects the paid path, and without it an engine that is
> not set up refuses rather than falling through to the paid API. The full contract + limits now live in
> [`engines/seedance-direct/README.md`](../../engines/seedance-direct/README.md); this file is
> kept for the wiring reference below. `higgs-cloak` remains the upstream dev origin of the code.

The engine drives Higgsfield's free, unlimited *Enhanced Seedance 2.0 Fast*
(`seedance_mini_unlimited`) through a stealth browser (CloakBrowser) at **$0/clip**, plugging
into videoclaw as a route adapter (one JSON object in on stdin, one out on stdout).

## Enable

Bootstrap the in-tree engine once, then run `seedance-direct` as normal — no env var:

```bash
engines/seedance-direct/bootstrap.sh                              # venv + deps + chromium
engines/seedance-direct/.venv/bin/python engines/seedance-direct/bootstrap/import_cookies.py  # seed .cloak-profile
```

Once the engine is fully bootstrapped (both `engines/seedance-direct/.venv` and its
`.cloak-profile/` exist), `resolveAdapterCommand` routes `seedance-direct` to the in-tree
engine by default (see ADR 0006). To force a specific command instead, set
`VCLAW_SEEDANCE_DIRECT_ADAPTER` (e.g. at `engines/seedance-direct/run.sh`, or an external
`higgs-cloak/higgs_vclaw_adapter.sh` copy); to select the paid xskill.ai path, set
`VCLAW_SEEDANCE_DIRECT_NATIVE=1`. When the engine is not fully bootstrapped and neither of
those is set, the route refuses (`execution_blocked_by_readiness`) instead of billing the
paid API — ADR 0007.

> ⚠️ videoclaw resolves the adapter command from `process.env` (`resolveAdapterCommand`
> in `src/video/execution-runtime.ts`); it has **no global dotenv autoloader** for this
> path. Putting the line in `.env.local` is the documented home, but it only takes effect
> when that file is actually exported into the environment running `vclaw`
> (`set -a; source .env.local; set +a`, or add the export to your shell profile). If the
> var is unset, videoclaw silently falls back to the built-in paid `seedance-direct`
> adapter.

Setting this var **overrides** the paid `ark/seedance-2.0` path on `seedance-direct`.
Unset it to restore paid Seedance. (Option B in the upstream
`higgs-cloak/VCLAW_INTEGRATION.md` — a distinct `higgsfield-seedance` route via
`VCLAW_HIGGSFIELD_SEEDANCE_ADAPTER` — was not chosen; it would need ~6 TS edits.)

## Contract (what videoclaw exchanges with the adapter)

One JSON object in on stdin, one clean JSON object out on stdout (all logs → stderr).
Three actions, mirroring `native-seedance.ts`:

| Action | stdin | stdout (required fields) |
|---|---|---|
| **submit** | the raw `VideoExecutionPayload` (no `action` field); all `tasks[]` in one call | `{ externalJobId }` (non-empty). Submits-and-returns; does **not** block on render. |
| **poll** | `{ action:'poll', externalJobId, outputDir, workspaceRoot, … }`, single-shot | `{ status:'pending'\|'completed'\|'failed', outputs:[{ id, kind:'video', path, sceneIndex }] }`. Downloads each completed scene to `outputDir/scene-<i>.mp4`. |
| **cancel** | `{ action:'cancel', externalJobId, … }` | `{ status:'unsupported', … }` (free queue has no cancel; no browser opened). |

Job-state lives at `<outputDir>/.vclaw-jobs/<externalJobId>.json` (per-scene Higgsfield
`job_set_id`s) so `poll` can resolve a `submit` across invocations. Higgsfield has no
server-side run id, so the adapter mints `seedance-<unixtime-ms>` and keeps its own map.

## FREE-SAFETY

The adapter reads the Higgsfield wallet before and after each submit batch and
**hard-aborts** (exits non-zero) if `credits_balance` drops — only the unlimited free
path (`use_unlim:true`, `use_free_gens:false`) is ever used. A paid spend cannot occur.

## Phase-2 — image-to-video is live

The engine now uploads a scene's first usable `referencePaths` image as the **i2v start
frame** on the free `seedance_unlimited` model (text-to-video stays on
`seedance_mini_unlimited`). Identity-locked photoreal i2v renders **free** on this route —
proven end-to-end on the *Last Call* film (15/15 scenes, keyframe-seeded, identity held).
The full capability list and the production requirements (**absolute** `referencePaths`,
**720p-max** execution profile, job-state diagnosis, transient-404 retry, stealth-session
health) live in [`engines/seedance-direct/README.md`](../../engines/seedance-direct/README.md)
— that file is the canonical contract; keep this one to the wiring reference.

## Verify (no spend, no browser)

The adapter's own self-test (`./.venv/bin/python test_higgs_vclaw_adapter.py`, 7 tests)
covers the contract. To prove the **videoclaw→adapter** wiring without a render, drive a
CANCEL through the compiled runtime (CANCEL opens no browser and spends nothing):

```bash
node -e '
import("./dist/video/execution-runtime.js").then(async (m) => {
  const env = { ...process.env, VCLAW_SEEDANCE_DIRECT_ADAPTER: "/path/to/higgs-cloak/higgs_vclaw_adapter.sh" };
  const r = await m.cancelExecutionPayload(
    { projectSlug:"wire", routeId:"seedance-direct", externalJobId:"x", outputDir:"/tmp", workspaceRoot:"/tmp" },
    { env });
  console.log(r.status); // -> "unsupported"  (videoclaw resolved + spawned + parsed the adapter)
});'
```

A live end-to-end free render (slow; occupies the single free slot) is documented in
`higgs-cloak/VCLAW_INTEGRATION.md` under "Live end-to-end test".
