fallow coverage groups the paid Runtime Coverage workflow commands:
fallow coverage setup # resumable first-run flow
fallow coverage analyze --cloud --repo owner/repo
fallow coverage upload-inventory # push a static function inventory
fallow coverage upload-source-maps # upload build source maps for bundled code
setupThe setup flow is resumable: run it once to bootstrap, follow the generated recipe, then run it again after traffic has been captured. It performs four steps:
fallow-covdocs/collect-coverage.mdfallow health --runtime-coverage <path>fallow coverage setup
fallow coverage setup --non-interactive
fallow coverage setup --yes
fallow coverage setup --yes --json
fallow coverage setup --yes --json --explain
| Flag | Description |
|---|---|
-y, --yes | Accept prompts automatically. Useful for local setup when you want fallow to continue without confirmation prompts. |
--non-interactive | Print instructions instead of prompting. Useful in CI, remote shells, or agent workflows. |
--json | Emit deterministic agent-readable setup instructions as JSON. Implies --non-interactive and does not prompt, write files, install packages, activate a license, or make network calls. See Agent-readable JSON. |
--explain | With --json, include a _meta block with field definitions, enum values, warning semantics, and this docs URL. |
fallow coverage setup inspects package.json, lockfiles, and scripts to tailor the instructions:
| Detection | Purpose |
|---|---|
| Framework | Chooses a recipe for Next.js, NestJS, Nuxt, SvelteKit, Astro, Remix, Vite browser apps, plain Node services, or a generic fallback. When both a Node-server framework (Elysia, Hono, Fastify, Express, Koa, @trpc/server) and Vite appear in the same package.json, the Node-server framework wins so the recipe targets the API rather than the bundled client. |
| Workspace topology | Walks workspaces (npm/pnpm/yarn/bun) and emits one recipe per runtime-bearing member. Library-only workspaces (no start/preview/dev script and no Node-server dependency) are skipped. The top-level runtime_targets array is the union (["node"], ["browser"], or ["node", "browser"]) across all members. |
| Package manager | Uses packageManager, lockfiles, or both to choose install commands for npm, pnpm, yarn, or bun. |
| Coverage artifact | Detects existing coverage/coverage-final.json, .nyc_output/coverage-final.json, or JSON-containing V8 directories. |
| Sidecar binary | Resolves FALLOW_COV_BIN, FALLOW_COV_BINARY_PATH, project-local shims, package-manager bin lookups, ~/.fallow/bin/fallow-cov, and PATH. |
If the project does not match a built-in framework recipe, fallow still writes a fallback docs/collect-coverage.md with a link to the public coverage docs for manual setup.
fallow coverage setup --yes --json is the agent-driven entry point. The payload is deterministic and stable across reruns; --explain adds the optional _meta keys without changing schema_version.
On a workspace monorepo (Elysia API at root + two Vite browser apps in dashboard/ and site/):
{
"schema_version": "1",
"framework_detected": "plain_node",
"package_manager": "bun",
"runtime_targets": ["node", "browser"],
"members": [
{
"name": "fallow-cloud",
"path": ".",
"framework_detected": "plain_node",
"runtime_targets": ["node"],
"files_to_edit": [{"path": "src/index.ts", "reason": "..."}],
"snippets": [{"label": "Node entrypoint", "path": "src/index.ts", "content": "..."}],
"dockerfile_snippet": "ENV FALLOW_TRANSPORT=fs\nENV FALLOW_WRITE_TO_DIR=/tmp/fallow-coverage",
"warnings": []
},
{
"name": "fallow-dashboard",
"path": "dashboard",
"framework_detected": "vite",
"runtime_targets": ["browser"],
"files_to_edit": [{"path": "dashboard/src/main.ts", "reason": "..."}],
"snippets": [{"label": "Vite browser entry", "path": "dashboard/src/main.ts", "content": "..."}],
"dockerfile_snippet": null,
"warnings": []
}
],
"config_written": null,
"commands": ["bun add @fallow-cli/beacon", "bun add -d @fallow-cli/fallow-cov"],
"files_to_edit": [{"path": "src/index.ts", "reason": "..."}],
"snippets": [{"label": "Node entrypoint", "path": "src/index.ts", "content": "..."}],
"dockerfile_snippet": "ENV FALLOW_TRANSPORT=fs\nENV FALLOW_WRITE_TO_DIR=/tmp/fallow-coverage",
"next_steps": [...],
"warnings": []
}
framework_detected uses canonical ids (nextjs, nestjs, nuxt, sveltekit, astro, remix, vite, plain_node, unknown). Workspace projects emit one members[] entry per runtime-bearing workspace, each with its own framework_detected, runtime_targets, files_to_edit, snippets, and dockerfile_snippet. Top-level fields mirror the first emitted runtime member (the root when the root itself is a runtime app, otherwise the first runtime workspace), so on a browser-rooted monorepo with a Node child agents should read each member's dockerfile_snippet rather than the top-level one. Single-app projects emit a members array of length 1 (path "."), so consumers can treat members[] uniformly.
Aggregator-only repos (workspaces whose only runtime indicator is a Turbo/Nx-style dev script delegating to children, plus build-only library packages) are not runtime-bearing and emit an empty payload: framework_detected: "unknown", runtime_targets: [], members: [], plus a warnings entry of "No runtime workspace members were detected; emitted install commands only.". Agents can branch on members.length === 0 (or framework_detected === "unknown") to detect this case and skip the snippet-application step.
The generated docs/collect-coverage.md recipe typically looks like:
1. Remove any old dump directory: rm -rf ./coverage
2. Build the app
3. Start the app with NODE_V8_COVERAGE=./coverage ...
4. Exercise the routes or jobs you care about
5. Stop the app and run: fallow coverage setup
Framework-aware script selection means fallow prefers existing build, start, or preview scripts when they exist and falls back to common framework commands otherwise.
fallow license activate --trial --email [email protected]
fallow coverage setup
# Follow docs/collect-coverage.md, then run setup again.
# With coverage present it hands off to `fallow health --runtime-coverage ./coverage`.
fallow coverage setup
analyzeUse local mode when you have a coverage artifact on disk:
fallow coverage analyze --runtime-coverage ./coverage --format json
Use cloud mode when you want the latest fallow.cloud runtime context for a repo:
FALLOW_API_KEY=fallow_live_... \
fallow coverage analyze --cloud --repo owner/repo --format json
FALLOW_API_KEY alone never selects cloud mode. You must pass --cloud, --runtime-coverage-cloud, or set FALLOW_RUNTIME_COVERAGE_SOURCE=cloud.
| Flag | Description |
|---|---|
--runtime-coverage <PATH> | Local V8 directory, V8 JSON file, or Istanbul coverage map. Mutually exclusive with cloud mode. |
--cloud, --runtime-coverage-cloud | Explicitly fetch cloud runtime facts from /v1/coverage/:repo/runtime-context. |
--repo <OWNER/REPO> | Repository to fetch in cloud mode. Falls back to $FALLOW_REPO, then parsed origin. Slashes are URL-encoded as one route segment. |
--api-key <KEY> | Fallow cloud bearer token. Falls back to $FALLOW_API_KEY, but only after explicit cloud opt-in. |
--api-endpoint <URL> | Override the API base URL. Useful for staging and on-prem deployments. |
--coverage-period <DAYS> | Cloud observation window, 1 through 90 days. Default: 30. |
--project-id <ID> | Optional monorepo/project discriminator. |
--environment <NAME> | Optional cloud environment filter. |
--commit-sha <SHA> | Optional cloud commit filter. |
--top <N> | Show only the top N runtime findings, hot paths, blast-radius entries, and importance entries. Truncation happens before rendering, so it propagates to JSON, human, and cloud-merge output equally. |
--blast-radius | Show the first-class blast-radius section in human output. JSON always includes runtime_coverage.blast_radius whenever runtime coverage analysis runs. |
--importance | Show the first-class importance section in human output. JSON always includes runtime_coverage.importance whenever runtime coverage analysis runs. |
--production | Run analyze in production mode, matching fallow health --production. Filters out test files and dev-only code paths before merging runtime data. |
--min-invocations-hot <N> | Hot-path classification threshold. Functions invoked at least N times during the captured window are classified as hot. Default: 100. Mirrors the same flag on fallow health --runtime-coverage. |
--min-observation-volume <N> | Minimum total trace volume before high-confidence safe_to_delete / review_required verdicts. Below this the sidecar caps confidence at medium. Default: 5000. |
--low-traffic-threshold <RATIO> | Fraction of total trace count below which an invoked function is classified low_traffic rather than active. Decimal form (0.001 = 0.1%). Default: 0.001. |
--explain | With --format json, attach a top-level _meta block with field definitions, enum values (data_source, test_coverage, v8_tracking, action_type, etc.), warning-code documentation, and the docs URL. |
Cloud analysis emits the same runtime_coverage JSON block as local mode. Its summary includes data_source: "cloud", last_received_at, and capture_quality derived from the pulled runtime window. Cloud functions that cannot be matched to the local AST/static index are omitted from findings and reported through a cloud_functions_unmatched warning.
Each finding's actions[].type uses the canonical kebab-case vocabulary: delete-cold-code is emitted on verdict=safe_to_delete, review-runtime on verdict=review_required. The sidecar may emit additional protocol-specific identifiers, so consumers should treat unknown values as forward-compat extensions rather than schema violations.
If fallow-cov is missing, setup tells you exactly what it checked and suggests the correct install command for your package manager, for example:
pnpm add -D @fallow-cli/fallow-cov
You can also bypass auto-discovery by setting either explicit-override env var to a binary path. Resolution order is FALLOW_COV_BIN first, then FALLOW_COV_BINARY_PATH, then project-local auto-discovery. Both fail hard with a clear error if the configured path does not exist (they never silently fall through to the next step). FALLOW_COV_BINARY_PATH exists for air-gapped enterprise installs, Linux distro-packaged sidecars, and Docker multi-user setups where ~/.fallow/bin is not writable.
Every sidecar spawn runs an Ed25519 signature check against the compiled-in public key. The sidecar binary must ship with an adjacent <binary>.sig file. A missing, wrong-length, or invalid signature fails hard with exit code 4 rather than executing an unverified binary. There is no warn-and-run mode and no opt-out env var. If verification fails, reinstall the signed distribution with the install command above.
upload-inventoryfallow coverage upload-inventory walks every JS/TS source in the project and POSTs a static function inventory (one row per declaration, expression, arrow, or method) to fallow cloud. The server computes inventory − runtime-seen = untracked for the matching git SHA, which lights up the dashboard's Untracked filter.
fallow coverage upload-inventory \
--api-key $FALLOW_API_KEY \
--project-id acme/web
| Flag | Default |
|---|---|
--api-key | $FALLOW_API_KEY |
--api-endpoint | $FALLOW_API_URL or https://api.fallow.cloud |
--project-id | $GITHUB_REPOSITORY, then $CI_PROJECT_PATH, then git remote get-url origin parsed to owner/repo |
--git-sha | git rev-parse HEAD |
| Flag | Description |
|---|---|
--api-key <KEY> | Fallow cloud bearer token. Prefer $FALLOW_API_KEY on shared CI runners: passing secrets on the command line may be visible to other processes via ps and can leak into shell history or process audit logs. |
--api-endpoint <URL> | Override the base URL. Useful for staging and on-prem deployments. |
--project-id <OWNER/REPO> | Project identifier, owner/repo form. |
--git-sha <SHA> | Commit SHA this inventory is keyed to. Max 64 chars; [A-Za-z0-9._-] only. |
--allow-dirty | Silence the warning when the working tree has uncommitted changes. |
--exclude-paths <GLOB> | Additional globs to skip, applied after the configured fallow ignore rules. Repeatable. |
--path-prefix <PREFIX> | Prefix prepended to every emitted filePath so the static inventory matches the path shape the runtime beacon reports. Required for containerized deployments: the beacon reports V8's absolute path inside the container (e.g. /app/src/foo.ts) while the walker emits repo-relative paths (src/foo.ts) by default. Must start with / and use POSIX separators. See Path prefix for common values. |
--with-callers | Also upload importer edges (which modules import each declaration) alongside the inventory, so the dashboard can surface caller reach for untracked code. The inventory rows also carry per-function complexity and per-file churn. |
--dry-run | Print what would be uploaded and exit. No network call. |
--ignore-upload-errors | Treat upload failures as warnings (exit 0). Validation errors still fail hard. |
Inventory entries use names that match oxc-coverage-instrument byte-for-byte so the runtime join succeeds. Precedence:
export default).id (function foo() {}, named function expression).(anonymous_N) where N is a file-scoped monotonic counter.TypeScript declaration files (*.d.ts, *.d.mts, *.d.cts, *.d.tsx) and overload signatures with no body are intentionally skipped: they have no runtime footprint, so including them would pollute the dashboard with permanently untracked entries.
The runtime beacon reports V8's filePath as it sees it at runtime. In a container, that is the path inside the image (e.g. /app/src/foo.ts when the Dockerfile sets WORKDIR /app). The static inventory walker, running in CI against the checkout, emits repo-relative paths (src/foo.ts) by default. The server joins runtime and inventory on the exact (filePath, functionName) pair; if the two sides don't share a prefix shape, the Untracked filter never lights up and the dashboard surfaces a mismatched state.
The fix: pass --path-prefix matching your deployed WORKDIR:
fallow coverage upload-inventory --path-prefix /app
Common values by deployment target:
| Target | Prefix |
|---|---|
Docker image with WORKDIR /app (most Node services) | /app |
| Cloud Run / Buildpacks | /workspace |
| Older official Node images | /usr/src/app |
| AWS Lambda | /var/task |
| GitHub Actions default checkout | /home/runner/work/<repo>/<repo> |
After upload, the server samples up to 100 recent runtime rows and reports the overlap with the inventory. The CLI prints a yellow warning if overlap < 50% with an example mismatch, and the dashboard renders a configuration-needed state on the repo detail page until the prefix is corrected.
| Code | Meaning | When it fires |
|---|---|---|
0 | ok | Upload succeeded, or --dry-run, or --ignore-upload-errors downgraded a transient failure. |
7 | network | DNS, TLS, connect, or transport failure. |
10 | validation | Missing API key, unresolvable project-id, zero functions in walk. |
11 | payload too large | Inventory exceeds the server's 200,000-function cap. Scope with --exclude-paths. |
12 | auth rejected | 401 / 403 from the server. Rotate the token or widen its scope. |
13 | server error | 5xx after retries, or other non-2xx status. |
- name: Upload function inventory to fallow cloud
run: |
npx fallow coverage upload-inventory
env:
FALLOW_API_KEY: ${{ secrets.FALLOW_API_KEY }}
fallow coverage upload-inventory (dry run)
project-id: acme/web
git-sha: a1b2c3d4e5f6...
functions: 14,280
endpoint: https://api.fallow.cloud/v1/coverage/acme/web/inventory
first 5 of 14,280 entries:
src/index.ts:1 bootstrap
src/index.ts:12 (anonymous_1)
src/user.ts:3 createUser
src/user.ts:18 default
src/user.ts:42 (anonymous_5)
... and 14,275 more
upload-source-mapsfallow coverage upload-source-maps scans a build output directory for source maps and POSTs each map to fallow cloud under the current repo and git SHA. The production beacon reports coverage against deployed bundles; uploaded source maps let the cloud resolver remap those bundle positions back to original source files.
FALLOW_API_KEY=fallow_live_... \
fallow coverage upload-source-maps --dir dist --repo my-app --git-sha "$GITHUB_SHA"
| Flag | Default | Description |
|---|---|---|
--dir <PATH> | dist | Directory scanned recursively. Relative paths resolve from the project root. |
--include <GLOB> | **/*.map | Include glob, matched relative to --dir. |
--exclude <GLOB> | **/node_modules/** | Exclude glob, repeatable. |
--repo <NAME> | package.json repository.url, then git remote get-url origin parsed to owner/repo | Repo identifier used in /v1/coverage/:repo/source-maps. Must match the beacon's projectId (and the --project-id passed to upload-inventory); pass --repo <bare-name> explicitly if your beacon reports a bare name. |
--git-sha <SHA> | $GITHUB_SHA, $CI_COMMIT_SHA, $COMMIT_SHA, then git rev-parse HEAD | Commit SHA the beacon will report for the deployed build. Must be 7-40 hex chars. |
--endpoint <URL> | $FALLOW_API_URL or https://api.fallow.cloud | Override for staging or enterprise deployments. |
--strip-path <BOOL> | true | Upload only the basename as fileName, e.g. dist/assets/app.js.map -> app.js.map. Set --strip-path=false when runtime coverage reports bundle paths with directories, e.g. assets/app.js. |
--dry-run | false | Print what would upload without reading FALLOW_API_KEY or making network calls. |
--concurrency <N> | 4 | Parallel upload fanout. |
--fail-fast | false | Stop on the first failed map instead of uploading the rest and reporting a partial-failure summary. |
The API key comes only from FALLOW_API_KEY; there is intentionally no --api-key flag, so secrets do not land in shell history or process lists. Set FALLOW_CA_BUNDLE=/path/to/bundle.pem when CI needs a custom PEM trust bundle for fallow cloud. The bundle replaces the default WebPKI roots, so private-CA environments should pass a complete bundle containing public roots plus the private CA. Relative bundle paths resolve from the process working directory.
Uploads retry network failures, HTTP 429, and HTTP 502/503/504 up to three attempts. HTTP 429 honors Retry-After delta seconds and HTTP-date values, capped at 60 seconds. Setup or transport failures that prevent every map from uploading exit 7; mixed per-map failures still exit 1.
- name: Build
run: npm run build
- name: Upload source maps to fallow
run: npx fallow coverage upload-source-maps --dir dist --git-sha "$GITHUB_SHA"
env:
FALLOW_API_KEY: ${{ secrets.FALLOW_API_KEY }}| Exit | Meaning |
|---|---|
0 | All maps uploaded, or dry-run completed. |
1 | Partial or full upload failure, including invalid JSON maps, auth rejection, rate limits, server errors, or exhausted network retries. |
2 | Bad invocation: missing FALLOW_API_KEY, unresolvable repo/SHA, missing directory, or zero maps found. |