| name | code-organization |
| description | Enforce frontend code organization โ bulletproof-react ownership-first placement, the allowed-folder law (a folder holds only the kind of thing its name promises), kebab-case file naming with PascalCase / UI* identifiers, type-only files, classes-only / no-static-or-free-functions outside React, path-alias imports, semantic (no-data-testid) selectors, and hardcoded value / string extraction to `.env` and i18n. Use when placing, moving, renaming, or splitting components, hooks, repositories, stores, or modules; reviewing structure; fixing dependency-cruiser, ESLint, type-file, or duplication CI failures that stem from structural or naming issues; or extracting hardcoded configuration. |
Code Organization Skill
Profile keys consumed
architecture.source_root
architecture.modules
architecture.component_prefix
architecture.path_aliases
framework.ui
framework.state
framework.di
framework.i18n
make.ci
make.lint_deps
make.lint_eslint
make.lint_tsc
make.lint_dup
make.lint_metrics
make.format
make.test_unit_client
make.test_integration
quality.depcruise_violations
quality.eslint_errors
quality.eslint_warnings
quality.tsc_errors
quality.jscpd_clones
quality.metrics_enforced
Core Principle
Place code by ownership first, then by kind โ a feature owns its code,
and a folder holds ONLY the kind of thing its name promises.
This is the fundamental, stack-generic law of code organization in a
bulletproof-react layout. It holds in every feature-module under
architecture.source_root, regardless of the UI framework or state
manager. Prefer the existing module and feature structure over new
top-level abstractions. See
DIRECTORY-STRUCTURE.md for the full
folder-by-folder placement reference.
Context (Input)
- Creating new components / hooks / repositories / stores and determining
the correct directory
- Moving files to their proper owner
- Reviewing code for organizational compliance
- Fixing organizational issues from code reviews (the
code-quality-reviewer agent flags these)
- Ensuring file and identifier names match their responsibility
- Refactoring code structure (moving, renaming, splitting components,
hooks, or modules)
- Fixing CI failures that stem from structural / naming issues
(dependency-cruiser, ESLint, type-file, duplication gates)
- Extracting hardcoded config values to
.env and user-facing
strings to feature i18n
Task (Function)
Enforce strict code organization principles: bulletproof-react
ownership-first placement, the allowed-folder law, kebab-case file naming
with PascalCase / architecture.component_prefix identifiers, type-only
files, classes-only / no-static outside React, path-alias imports, and
semantic (no-data-testid) selectors.
Source paths follow the profile: feature code lives under
<architecture.source_root>/modules/<module>/features/<feature>/ where
<module> is one of architecture.modules; reusable UI lives under
<architecture.source_root>/components/<component_prefix lowercased>-*.
See DIRECTORY-STRUCTURE.md for the
complete tree.
Directory Type Classification
Folders MUST hold only the kind of thing their name promises. The
allowed-folder sets are enforced by dependency-cruiser
(module-allowed-folders, feature-allowed-folders,
tests-top-level-allowed-folders); anything else fails make.lint_deps.
Top-level <architecture.source_root>/
| Directory | Contains ONLY | Example |
|---|
modules/ | Feature-modules (domain-owned workflows) | modules/catalog/ |
components/ | Reusable UI building blocks (<component_prefix>*) | components/ui-button/ |
services/ | Singleton infrastructure services | services/https-client/ |
stores/ | Shared cross-module state | stores/session-store.ts |
config/ | DI container config, tokens, app config | config/dependency-injection-config.ts |
routes/ | Route definitions | routes/app-routes.tsx |
providers/ | React context providers | providers/theme-provider.tsx |
utils/ | Shared cross-cutting utilities (specific names) | utils/format-currency.ts |
Module root โ module-allowed-folders
A module directory may contain ONLY: config, features, hooks,
lib, store, types, utils. Shared module code goes in lib/ or
utils/ โ never helpers/.
Feature root โ feature-allowed-folders
A feature directory may contain ONLY: assets, components, hooks,
i18n, repositories, routes, types, utils. Data access is
repositories/; the store stays at module level. Do not add api/,
helpers/, or a feature-level store/.
| Folder | Contains ONLY | Example |
|---|
components/ | React components (feature UI) | components/form-section/inert-box.tsx |
hooks/ | Hooks (use-* or index) | hooks/use-login-switcher.ts |
repositories/ | Data-access classes (consumed via index only) | repositories/login-repository.ts |
routes/ | Route components / definitions | routes/sign-in-route.tsx |
types/ | Type-only files | types/login-form/fields.ts |
utils/ | Instance-method helper classes (specific names) | utils/normalize-auth-error.ts |
i18n/ | en.json / uk.json | i18n/en.json |
assets/ | Static assets | assets/logo.svg |
Directory Creation Guardrails
- NEVER create new directories autonomously โ the allowed-folder sets
above are closed lists enforced by
dependency-cruiser. When in doubt,
use an existing folder.
- Do not invent ad-hoc catch-all directories. The following are
explicitly forbidden (and rejected by
make.lint_deps):
helpers/, misc/, common/, manager/ โ vague catch-all
anti-patterns; use lib/ or utils/ with a specific file name
api/ at feature level โ data access is repositories/
- feature-level
store/ โ module state stays at module level
- any folder with uppercase letters (
no-uppercase-paths)
- A genuinely new shared folder needs demand from multiple callers
and explicit user approval โ never add one speculatively.
Frontend Naming Patterns
Files and folders are lowercase kebab-case; exported identifiers carry
their role in the name.
All path-shape rules are enforced by dependency-cruiser
(no-uppercase-paths, src-module-name-kebab-case,
src-feature-name-kebab-case, feature-hooks-file-convention). Violations
fail make.lint_deps.
By Kind and Type
| Kind | File pattern | Identifier | Example |
|---|
| Reusable UI component | <prefix-lower>-<kebab>/index.tsx | PascalCase <component_prefix><Name> | ui-button/index.tsx โ UIButton |
| Feature component | <kebab>.tsx | PascalCase | inert-box.tsx โ InertBox |
| Hook | use-<kebab>.ts(x) or index.* | use<Name> | use-login-switcher.ts โ useLoginSwitcher |
| Repository | <kebab>-repository.ts | class <Name>Repository | login-repository.ts โ LoginRepository |
| Store / reactive var | <kebab>.ts | class + module singleton | auth-var.ts โ AuthStateVar |
| Selectors | <kebab>-selectors.ts | class singleton | auth-store-selectors.ts |
| Helper / util | <specific-kebab>.ts | class with instance methods | normalize-auth-error.ts |
| Type-only file | types/<area>/<kebab>.ts | interface / type only | types/ui-form/submit-controls.ts |
| i18n | i18n/{en,uk}.json | โ | i18n/en.json |
| Test | mirror subject + environment | โ | normalize-auth-error.test.ts |
- Module names under
modules/ are lowercase kebab-case (back-to-main,
not BackToMain); feature names under features/ are too (auth, not
Auth).
- Reusable component identifiers are prefixed with
architecture.component_prefix (default UI), but their files and
folders stay kebab-case (ui-button/index.tsx exporting UIButton).
- Hook identifiers are
useSomething; hook files are use-something.ts.
- Helpers get specific names, never
helper, misc, or utils
catch-alls.
Directory Structure by Layer
<architecture.source_root>/
โโโ modules/
โ โโโ <module>/ โ one per architecture.modules entry
โ โโโ config/
โ โโโ features/
โ โ โโโ <feature>/
โ โ โโโ assets/
โ โ โโโ components/ โ feature UI
โ โ โโโ hooks/ โ use-* / index
โ โ โโโ i18n/ โ en.json, uk.json
โ โ โโโ repositories/ โ data access (via index)
โ โ โโโ routes/
โ โ โโโ types/ โ type-only files
โ โ โโโ utils/
โ โโโ hooks/
โ โโโ lib/
โ โโโ store/ โ module state + composition root
โ โโโ types/
โ โโโ utils/
โโโ components/ โ reusable <component_prefix>* UI
โโโ services/ โ singleton services (HTTP client, โฆ)
โโโ stores/
โโโ config/ โ DI config + tokens
โโโ routes/
โโโ providers/
โโโ utils/
<module> is each entry of architecture.modules; <feature> is any
feature directory under that module.
Verification Checklist
When creating or reviewing a file, verify:
- โ
Kind Matches Folder (a folder holds ONLY its declared kind)
- Example: a hook in
hooks/use-*.ts, NOT in components/
- โ
Name Follows the Pattern for its kind (kebab file, role-carrying
identifier)
- โ
Path Is Lowercase Kebab-Case (no uppercase anywhere under
architecture.source_root or tests/)
- โ
Name Reflects Actual Responsibility
- โ
Correct Owner (feature-owned by default; shared only on real
multi-caller demand)
- โ
Reusable Components Have NO Feature Dependency (no feature i18n,
store, repository types, or route state in
components/)
- โ
Identifiers Are Specific (not vague)
- โ
normalizeAuthError, useLoginSwitcher (specific)
- โ
helper, doStuff, useData (too vague)
- โ
Type-Only Files Hold Only Types (no runtime); logic files hold
no
interface / type
- โ
No "Helper" / "Util" Catch-Alls (extract specific
responsibilities into named classes)
- โ
No Free Functions /
static in non-React .ts (instance
methods on a class instead)
- โ
New Folders Are From the Allowed Set, not agent-invented โ must
be explicitly approved by the user
Frontend Best Practices
Required Patterns
- โ
Classes + instance methods for non-React
src/**/*.ts โ no
static members, no standalone (free) functions. Behavioral
collaborators are @injectable() classes; render-path primitives are
instance classes exported as a module singleton. Enforced by an ESLint
no-restricted-syntax gate (make.lint_eslint). Exempt: *.tsx
components and use-* hooks.
- โ
Type-only files โ types live in dedicated
types/ folders;
logic files never declare interface / type. Enforced by ESLint plus
dependency-cruiser (type-files-imported-as-type-only,
type-files-no-runtime-imports).
- โ
Container-free render path โ only the composition root touches
the
framework.di container, behind a dynamic import(), so the paint
path stays light.
- โ
Semantic selectors โ source ships no
data-testid; locate by
role / label / text, falling back to a stable id only when no semantic
query fits. Enforced by ESLint (make.lint_eslint).
- โ
Path aliases โ
@/ for cross-folder imports and a feature-scoped
alias for deep within-feature imports (architecture.path_aliases);
avoid ../../../ chains.
- โ
framework.ui styling โ Material UI v7 + Emotion (sx,
styled(), theme); reusable components prefixed
architecture.component_prefix.
- โ
DRY โ no copy-paste clones at or above the jscpd threshold
(
make.lint_dup); extract a shared component, hook, or constant rather
than duplicating (see the complexity-management skill for the split).
Anti-Patterns (Forbidden)
These are enforced by dependency-cruiser and the ESLint
no-restricted-syntax gates under architecture.source_root; treat the
rules as binding.
- โ
helpers/ / misc/ / common/ / manager/ folders or
helper / util / misc file names โ extract specific
responsibilities into named classes in lib/ or utils/
- โ Free functions or
static members in non-React .ts โ use
instance methods on an injectable class (or module-singleton class)
- โ
data-testid anywhere under architecture.source_root โ expose a
stable id or query by role / label / text
- โ
interface / type in logic files, and runtime (const /
function / class) in type-only files
- โ Deep relative import chains (
../../../X) across folders โ use an
alias from architecture.path_aliases
- โ Business / user-facing strings hardcoded in JSX โ move them to
feature
i18n/{en,uk}.json
- โ Feature UI importing the module store, repositories, or
services/ directly โ route through a hook
(no-components-to-store, no-components-to-repositories,
no-feature-ui-to-services)
- โ Repositories reached by anything but their
index, or features
calling the HTTP client directly (no-repository-internal-imports,
no-feature-direct-http-client)
- โ Cross-module / cross-feature imports that bypass public exports
(
no-cross-module-imports, no-cross-feature-imports)
- โ Uppercase letters in any path under
architecture.source_root or
tests/ (no-uppercase-paths)
- โ Suppression directives (
eslint-disable, @ts-ignore,
// dependency-cruiser-disable) โ fix the structure instead
Factory & DI Pattern (Maintainability & Flexibility)
Avoid hardcoded new ClassName(...) of collaborators in production
code โ resolve them through the DI container (framework.di).
Behavioral Collaborators โ DI
Services, repositories, mappers, factories, and error handlers are
@injectable() classes registered against a token and resolved by token
or constructor @inject:
const repo = new LoginRepository(new HttpsClient(), new AuthErrorFactory());
const repo = container.resolve<LoginRepository>(TOKENS.LoginRepository);
Resolution by token is the preferred way to obtain a collaborator
outside its own construction โ substitution in tests happens by swapping
the container binding, not by monkey-patching a module.
Render-Path Primitives โ Module Singleton
State primitives that must stay container-free (for the
framework.di-free paint path / Lighthouse budget) are instance classes
exported as a module singleton, so call sites stay X.method(...) without
pulling DI into the chunk:
class AuthStateVar {
public get(): AuthState {
}
public set(partial: Partial<AuthState>): void {
}
}
const authStateVar = new AuthStateVar();
export default authStateVar;
When DI Registration Is REQUIRED
- Collaborators with injected dependencies (HTTP client, config, factories)
- Anything that must be substituted in tests (mockability)
- Objects constructed from external input (responses, DTOs)
- A token with multiple implementations (register the chosen one)
When Direct Construction Is ACCEPTABLE
- Inside factory classes and the composition root (that is their purpose)
- In test code (Faker builders favor simplicity over abstraction)
- For framework-required patterns (
throw new ValidationError(...))
- Render-path module singletons (
export default new X())
Factory Naming Convention
<ObjectName>Factory creates <ObjectName> instances
- Location: same feature area as the object being created
- Example:
AuthErrorFactory creates auth error objects
Type Safety: Typed Files Over Loose Shapes
Known shapes get a named interface / type in a dedicated type-only
file โ not an inline anonymous object repeated across files, and never
any.
Loose shapes lose IDE support and let drift through. Name the shape once,
import it via import type.
Loose vs Typed Comparison
| Pattern | Bad (loose) | Good (typed) |
|---|
| Component props | inline { label: string; onChange: ... } | LoginFormFields interface in types/ |
| API value | const data: any = await repo.fetch() | const data: LoginResponse = await repo.fetch() |
| Known map | Record<string, unknown> for a fixed shape | a named interface |
| Re-declared DTO | the same shape typed in two files | one type re-exported from types/ |
Benefits of Named Types
- โ
IDE autocompletion and safe refactoring
- โ
make.lint_tsc catches drift (quality.tsc_errors = 0)
- โ
Self-documenting boundaries
- โ
Single source of truth (import the type, don't restate it)
When Loose Shapes ARE Acceptable
- Genuinely dynamic, one-shot internal data inside a single function
- Framework integration points that hand back
unknown
- A
toJSON-style serialization output
Cross-Cutting Concerns Pattern
Hooks own data and side effects; components render props and
translated UI. Feature components reach state and data through hooks โ
never by importing the store, repositories, or services directly.
Anti-Pattern: Container Logic Inside a Component
export function ProfileForm() {
const repo = container.resolve<ProfileRepository>(TOKENS.ProfileRepository);
const [state, setState] = useState();
return <form>{/* โฆ */}</form>;
}
Correct Pattern: Hook Owns Data, Component Renders
<source_root>/modules/<module>/features/<feature>/
hooks/use-profile-form.ts โ data + side effects + state
components/profile-form.tsx โ renders props + translated UI
components/profile-form-fields.tsx โ renders fields
export function ProfileForm() {
const { fields, onSubmit, isSubmitting } = useProfileForm();
return <ProfileFormFields fields={fields} onSubmit={onSubmit} busy={isSubmitting} />;
}
The hook is the only place that resolves repositories or store actions;
the component stays a pure render of translated props.
Common Issues and Fixes
Issue 1: File in the Wrong Folder
โ WRONG:
<source_root>/components/login-card.tsx
โ
CORRECT:
<source_root>/modules/<module>/features/<feature>/components/login-card.tsx
mv <source_root>/components/login-card.tsx \
<source_root>/modules/<module>/features/<feature>/components/login-card.tsx
Issue 2: Uppercase or Non-Kebab Path
โ WRONG: src/modules/BackToMain/Features/Auth/
โ
CORRECT: src/modules/back-to-main/features/auth/
no-uppercase-paths, src-module-name-kebab-case, and
src-feature-name-kebab-case fail make.lint_deps on uppercase or
PascalCase path segments.
Issue 3: Vague Util Names
โ WRONG:
<source_root>/modules/<module>/features/<feature>/utils/utils.ts
โ
CORRECT:
<source_root>/modules/<module>/features/<feature>/utils/normalize-auth-error.ts
<source_root>/modules/<module>/features/<feature>/utils/map-validation-errors.ts
Name utilities by the transformation or decision they perform.
Issue 4: Helper Class / Free Function
export function validateEmail() {}
export function formatName() {}
class EmailValidator {
public validate(value: string): boolean {
}
}
Issue 5: Type Declared in a Logic File
interface SubmitControlsProps { busy: boolean }
export interface SubmitControlsProps { busy: boolean }
import type { SubmitControlsProps } from '@/.../types/ui-form/submit-controls';
Decision Tree: Where Does It Belong?
What does the file DO?
โโ Renders UI reused across modules? โ components/<prefix-lower>-*/
โโ Renders feature UI? โ modules/<module>/features/<feature>/components/
โโ Owns data / side effects / state for components? โ a hook (hooks/use-*.ts)
โโ Talks to the API? โ features/<feature>/repositories/ (consumed via index)
โโ Holds module state? โ modules/<module>/store/ (+ composition root)
โโ Declares a type only? โ a types/ folder (import type)
โโ Pure transformation / decision? โ instance method in utils/ or lib/
โโ A user-facing string? โ i18n/{en,uk}.json
โโ Something else? โ confirm against the allowed-folder set before adding a folder!
Verification Commands
grep -rn "data-testid" <architecture.source_root>/
grep -rEn "/(helpers|misc|common|manager)/" <architecture.source_root>/
grep -rEn "\.\./\.\./\.\." <architecture.source_root>/
If a make.* entry is null, the capability is absent โ note the skip
and fall back to the remaining checks instead of inventing a target.
DI Registration: No Redundant Wiring
Applies when framework.di is set (e.g. tsyringe; adapt the mechanics for
another container โ the rule itself is generic).
Register each @injectable() class once against a token, then resolve
it by token or constructor @inject. Do not register the same class
twice, and do not reach for the container outside the composition root.
Rule
A class with constructor-injectable dependencies is registered once in
the DI config against a token in the tokens file, then obtained via
container.resolve<Type>(TOKENS.X) or @inject(TOKENS.X). The render-path
store stays container-free: only the composition root imports the
container, behind a dynamic import() (dependency-cruiser
no-di-config-import-outside-composition-root).
When Explicit Registration IS Required
- A token has multiple implementations (register the chosen one)
- A class needs a constructor argument the container cannot resolve
(a config value, an env-backed primitive) โ register it with an explicit
value
- The implementation lives outside the autowired source
When Wiring is REDUNDANT (don't add it)
- A class with only constructor-injectable deps and a single token โ
register once, resolve by token; do not also
new it at call sites
- A render-path primitive that is already a module singleton โ do not also
bind it into the container
Verification
Constraints (Never Do This)
NEVER:
- Place a file in the wrong folder (violates "a folder holds ONLY its
declared kind")
- Let a reusable
components/ UI depend on feature i18n, store,
repositories, or route state
- Use vague identifiers (
helper, doStuff, useData โ be specific!)
- Create
helpers/ / misc/ / common/ / manager/ catch-alls
- Use uppercase in any path under
architecture.source_root or tests/
- Declare
interface / type in a logic file, or runtime in a type file
- Use
data-testid in source โ expose a stable id or query by
role / label / text
- Use free functions or
static members in non-React .ts
- Import the module store, repositories, or
services/ from feature UI โ
route through a hook
- Reach a repository by anything but its
index, or call the HTTP client
from a feature directly
- Use deep relative chains across folders instead of a path alias
- Hardcode user-facing strings in JSX instead of feature i18n
- Add suppression directives (
eslint-disable, @ts-ignore,
dependency-cruiser disables) instead of fixing structure
- Lower any
quality.* threshold or edit .dependency-cruiser.js /
eslint.config.mjs to make misplaced code pass
ALWAYS:
- Verify "a folder holds ONLY its declared kind"
- Keep code feature-owned by default; promote to shared only on real
multi-caller demand
- Use specific, role-carrying identifiers and kebab-case paths
- Keep types in dedicated type-only files, imported via
import type
- Use instance methods on classes instead of free functions /
static
- Route feature data and state through hooks
- Use
@/ and feature-scoped aliases instead of deep relative chains
- Move user-facing strings to feature i18n
- Use DI to obtain collaborators in production code
Hardcoded Configuration & Strings โ .env / i18n Extraction
Configurable values (ports, URLs, language defaults, schema versions,
feature flags) belong in .env; user-facing strings belong in feature
i18n โ not inline in code or JSX.
When to Extract to .env
Extract to .env (exposed to the client as REACT_APP_*, read through
app config and inlined at build time by framework.bundler) when the
value is an environment tunable:
- Ports / hosts: dev / prod / mock server ports and URLs
- Language defaults: main and fallback language
- Schema / contract versions: pinned external schema version
- Feature flags and timeouts: client-side toggles, request timeouts
When to Extract to i18n
Every user-facing string (framework.i18n) โ feature
i18n/{en,uk}.json, used via t('key'). Never inline a label, button
text, or message in JSX.
When NOT to Extract
Keep inline / in code when the value is:
- A design token: spacing, colors, radii โ they live in the theme
- Protocol / spec-defined: HTTP status codes, ARIA role names
- A domain invariant: validation rules that are part of the model
- A test fixture / golden text / mock sentinel (see the Faker-builders
convention)
Extraction Pattern โ Config (3-Step)
Step 1: Add the variable to .env (and .env.test)
# .env
REACT_APP_REQUEST_TIMEOUT_MS=8000
Step 2: Read it through app config (not process.env scattered in
components)
public get requestTimeoutMs(): number {
return Number(import.meta.env.REACT_APP_REQUEST_TIMEOUT_MS);
}
Step 3: Inject the config value where it is used, instead of a literal.
Extraction Pattern โ Strings (i18n)
Step 1: Add the key to i18n/en.json and i18n/uk.json.
Step 2: Replace the literal with t('feature.key').
Step 3: Re-run localization generation if your build composes module
i18n.
Verification After Extraction
Run, in order:
- Target mapped by
make.format, then make.lint_eslint โ
quality.eslint_errors / quality.eslint_warnings stay 0
- Target mapped by
make.lint_tsc โ quality.tsc_errors (= 0)
- Target mapped by
make.test_unit_client โ update mocks / props
- Target mapped by
make.ci โ full validation
CI Integration: When CI Fails
When the target mapped by make.ci fails, consult this skill if the
failure involves:
| CI Failure Indicator | Code Organization Fix |
|---|
dependency-cruiser violation | Check folder placement / allowed folders / cross-module & cross-feature imports |
ESLint no-restricted-syntax (static / free function) | Refactor to an instance method on an injectable class |
ESLint data-testid finding | Query by role / label / text; fall back to a stable id |
| Type-file ESLint / dep-cruiser violation | Move runtime out of type files; import types via import type |
| jscpd clone over threshold | Deduplicate the block (extract a component / hook / constant) |
make.lint_tsc error after a move | Update imports and aliases everywhere |
make.lint_metrics violation after a split | Extract a hook / helper (see the complexity-management skill) |
| Test failure after a file move | Move the mirrored test file too (mirror source ownership) |
Thresholds come from the profile only: quality.depcruise_violations,
quality.eslint_errors, quality.eslint_warnings, quality.tsc_errors,
and quality.jscpd_clones are fixed ceilings of 0, and
quality.metrics_enforced stays true. Ceilings are fixed and floors are
raise-only: a profile may tighten them, never relax them โ fix the
code, never the threshold.
Refactoring Checklist (Before Running CI)
When moving, renaming, or restructuring files:
architecture:
source_root: src
modules: [catalog, checkout]
component_prefix: UI
path_aliases: ["@/", "@feature/"]
framework:
ui: mui-v7
state: zustand
di: tsyringe
i18n: react-i18next
make:
ci: ci
lint_deps: lint-deps
lint_eslint: lint-eslint
lint_tsc: lint-tsc
lint_dup: lint-dup
lint_metrics: lint-metrics
format: format
test_unit_client: test-unit-client
test_integration: test-integration
quality:
depcruise_violations: 0
eslint_errors: 0
eslint_warnings: 0
tsc_errors: 0
jscpd_clones: 0
metrics_enforced: true
Related Skills
- architecture โ layering, repository / hook
boundaries, and
dependency-cruiser rule resolution; use it together
with this skill when a move crosses a layer boundary
- complexity-management โ refactoring
often requires reorganization; consult both when a file or component
exceeds the metrics gate
- code-review โ references this skill for
organization verification during PR reviews
- ci-workflow โ apply code-organization
principles when fixing CI failures that stem from structural issues
- frontend-component-development
โ component / hook authoring conventions that this placement guidance
enforces
- quality-standards โ maintains the
overall quality thresholds these gates protect
Before applying this skill, confirm the active task against
../AI-AGENT-GUIDE.md and
../SKILL-DECISION-GUIDE.md so every relevant
skill is consulted and no verdict is silently skipped.
Related Documentation
See DIRECTORY-STRUCTURE.md for the
complete folder-by-folder placement reference, file naming conventions,
and step-by-step placement guides.
Remember: Structure reflects intent. Feature-owned placement and
truthful folder names make the architecture self-documenting.