Install
$ agentstack add skill-vmvenkatesh78-engineering-skills-engineering-standards Open-source listing — not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Dangerous shell/eval execution.
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ● Dynamic code execution Used
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Frontend Engineering Standards
These standards apply to every line of code written, reviewed, or refactored. No exceptions for prototypes, demos, or "quick fixes." Code that violates these rules does not ship.
FOUR-STATE RULE: EVERY ASYNC OPERATION
AI generates code that works when everything goes right. Production code handles when things go wrong.
Every fetch, API call, or async operation MUST handle four states:
- Loading. Skeleton or spinner visible before data arrives
- Error. Meaningful error message, retry mechanism, or fallback
- Empty. Explicit empty state with action to resolve it
- Success. The data renders correctly
If code only handles success, it is not production-ready. Reject it on review.
// WRONG, happy path only
const { data } = await fetchUsers();
return ;
// RIGHT: all four states
function UserList() {
const { data, error, isLoading } = useUsers();
if (isLoading) return ;
if (error) return ;
if (!data?.length) return } />;
return ;
}
TYPESCRIPT
Strict Mode: Non-Negotiable
Every project: strict: true, noUncheckedIndexedAccess: true, exactOptionalPropertyTypes: true.
Forbidden Patterns
any, never. Useunknownand narrow with type guards.astype assertions, only with proof (instanceof check, discriminated union).@ts-ignore/@ts-expect-errorwithout a comment explaining why.- Non-null assertion (
!), use optional chaining or explicit guards. enum, useas constobjects instead. Enums have runtime cost and quirky behavior.
// WRONG
const value = data as UserResponse;
const name = user!.name;
enum Status { Active, Inactive }
// RIGHT
function isUserResponse(data: unknown): data is UserResponse {
return typeof data === 'object' && data !== null && 'id' in data;
}
const name = user?.name ?? 'Unknown';
const Status = { Active: 'active', Inactive: 'inactive' } as const;
Required Patterns
- All function parameters:
Readonly<>when the function should not mutate them. - All component props: defined as interfaces, not inline types.
- All re-exports:
export type { X }for type-only exports (isolatedModules compliance). - Generic components: explicit constraint + default: ``.
- Discriminated unions for state machines, not boolean flags.
// WRONG, boolean flags
interface State {
isLoading: boolean;
isError: boolean;
data: User[] | null;
error: Error | null;
}
// RIGHT, discriminated union
type State =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'error'; error: Error }
| { status: 'success'; data: User[] };
REACT PATTERNS
Component Architecture
- One component per file. Component name matches filename.
- Compound components over prop-heavy monoliths (>8 props = split).
- Hooks extract reusable logic. Components own behavior, styles own presentation.
forwardRefcomponents MUST havedisplayName.- No inline styles except truly dynamic values (computed positions, API-driven colors).
- No class components. Functional only.
Hooks Rules
useEffectwith async work: ALWAYS include cleanup (AbortController or stale flag).useEffectdependency arrays: exhaustive or line-level eslint-disable with rationale.useMemo/useCallback: only when profiling shows measurable gain or reference stability matters.useState: prefer single state object for related state that updates together.useRef: for DOM refs, abort controllers, values that don't trigger re-renders.- Never call
setStateduring render, derive withuseMemo. - Never nest hooks inside conditions or loops.
// WRONG, no cleanup, race condition
useEffect(() => {
fetchData().then(setData);
}, [id]);
// RIGHT: abort controller, cleanup, error handling
useEffect(() => {
const controller = new AbortController();
async function load() {
try {
const result = await fetchData(id, { signal: controller.signal });
setData(result);
} catch (err) {
if (!controller.signal.aborted) {
setError(err instanceof Error ? err : new Error('Unknown error'));
}
}
}
load();
return () => controller.abort();
}, [id]);
State Management
- Co-locate state where it's used. Don't hoist to global store unless 2+ unrelated components need it.
- No derived state in store, compute with selectors or
useMemo. - Context: use for dependency injection (theme, auth), not for frequently changing state.
- Async operations: dispatch started/success/failure actions. Never leave error paths unhandled.
Props
- Boolean props:
is/has/allow/enableprefix:isLoading,hasError. - Event handlers:
handleprefix for component handlers,onprefix for prop callbacks:handleClick,onClick. - No boolean parameters in functions, use options objects instead.
// WRONG
function createUser(name: string, isAdmin: boolean, sendEmail: boolean) {}
// RIGHT
function createUser(name: string, options: { isAdmin?: boolean; sendEmail?: boolean }) {}
CSS ARCHITECTURE
Design System CSS
- Token-driven: every color, spacing, font size, radius, and shadow comes from CSS custom properties.
- No hardcoded hex values, pixel values, or font names in component CSS.
- Option B pattern: base selector declares structure with
--_intermediate variables, variant selectors swap values. - Data attribute selectors:
[data-variant="primary"], not class-based variants. - Plain CSS files with data attributes, no CSS-in-JS, no CSS Modules, no Tailwind in component library code.
CSS Rules
- No
!importantunless overriding third-party styles (with comment). - No element-type selectors deeper than 2 levels.
&:focus-visiblenot&:focusfor keyboard-only indicators.flex-shrink: 0on icons and fixed-width elements in flex containers.- No
:global()selectors targeting library internals. - Transitions: only on
color,background-color,border-color,opacity,transform,box-shadow. Never onpadding,margin,width,height,border-radius. - Duration: 100-150ms for hover/active, 200ms for modals. No bounce/spring/elastic easing.
Production UI Standard Compliance
All UI output must comply with the production-ui-standard skill. Key checks:
- Primary accent: blue (#2563EB light, #3B82F6 dark). Not indigo, not purple.
- Grays: Slate family (cool-toned). Not neutral gray.
- Shadows: use Slate-900 rgba, not pure black. Cards on white = border, no shadow.
- Border radius: 6px buttons/inputs, 8px cards, 12px modals. Never above 12px on rectangles.
- No gradients on buttons. No glassmorphism. No colored card backgrounds.
ACCESSIBILITY: NON-NEGOTIABLE
- Every interactive element: keyboard-reachable and operable.
- Every icon-only button:
aria-label. - Every image:
alttext (emptyalt=""for decorative). tabIndex> 0: NEVER.- Color as sole indicator: NEVER. Always pair with text, icon, or pattern.
role="button"on non-button elements: MUST also havetabIndex={0}andonKeyDown.- Focus indicators: 2px solid outline, 2px offset, visible on EVERY interactive element.
- Contrast: 4.5:1 normal text, 3:1 large text minimum. Target 7:1 where possible.
- Touch targets: 44x44px minimum on mobile.
- Reduced motion:
@media (prefers-reduced-motion: reduce)disables all animations. - Screen reader: all form inputs have associated labels, all regions have landmarks.
- Focus traps: only in modals/dialogs. Must be escapable with Escape key.
ERROR HANDLING
- No empty
catchblocks: at minimum log with context or re-throw. - API errors: distinguish 4xx (user error) from 5xx (server error) in UI messaging.
- Form validation: show inline errors, not just toasts.
- Network errors: provide retry mechanism or clear feedback.
Promise.all: handle errors, one rejection must not crash the entire batch. UsePromise.allSettledwhen partial success is acceptable.- Every
switchstatement:defaultcase required. - Null/undefined from API: never assume shape matches TypeScript interface at runtime. Validate.
// WRONG: assumes API shape
const userName = response.data.user.name;
// RIGHT, defensive
const userName = response.data?.user?.name ?? 'Unknown';
PERFORMANCE
- No O(n²) operations on arrays that could exceed 100 items.
- No
new RegExp()inside render, extract to module scope oruseMemo. keyprops: stable identifiers, never array indices (unless static, never reordered).- Lazy load routes and heavy components:
React.lazy+Suspense. - No layout thrashing: batch DOM reads before DOM writes.
- Images: use
loading="lazy"for below-fold images, specifywidth/heightto prevent CLS. - Bundle: code-split at route level minimum. Monitor with build:size script.
- Memoization: profile first. Don't
useMemoeverything, measure before optimizing.
SECURITY
- No API keys, tokens, or secrets in source code.
- No PII in console.log statements.
- All user inputs sanitized before rendering.
dangerouslySetInnerHTML: ONLY with DOMPurify sanitization. Prefer avoiding entirely.eval(),new Function(),innerHTMLwith user content: FORBIDDEN.- File uploads: validate extensions AND file size. Use allowlist, not blocklist.
- CSS custom properties from user input: block
url(),@import,expression(). - Third-party scripts: load with
integrityattribute (SRI) when available. - CORS: never use
Access-Control-Allow-Origin: *in production.
TESTING
Structure
- Test file: adjacent to source:
Component.test.tsx. - Test names: describe behavior, not implementation:
"shows error when API returns 400"not"calls setError". - No test depends on another test's state.
- Mock at boundaries (API calls, browser APIs), not internal functions.
Coverage
- Every component: at least one test for render, one for interaction, one for error state.
- Every hook: test the contract (inputs → outputs), not the implementation.
- Every async operation: test both success and error paths.
- Every form: test validation, submission, and error display.
- Edge cases: empty data, null values, extremely long strings, special characters.
Patterns
// Test the four states
describe('UserList', () => {
it('shows skeleton while loading', () => { /* ... */ });
it('shows error message when fetch fails', () => { /* ... */ });
it('shows empty state when no users exist', () => { /* ... */ });
it('renders user rows on success', () => { /* ... */ });
});
- Accessibility tests: use
jest-axeorvitest-axefor automated a11y checks. - No
data-testidas the primary query strategy, prefergetByRole,getByLabelText,getByText. - Test user behavior, not implementation details. Click buttons, type in inputs, verify visible output.
NAMING CONVENTIONS
Files
- Components:
PascalCase:FormUpload.tsx - Hooks:
camelCasewithuseprefix:useClickOutside.ts - Utils:
camelCase:formatDate.utils.ts - Tests:
*.test.tsx/*.test.ts - Styles:
*.css(design system),*.module.scss(app-level) - Constants:
SCREAMING_SNAKE_CASEin files,camelCasefilenames
Code
- Components:
PascalCase - Hooks:
useCamelCase - Functions/variables:
camelCase - Constants:
SCREAMING_SNAKE_CASE - Types/Interfaces:
PascalCase:interface ButtonProps - Generic type params: single uppercase letter or descriptive:
T,TElement - Boolean:
is/has/should/canprefix - Event handlers:
handleprefix (component),onprefix (prop)
GIT CONVENTIONS
- Commit format:
type(scope): description - Types:
feat,fix,refactor,style,perf,docs,test,chore,build,ci,revert - One logical change per commit.
- Remove all
console.logbefore committing. - No commented-out code, git has history.
- PR: one concern per PR. Don't mix features with refactors.
FILE SIZE LIMITS
- Components: 0 | Breaks natural tab order | Restructure DOM order |
| Color as sole indicator | Accessibility failure | Add text/icon/pattern | | dangerouslySetInnerHTML no sanitize | XSS vulnerability | DOMPurify or avoid | | Fetch with only success handling | Crashes on error/empty | Four-state pattern | | Boolean function parameters | Unreadable call sites | Options object | | Nested ternaries | Unreadable | if/else or early return | | Prop drilling >3 levels | Maintenance burden | Context or composition |
REVIEW CHECKLIST: RUN ON EVERY REVIEW
Pre-flight (before reading code)
- Does the PR have a clear title in commit format?
- Is it one logical change or a mixed bag?
- Are there any
console.logstatements?
Code Quality
- TypeScript strict, no
any, no unsafeas, no! - Every async operation handles four states (loading, error, empty, success)
- Every
useEffectwith async work has cleanup - Every error path has user-facing feedback
- No magic numbers, no hardcoded strings, no hardcoded URLs
- Functions under 40 lines, components under 300 lines
- Nesting under 3 levels
Accessibility
- Every interactive element keyboard-reachable
- Every icon-only button has
aria-label - Focus indicators visible on all interactive elements
- Color contrast meets 4.5:1 minimum
- No color-only state indicators
Security
- No secrets, tokens, or PII in source
- User input sanitized before rendering
- No
eval,innerHTML, or unsanitizeddangerouslySetInnerHTML
CSS
- No hardcoded colors, spacing, or font values
- Token-driven: all values from CSS custom properties
- No
!importantwithout justification - Focus-visible, not focus
- Compliant with production UI standard
Testing
- Tests exist for success, error, and edge cases
- Test names describe behavior
- No tests depend on other tests' state
- Queries use
getByRole/getByLabelText, notdata-testid
Performance
- No O(n²) on growing arrays
- No RegExp construction in render
- Stable keys on list items
- Lazy loading for routes and heavy components
CONFIDENCE THRESHOLD
When reviewing code, score each finding 0-100. Only surface findings at 85+ confidence. This prevents noise and focuses review on real issues.
Severity levels:
- Critical. Security vulnerability, data loss, crash. Must fix before merge.
- High. Logic bug, accessibility failure, missing error handling. Should fix.
- Medium. Performance issue, architectural concern, maintainability. Consider fixing.
- Low. Naming, style preference, minor improvement. Optional.
Verdict:
- Ready to Merge. No Critical or High issues
- Needs Attention. Medium issues found
- Needs Work. Critical or High issues found
THE DURABILITY LADDER FOR TYPE-LEVEL CONSTRAINTS
The rules above tell you what TypeScript patterns to use. This section tells you the meta-principle those patterns serve: every constraint that protects correctness should be encoded at the highest tier the type system supports, and only fall to a lower tier when the higher tier genuinely cannot carry it.
The ladder, ranked by ability to survive consumer turnover, codebase growth, and AI-agent-assisted refactoring:
- Type-level constraint. The compiler refuses to mix incompatible categories of data. Discriminated unions for state. Branded types for validated values. Exhaustiveness checks via
assertNeveron every switch over a union. Enforced at build time; no consumer can bypass without writing a cast that is visible in code review. - Runtime invariant. A contract check at a module boundary that fires when the assumption is violated, with a stack trace pointing at the violation site. Used when the constraint is mechanically checkable but only at runtime.
- Test as executing context. A regression test named for the constraint it protects, which fails in CI if someone changes the code wi
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: vmvenkatesh78
- Source: vmvenkatesh78/engineering-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.