Files
callstack__agent-device/.fallowrc.json
Michał Pierzchała fb1117f229 ci: ratchet against production-unused exports (#1202)
* ci: ratchet against test-only exports

Three exported-and-unit-tested-but-unreferenced-in-production incidents
this week (#1166 getNearestCommandNames, #1167 buildSettleTail, #1199
clearMetroSessionHints) — the first two were caught by fallow's dead-code
check because they had zero importers anywhere; #1199 was missed because a
test file imports the export, and fallow's default reachability graph
counts a test import as "used".

Adds a second, stricter pass reusing fallow's own --production mode
(entry.exclude test/story/dev files) via scripts/test-only-exports/check.ts:
an export alive in fallow's default graph but dead in its production graph,
with no other reference anywhere in its own file, has no production call
site — exactly the #1199 shape. Ratchets against a checked-in baseline
(scripts/test-only-exports-baseline.json, 77 entries); new findings fail
`pnpm check:test-only-exports` (wired into CI's Fallow job and
check:tooling). A `// test-seam: <reason>` comment above an export is the
escape hatch for intentional test seams.

Also extends .fallowrc.json's ignoreExports for seven daemon route handlers
(src/daemon/handlers/*.ts) that are genuinely production-reachable through
request-handler-chain.ts's `typeof import()` lazy-load pattern, which
fallow's static import graph can't trace as a named-export consumer —
without this they were false positives in the production-mode pass.

* fix: harden test-only-exports ratchet per review

Addresses the two should-fixes and all five minors from the independent
review of #1202:

- Replace the regex own-file occurrence count with an oxc-parser AST walk
  (typescript@7 ships no JS scanner API, so the review's fallback tool
  suggestion is the primary): identifiers are counted as AST nodes deduped
  by source span, so mentions in JSDoc/block comments, strings, and
  template-literal text no longer masquerade as call sites (review finding
  1, both constructed cases re-verified fixed), and a `//` inside a string
  no longer hides real usages (finding 6). Span dedupe keeps barrel
  re-exports (`export { x } from`) counting once. The sharper count
  surfaced one organic false negative on main: `selector` in
  src/commands/index.ts was previously exempted because the regex matched
  "selector" inside the './...selector-read.ts' import path string; it is
  now baselined alongside its sibling `ref` (same re-export line).
- Make the baseline shrink-only (finding 2): --update-baseline refuses new
  findings with the same wire/delete/annotate message, so the `// test-seam:`
  annotation in the reviewed source diff is the only acceptance path;
  CONTRIBUTING no longer documents baseline regeneration as an acceptance
  option and now describes baseline growth as a deliberate manual edit.
- Stale baseline entries now emit a `::warning` CI annotation (finding 3).
- Commit a re-runnable fixture test (finding 4): check.test.ts mirrors
  scripts/layering/model.test.ts, builds a synthetic package with a
  clearMetroSessionHints-shaped export (JSDoc self-mention included),
  asserts it is flagged, and asserts the annotated twin passes; wired
  before the check in pnpm check:test-only-exports.
- Mark the unreadable/unparseable-file fallbacks CONSERVATIVE: per
  CONTRIBUTING's convention (finding 5).
- Document the dynamic property access (obj[name]) blind spot in the
  script header and CONTRIBUTING (finding 7).

* fix: harden test-only export ratchet

* refactor: use native Fallow export gate

* chore: refresh production export baseline
2026-07-10 17:14:54 +02:00

134 lines
3.7 KiB
JSON

{
"$schema": "https://raw.githubusercontent.com/fallow-rs/fallow/main/schema.json",
"entry": [
"tsdown.config.ts",
"src/sdk/index.ts",
"src/sdk/io.ts",
"src/sdk/artifacts.ts",
"src/sdk/metro.ts",
"src/sdk/remote-config.ts",
"src/sdk/install-source.ts",
"src/sdk/android-adb.ts",
"src/sdk/contracts.ts",
"src/sdk/selectors.ts",
"src/sdk/finders.ts",
"src/bin.ts",
"src/client/companion-tunnel.ts",
"src/daemon.ts",
"src/utils/png-worker.ts",
"scripts/patch-xcuitest-runner-icon.ts",
"scripts/runner-request-count/run.ts",
"src/utils/update-check-entry.ts",
"test/scripts/metro-prepare-packaged-smoke.mjs",
"test/integration/*.test.ts",
"website/docs/404.mdx",
"website/rspress.config.ts"
],
"ignorePatterns": [
"examples/test-app/**",
"scripts/perf/**",
"scripts/layering/**",
"apple-runner/AgentDeviceRunner/AgentDeviceRunnerUITests.xctestplan",
"scripts/write-xcuitest-cache-metadata.mjs"
],
"ignoreDependencies": ["@theme"],
"ignoreExports": [
{
"file": "src/__tests__/test-utils/index.ts",
"exports": ["*"]
},
{
"file": "src/__tests__/test-utils/device-fixtures.ts",
"exports": ["LINUX_DEVICE", "ANDROID_TV_DEVICE", "TVOS_SIMULATOR"]
},
{
"file": "src/__tests__/test-utils/mocked-binaries.ts",
"exports": ["withMockedXcrun"]
},
{
"file": "src/daemon/app-log.ts",
"exports": ["readRecentAndroidLogcatForPackage"]
},
{
"file": "src/platforms/android/app-lifecycle.ts",
"exports": [
"parseAndroidForegroundApp",
"parseAndroidLaunchablePackages",
"parseAndroidUserInstalledPackages",
"installAndroidInstallablePath"
]
},
{
"file": "src/platforms/android/index.ts",
"exports": ["installAndroidInstallablePath"]
},
{
"file": "src/platforms/apple/core/apps.ts",
"exports": ["listSimulatorApps", "uninstallIosApp"]
},
{
"file": "src/platforms/apple/core/runner/runner-contract.ts",
"exports": ["resolveRunnerEarlyExitHint"]
},
{
"file": "src/platforms/apple/core/runner/runner-session.ts",
"exports": ["stopAllIosRunnerSessions"]
},
{
"file": "src/platforms/apple/core/runner/runner-xctestrun.ts",
"exports": [
"resolveRunnerBuildDestination",
"resolveRunnerAppBundleId",
"resolveRunnerSigningBuildSettings",
"resolveRunnerBundleBuildSettings",
"assertSafeDerivedCleanup"
]
},
{
"file": "src/cloud-webdriver.ts",
"exports": ["CLOUD_WEBDRIVER_PROVIDERS"]
},
{
"file": "src/daemon/handlers/lease.ts",
"exports": ["handleLeaseCommands"]
},
{
"file": "src/daemon/handlers/session.ts",
"exports": ["handleSessionCommands"]
},
{
"file": "src/daemon/handlers/snapshot.ts",
"exports": ["handleSnapshotCommands"]
},
{
"file": "src/daemon/handlers/react-native.ts",
"exports": ["handleReactNativeCommands"]
},
{
"file": "src/daemon/handlers/record-trace.ts",
"exports": ["handleRecordTraceCommands"]
},
{
"file": "src/daemon/handlers/find.ts",
"exports": ["handleFindCommands"]
},
{
"file": "src/daemon/handlers/interaction.ts",
"exports": ["handleInteractionCommands"]
}
],
"usedClassMembers": ["name", "listActiveLeases", "delete", "values", "elapsedMs", "isExpired"],
"rules": {
"unused-types": "off",
"duplicate-exports": "off"
},
"production": {
"dupes": true
},
"publicPackages": ["agent-device"],
"audit": {
"deadCodeBaseline": "fallow-baselines/dead-code.json",
"healthBaseline": "fallow-baselines/health.json"
}
}