# How narrow-react-prop-types Identifies Suspect React Components: A Rule-Based Detection System

> Discover how narrow-react-prop-types identifies suspect React components using a two-stage workflow. Detect components with prop-type definitions that never occur in production.

- Repository: [HumanLayer/skills](https://github.com/humanlayer/skills)
- Tags: deep-dive
- Published: 2026-09-07

---

**`narrow-react-prop-types` uses a two-stage detection workflow that combines heuristic signal scanning with live-usage verification to flag React components whose prop-type definitions describe states that never occur in production.**

The `narrow-react-prop-types` skill in the [humanlayer/skills](https://github.com/humanlayer/skills) repository is a code-analysis tool designed to tighten TypeScript prop definitions by eliminating phantom states. Its identification process, defined in [`SKILL.md`](https://github.com/humanlayer/skills/blob/main/SKILL.md), separates over-wide components from genuinely flexible ones through systematic signal detection followed by real-world usage validation.

## Signal Detection: Spotting the Symptoms of Over-Wide Props

The first stage scans for **heuristic indicators** that a component's interface has been widened for convenience rather than necessity. These signals are enumerated in the ["Identify the suspect component"](https://github.com/humanlayer/skills/blob/main/plugins/narrow-react-prop-types/skills/narrow-react-prop-types/SKILL.md#L30-L34) section of the skill specification.

### Key Signals That Flag Suspect Components

- **Large prop interfaces with many optional fields** — interfaces where most properties use `?` markers
- **Optional callback invocations** — patterns like `onSelect?.(...)` or `onArchive?.(...)` suggesting handlers that may never fire
- **Fallback operators guarding always-provided values** — `items ?? []`, `count ?? 0`, or `&&` short-circuits where live code actually always supplies the value
- **UI affordances rendered unconditionally** — buttons or controls shown even when their callbacks are optional
- **Demo-oriented prop shapes** — props named `defaultFoo`, alternate handler shapes, or toggles unused by production UI

These patterns suggest the component was designed defensively for testing, Storybook, or future extensibility rather than current production needs.

## Live-Usage Verification: Separating Real Code from Support Code

Once a component triggers the signal detection, the skill executes its second stage: **whole-codebase analysis** to classify every import and usage. This process is documented in the ["Find every live usage"](https://github.com/humanlayer/skills/blob/main/plugins/narrow-react-prop-types/skills/narrow-react-prop-types/SKILL.md#L40-L55) and ["Derive the real types from the live code paths"](https://github.com/humanlayer/skills/blob/main/plugins/narrow-react-prop-types/skills/narrow-react-prop-types/SKILL.md#L40-L55) workflow sections.

The skill categorizes call sites into two buckets:

| Category | Examples | Used for type derivation? |
|----------|----------|---------------------------|
| **Live code paths** | Application routes, wired components, providers, hooks, production exports | **Yes** — these determine real prop requirements |
| **Support code** | Storybook stories, tests, fixtures, mocks, demo harnesses | **No** — these are filtered out to avoid widening types |

Only live code paths inform the final narrowed prop definitions. Support code that passes extra props is identified as the source of interface bloat.

## Implementation Sketch: How the Detection Logic Works

While the actual skill runs inside the Code-Layer CI agent, its logic mirrors this pattern:

```typescript
// Signal detection: scanning for heuristic indicators
function findSuspectComponents(pkgRoot: string) {
  const tsFiles = globSync('**/*.tsx', { cwd: pkgRoot, absolute: true });
  const suspects: string[] = [];

  for (const file of tsFiles) {
    const src = readFileSync(file, 'utf-8');

    // Heuristic: many optional fields in Props interface
    const hasWideProps = /interface\s+\w+Props\s*{[^}]*\?/s.test(src);

    // Heuristic: optional callback chaining
    const hasOptionalCallbacks = /\w+?\.\w+?\?\(/s.test(src);

    // Heuristic: fallback operators with likely unnecessary defaults
    const hasFallbacks = /\?\?\s+/s.test(src);

    if (hasWideProps || hasOptionalCallbacks || hasFallbacks) {
      suspects.push(file);
    }
  }
  return suspects;
}

```

For each suspect, the skill then verifies actual usage:

```typescript
// Live-usage verification: filtering to production code only
function getLiveCallSites(componentPath: string, repoRoot: string) {
  const allImports = globSync('**/*.tsx', { cwd: repoRoot, absolute: true })
    .filter(f => readFileSync(f, 'utf-8').includes(componentPath));

  // Exclude test and storybook files
  return allImports.filter(p => !/(\.test|\.stories)\.tsx$/.test(p));
}

```

## Key Files in the Detection Architecture

The `narrow-react-prop-types` skill is organized across four primary files in the repository:

- **[`SKILL.md`](https://github.com/humanlayer/skills/blob/main/SKILL.md)** — [[`plugins/narrow-react-prop-types/skills/narrow-react-prop-types/SKILL.md`](https://github.com/humanlayer/skills/blob/main/plugins/narrow-react-prop-types/skills/narrow-react-prop-types/SKILL.md)](https://github.com/humanlayer/skills/blob/main/plugins/narrow-react-prop-types/skills/narrow-react-prop-types/SKILL.md): The human-readable specification defining the complete detection workflow, signal list, and live-usage classification rules.

- **[`agent-narrow-component-props.yml`](https://github.com/humanlayer/skills/blob/main/agent-narrow-component-props.yml)** — [[`plugins/narrow-react-prop-types/skills/narrow-react-prop-types/references/agent-narrow-component-props.yml`](https://github.com/humanlayer/skills/blob/main/plugins/narrow-react-prop-types/skills/narrow-react-prop-types/references/agent-narrow-component-props.yml)](https://github.com/humanlayer/skills/blob/main/plugins/narrow-react-prop-types/skills/narrow-react-prop-types/references/agent-narrow-component-props.yml): CI-agent configuration that automates skill execution.

- **[`narrow-component-props-memory.md`](https://github.com/humanlayer/skills/blob/main/narrow-component-props-memory.md)** — [[`plugins/narrow-react-prop-types/skills/narrow-react-prop-types/references/narrow-component-props-memory.md`](https://github.com/humanlayer/skills/blob/main/plugins/narrow-react-prop-types/skills/narrow-react-prop-types/references/narrow-component-props-memory.md)](https://github.com/humanlayer/skills/blob/main/plugins/narrow-react-prop-types/skills/narrow-react-prop-types/references/narrow-component-props-memory.md): Persistent memory file for storing intermediate findings across agent runs.

- **[`response-template.md`](https://github.com/humanlayer/skills/blob/main/response-template.md)** — [[`plugins/narrow-react-prop-types/skills/narrow-react-prop-types/references/response-template.md`](https://github.com/humanlayer/skills/blob/main/plugins/narrow-react-prop-types/skills/narrow-react-prop-types/references/response-template.md)](https://github.com/humanlayer/skills/blob/main/plugins/narrow-react-prop-types/skills/narrow-react-prop-types/references/response-template.md): Template formatting the final PR body after prop types are narrowed.

## Summary

- `narrow-react-prop-types` identifies suspect components through **heuristic signal detection** followed by **live-usage verification**.
- Signals include optional-heavy interfaces, guarded callbacks, fallback operators, and demo-oriented prop shapes.
- Live code paths are distinguished from support code (tests, stories) to determine which props are truly required.
- The detection workflow is fully specified in [`SKILL.md`](https://github.com/humanlayer/skills/blob/main/SKILL.md) and executed via the CI-agent configuration.

## Frequently Asked Questions

### What makes a React component "suspect" in this system?

A component becomes suspect when it exhibits patterns suggesting its prop interface was widened beyond production needs—specifically large optional interfaces, defensive fallbacks, optional callbacks, or props that only appear in tests and stories. These signals are catalogued in the skill's [`SKILL.md`](https://github.com/humanlayer/skills/blob/main/SKILL.md) under the "Identify the suspect component" section.

### How does the skill avoid narrowing props that are actually used in production?

The skill performs **live-usage verification** by scanning every import of the flagged component and filtering out support code—files matching patterns like `*.test.tsx` or `*.stories.tsx`. Only imports from application routes, providers, and production exports inform the final type derivation, ensuring real usage patterns are preserved.

### Can this detection run automatically in CI pipelines?

Yes. The skill includes [`agent-narrow-component-props.yml`](https://github.com/humanlayer/skills/blob/main/agent-narrow-component-props.yml), a CI-agent configuration file that enables automated execution. The agent maintains state across runs using [`narrow-component-props-memory.md`](https://github.com/humanlayer/skills/blob/main/narrow-component-props-memory.md) and outputs formatted PR descriptions via [`response-template.md`](https://github.com/humanlayer/skills/blob/main/response-template.md), making the narrowing workflow fully automatable.