# How narrow-react-prop-types Differentiates Real Prop Callers from Storybook and Test References

> Learn how narrow-react-prop-types separates live code from Storybook and test references. Ensure prop type narrowing comes from production callers only, enforcing stricter contracts for your React components.

- Repository: [HumanLayer/skills](https://github.com/humanlayer/skills)
- Tags: internals
- Published: 2026-09-13

---

**`narrow-react-prop-types` classifies every component import and usage as either "live code paths" or "support code" by analyzing file paths for test and Storybook patterns, ensuring that prop type narrowing is derived exclusively from production callers while forcing stories and tests to adapt to the stricter contract.**

The `narrow-react-prop-types` skill in the `humanlayer/skills` repository solves the problem of prop type bloat by distinguishing between real production usage and mock-only references. By filtering out Storybook stories, test files, and mock fixtures from its analysis, the tool ensures that TypeScript prop types reflect actual runtime requirements rather than speculative test scenarios.

## Classification System: Live Code vs. Support Code

### Live Code Paths (Production)

**Live code paths** represent code that executes in production environments, including app routes, wired components, providers, and shared components. According to the workflow defined in [`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), these files are identified as existing outside known test directories and not matching patterns such as `*.stories.*`, `*.test.*`, `__tests__`, `__mocks__`, `fixtures`, or any path under a `storybook` folder. The skill explicitly instructs agents to classify call sites based on whether they represent live code paths or support scaffolding.

### Support Code (Stories, Tests, and Mocks)

**Support code** encompasses Storybook stories, unit tests, mock fixtures, demo harnesses, and visual examples. These files are detected by matching typical naming conventions including `*.stories.*`, `*.test.*`, and `*.spec.*`, or by residing in folders named `__mocks__`, `__tests__`, `stories`, or `fixtures`. These patterns are excluded from the "live" set when the agent builds the usage graph to prevent test utilities from diluting the production contract.

## The Four-Step Differentiation Process

The **differentiation process** follows a strict workflow as implemented in the agent configuration:

1. **Search for all imports and usages** of the target component—including its exported prop type and any child primitives—across the entire repository.

2. **Group each call site** based on its file path. Files matching support patterns are marked as *support code*, while all others are classified as *live* call sites.

3. **Analyze only live call sites** to derive the *real* prop contract. Optional versus required decisions are based on whether **every** live call supplies a prop (required) or at least one live call omits it for a meaningful runtime state (optional).

4. **Force stories and tests to adapt** to the narrowed contract rather than allowing them to dictate type definitions.

## Implementation Details and Pattern Matching

### File Path Pattern Detection

The skill uses regular expression patterns to filter support files before type analysis. The detection logic operates on file paths to categorize usage into production or scaffolding:

```typescript
const supportPatterns = [
  /\.stories\./,
  /\.test\./,
  /\.spec\./,
  /__tests__/,
  /__mocks__/,
  /fixtures/,
  /storybook/,
];

function isSupportFile(filePath: string): boolean {
  return supportPatterns.some(p => p.test(filePath));
}

// Example usage in the analysis pipeline:
const usages = findAllUsages(componentName); // returns [{filePath, node}, ...]
const liveUsages = usages.filter(u => !isSupportFile(u.filePath));

```

### Prop Analysis Logic

After isolating live usages, the skill constructs a prop usage map to determine requirement status based on actual production callers:

```typescript
const propUsage = new Map<string, { required: boolean; optional: boolean }>();

liveUsages.forEach(u => {
  const suppliedProps = getSuppliedProps(u.node);
  allPropNames.forEach(p => {
    if (suppliedProps.has(p)) {
      propUsage.get(p)!.required = true;
    } else {
      propUsage.get(p)!.optional = true;
    }
  });
});

// Build the narrowed type:
type LiveProps = {
  propA: string;          // Required: present in every live call
  propB?: number | null;  // Optional: omitted in at least one live call
};

```

### Impact on Storybook Stories

Once the narrowed contract is established from production code, support files must conform. For example, if a Storybook story previously provided `undefined` for an optional handler, the narrowed type might require a valid function:

```tsx
// Before narrowing (invalid after analysis)
export const Default = () => <MyComponent onSelect={undefined} />;

// After narrowing (required prop enforcement)
export const Default = () => (
  <MyComponent onSelect={(id) => console.log('selected', id)} />
);

```

## Key Source Files in the Repository

The implementation spans several files in the `humanlayer/skills` 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)): Defines the complete workflow and classification rules for live versus support code (lines 38-46).
- **[`agent-narrow-component-props.yml`](https://github.com/humanlayer/skills/blob/main/agent-narrow-component-props.yml)**: Configures the CI agent that executes the differentiation logic and enforces live-code-path analysis.
- **[`response-template.md`](https://github.com/humanlayer/skills/blob/main/response-template.md)**: Specifies the format for reporting narrowed-type results back to developers.
- **[`narrow-component-props-memory.md`](https://github.com/humanlayer/skills/blob/main/narrow-component-props-memory.md)**: Stores the live call site information that persists between analysis runs.

These resources collectively ensure that prop type tightening is driven solely by real production callers rather than test mocks or Storybook fixtures.

## Summary

- **narrow-react-prop-types** categorizes every component usage as either live production code or support scaffolding based on file path analysis.
- File path patterns like `*.stories.*`, `*.test.*`, and `__mocks__` automatically disqualify code from the live usage set.
- Only live call sites determine whether props are required or optional in the narrowed TypeScript contract.
- Storybook stories and tests are forced to adapt to the production-derived types, ensuring type safety reflects real runtime behavior rather than speculative test scenarios.

## Frequently Asked Questions

### What file patterns does narrow-react-prop-types recognize as test or mock files?

The skill recognizes files matching `*.stories.*`, `*.test.*`, `*.spec.*`, or containing `__tests__`, `__mocks__`, `fixtures`, or `storybook` in their paths. Any file matching these patterns is classified as support code and excluded from the live usage analysis that determines prop requirements.

### Can Storybook stories influence the final prop types?

No. According to the [`SKILL.md`](https://github.com/humanlayer/skills/blob/main/SKILL.md) workflow, stories and tests are explicitly treated as mock-only scaffolding and ignored for type-derivation purposes. The narrowed prop contract is derived solely from live code paths, and support files must be updated to satisfy the stricter production requirements.

### How does the skill determine if a prop should be required or optional?

The skill aggregates all live call sites and checks whether every production caller supplies a specific prop. If all live instances provide the prop, it becomes required in the narrowed type. If at least one live call omits it for a valid runtime state, it remains optional or nullable in the final contract.

### Where is the classification logic documented in the repository?

The classification rules are documented in [`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), specifically in the workflow section that instructs agents to "Search for all imports/usages of the component... Classify call sites by whether they are live code paths or support code."