* 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
* test: migrate test app to expo dev client
* docs: align test app device targeting
* docs: clarify dev client setup tradeoffs
* docs: remove stale sdk reference
* chore(fallow): fit config to repo profile so baselines stay near-empty
- Raise health thresholds in .fallowrc.json to the smallest values that
pass on a clean tree (maxCyclomatic 58, maxCognitive 77, maxCrap 591)
instead of grandfathering ~180 findings in fallow-baselines/health.json.
- Raise duplicates.minTokens to 66, the smallest value covering the four
tolerated clone groups (largest is 65 tokens).
- Regenerate baselines: health.json shrinks from ~18.6 KB of grandfathered
finding counts to refactoring-target metadata only; dead-code.json is
empty.
- Upgrade fallow 2.52.0 -> 2.91.0: 2.87.0 made ignorePatterns silence the
"examples/test-app is not declared as a workspace" warning, which 2.52.0
emitted regardless of config.
- Remove the unused ensureAdb export (and its now-unused imports) from
src/platforms/android/adb.ts; it is not re-exported by any public entry
and has no references anywhere in the repo.
- Document local (pnpm fallow) vs CI (fallow audit) usage in
CONTRIBUTING.md.
https://claude.ai/code/session_01LXZXzxi55sZ11DSyqWyBA2
* chore(fallow): keep default thresholds, gate locally via diff-based audit
Revise the previous commit after review: pinning global thresholds at the
repo's historical maxima (cyclomatic 58, cognitive 77, CRAP 591, minTokens
66) weakened the gate for brand-new code and left zero headroom on the
worst existing functions. Restore the original design — fallow default
thresholds with legacy findings grandfathered per-file in
fallow-baselines/health.json — and fix the local-DX problem at the script
level instead:
- .fallowrc.json: drop the health/duplicates overrides so fallow defaults
(cyclomatic 20, cognitive 15, CRAP 30, minTokens 50) apply to new code.
- fallow-baselines/health.json: regenerate at default thresholds under
fallow 2.91 (201 grandfathered findings across 108 files); dead-code
baseline stays empty.
- package.json: `pnpm fallow` now runs `fallow audit --base origin/main`,
the same diff-based gate CI uses, so it passes on a clean tree. The old
full-tree summary moves to `pnpm fallow:all` (expected to report legacy
findings). `check:fallow` is unchanged (CI passes an explicit --base).
- CONTRIBUTING.md: correct the fallow docs accordingly.
Verified: clean tree passes; a new unused export fails the audit; a new
cyclomatic-25 function fails the audit; +1 branch growth in an already-
grandfathered function (classifyBootFailure) is absorbed by the baseline.
https://claude.ai/code/session_01LXZXzxi55sZ11DSyqWyBA2
---------
Co-authored-by: Claude <noreply@anthropic.com>