These instructions are for AI assistants working in this project.
Always open @/openspec/AGENTS.md when the request:
- Mentions planning or proposals (words like proposal, spec, change, plan)
- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work
- Sounds ambiguous and you need the authoritative spec before coding
Use @/openspec/AGENTS.md to learn:
- How to create and apply change proposals
- Spec format and conventions
- Project structure and guidelines
Keep this managed block so 'openspec update' can refresh the instructions.
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
W3C WebCodecs API for Node.js using FFmpeg. Browser-compatible video/audio encoding/decoding with MP4 muxing/demuxing extensions.
CRITICAL: Check docs/specs/ before modifying C++ or codec behavior. W3C WebCodecs spec is the source of truth.
- Safety over velocity — C++ segfault crashes entire Node process
- RAII mandatory — Use
src/ffmpeg_raii.hwrappers (AVFramePtr,AVPacketPtr), never rawav_*_alloc/free - Thread paranoia —
AVCodecContextis NOT thread-safe; isolate from main thread inAsyncWorker - Spec compliance — Throw exact DOMException types per W3C spec (use
error_builder.h)
- C++ (
src/*.cc,src/*.h):/dev-cpp(Google C++ Style Guide) - TypeScript (
lib/*.ts,test/*.ts):/dev-ts(Google TypeScript Style Guide) - FFmpeg C++ review: Custom
ffmpeg-cpp-sentinelagent (automatically available)
ALWAYS use npm run scripts. Never use npx, tsx, or direct tool invocations.
# Build
npm run build # Full (native + TS), auto-runs after Write/Edit
npm run build:native # C++ only
npm run build:debug # Debug symbols
# Test
npm run check # Lint + test (CI equivalent)
npm test # All tests (unit + guardrails)
npm run test:unit # Fast iteration
# Lint
npm run lint # All (cpp + ts + types + md)
npm run lint:cpp # cpplint only
npm run lint:ts # biome only
# Output filters (spec reporter)
npm run test:failures # ✖ only
npm run test:summary # ℹ stats onlyPostToolUse hook: Write|Edit → auto-format + build TypeScript (see output for errors)
Two-layer design:
lib/(TypeScript) → W3C spec compliance, state validation, EventTargetsrc/(C++17 N-API) → FFmpeg operations, async workers
Critical files:
src/ffmpeg_raii.h— RAII wrappers (prevents leaks)src/error_builder.h— DOMException builderlib/resource-manager.ts— W3C reclamation (10s inactive timeout)lib/binding.ts— Platform addon loader
Pattern: TS handles state/validation, C++ handles FFmpeg ops. Clean separation.
- Version: 5.0+ required (
common.henforces) - Return values: Always check, use
FFmpegErrorString()for messages - Packet/frame ratio: NEVER assume 1:1 relationship
- Timebase: Handle conversions explicitly (check W3C spec)
H.264: avc1.42001e (Baseline), avc1.4d001e (Main), avc1.64001e (High)
H.265: hvc1.*, hev1.*
VP9: vp09.00.10.08
AV1: av01.0.04M.08
AAC: mp4a.40.2
STOP before editing code. Triage FIRST:
- Linker warnings ("built for macOS-X but linking with Y") = ABI issue, NOT code bug
- Crash in trivial code (constructor, allocation) = problem is ELSEWHERE
- Crash "moves around" when you edit = memory corruption or ABI mismatch
# Diagnose linked libraries
otool -L ./build/Release/*.node # macOS
ldd ./build/Release/*.node # LinuxLoop detection: Edited same file 3+ times for same crash → STOP. Root cause is elsewhere.
Fix: Adjust binding.gyp, rebuild deps. Do NOT modify source as first response.
MANDATORY requirements for ALL code changes. Reviewers MUST enforce, contributors MUST follow.
- ALWAYS update JSDoc in source files — TypeScript definitions auto-generated by
tsc; JSDoc becomes.d.tsdocumentation - NEVER skip type tests — Add type-level assertions in
test/types/*.test-d.tsviatsdto prevent regression - Accept both simple and complex forms — Use TypeScript overloads for progressive complexity (see
VideoFrameconstructor in lib/video-frame.ts:73-79) - Match FFmpeg naming conventions — Public APIs expand abbreviations (
createConfigurationnotcreateCfg), internal code mirrors FFmpeg (av_*) - Include
@exampleblocks in JSDoc — Show real usage for all public APIs, document defaults explicitly - Safety over convenience — Validate inputs at TypeScript layer before passing to C++; prevent segfaults proactively
- ALWAYS require tests for new features — NO exceptions; use
test/golden/for integration,test/unit/for isolated behavior - ALWAYS test error paths — Deliberately trigger errors (invalid configs, closed states, null buffers) - see test/golden/video-encoder.test.ts:31-46
- Edge case coverage MANDATORY — Test boundary conditions (zero-size frames, maximum values, null/undefined)
- W3C compliance tests separate — Use
test/golden/w3c-*.test.tspattern for spec behavior verification - Run
npm run checkbefore review — Lint + tests must pass; NO "works on my machine" exceptions
- Reduce duplication proactively — Extract common patterns to utilities (
lib/transfer.ts,lib/is.ts) during review - Move logic to appropriate layer — Validation in TypeScript (
lib/), FFmpeg ops in C++ (src/) - Use RAII wrappers MANDATORY —
src/ffmpeg_raii.h(AVFramePtr,AVPacketPtr) prevents leaks; rawav_*_alloc/freeFORBIDDEN - Fix related bugs during review — If you discover a bug while reviewing, file issue or fix immediately; NO "out of scope" excuses
- JSDoc for ALL public APIs — Include:
@examplewith working code snippets (not pseudocode)- FFmpeg/W3C upstream references where applicable
- Default values and range constraints
- Relationship to W3C spec sections (link to
docs/specs/)
- Comment FFmpeg quirks — Document non-obvious behavior (packet/frame ratio, timebase conversions, hardware acceleration caveats)
- Explain departures from spec — If extending W3C API, mark clearly as "Node.js extension" with rationale (see Muxer/Demuxer classes)
- Verify platform-specific code — Test macOS/Linux/Windows variations (ABI issues, library paths in lib/binding.ts)
- Check upstream FFmpeg version — Ensure features exist in FFmpeg 5.0+ (minimum supported version per common.h)
- NEVER assume upstream behavior — Validate FFmpeg return codes, check for null pointers, handle version differences
- NO secrets in test fixtures — Reject commits with
.env,credentials.json, API keys intest/fixtures/ - Validate ALL external inputs — Check buffer sizes, codec strings, frame dimensions at TS layer BEFORE C++
- Copyright/licensing verification — Ensure new dependencies have MIT-compatible licenses, check package.json
- FFmpeg error handling MANDATORY — ALWAYS check
av_*return values, useFFmpegErrorString()for messages (src/error_builder.h)
- Leverage FFmpeg optimizations — Use hardware acceleration flags (
hwaccel), let FFmpeg choose SIMD paths - AVOID premature TypeScript optimization — Keep TS layer readable; C++ layer is where performance matters
- Benchmark regressions — Run
test/stress/benchmarks for changes affecting encode/decode hot paths
- Use
@deprecatedJSDoc tag — Mark deprecated APIs with replacement instructions - Provide migration timeline — Specify version when deprecated feature will be removed
- Keep deprecated code functional — Don't break existing users until major version bump
- Example:
/** * @deprecated Use getReclaimableCodecs() instead. Will be removed in v1.0.0 */
Before approving ANY pull request, verify:
- JSDoc updated in source files (
lib/*.ts) with@exampleblocks - Type tests added/updated (
test/types/*.test-d.tsviatsd) - Integration tests in
test/golden/OR unit tests intest/unit/ - Error path tests included (invalid inputs, closed states)
- W3C spec compliance verified (check
docs/specs/) - FFmpeg version compatibility checked (5.0+ minimum)
- Platform-specific code tested (macOS/Linux at minimum)
- No duplication (refactored to utilities if needed)
- Security: no secrets, all inputs validated at TS layer
-
npm run checkpasses (lint + all tests) - Build passes (
npm run buildproduces valid.d.tsand native addon) - No raw FFmpeg allocations (
av_*_alloc/free); RAII wrappers only
Test GitHub Actions locally:
act -l # List jobs
act push -j build-native --container-architecture linux/amd64 -W .github/workflows/ci.yml