Claude-skill-registry gate-3-implementation

Gate 3 validation: Implementation quality and type safety

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/gate-3-implementation" ~/.claude/skills/majiayu000-claude-skill-registry-gate-3-implementation && rm -rf "$T"
manifest: skills/data/gate-3-implementation/SKILL.md
source content

Gate 3: Implementation

STOP AND CHECK:

# No 'any' types in signatures
grep "Promise<any>" src/*.ts
# Must return nothing

# Using generated types
grep "import.*generated" src/*.ts
# Must show imports

# ConnectorImpl extends only generated interface
grep "extends.*Connector" src/*ConnectorImpl.ts
# Should show: extends {Service}Connector (not AbstractConnector or other classes)

# Metadata uses boilerplate (ConnectionStatus.Down)
grep "ConnectionStatus.Down" src/*ConnectorImpl.ts
# MUST return match - this is the required boilerplate

# Metadata is async returning Promise
grep "async metadata(): Promise<ConnectionMetadata>" src/*ConnectorImpl.ts
# MUST return match - correct signature

# isSupported uses boilerplate (OperationSupportStatus.Maybe)
grep "OperationSupportStatus.Maybe" src/*ConnectorImpl.ts
# MUST return match - this is the required boilerplate

# isSupported parameter named correctly (_operationId with underscore)
grep "isSupported(_operationId:" src/*ConnectorImpl.ts
# MUST return match - underscore indicates unused parameter

# No customized metadata logic
grep -E "ConnectionStatus\.(Up|Connected|Ready)" src/*ConnectorImpl.ts
# Should return nothing - only Down allowed

# No customized isSupported logic
grep -E "OperationSupportStatus\.(Yes|No)" src/*ConnectorImpl.ts
# Should return nothing - only Maybe allowed

# No connection context in producer method parameters
grep -E "(apiKey|token|baseUrl|organizationId):" src/*Producer.ts
# Should return nothing - these come from client

# Mappers should prefer map() function over constructors
if [ -f "src/Mappers.ts" ]; then
  if grep -E "new (UUID|Email|URL|DateTime)\(" src/Mappers.ts > /dev/null; then
    echo "⚠️  WARNING: Mappers.ts using constructors directly"
    echo "   PREFER map() from @zerobias-org/util-hub-module-utils"
    echo "   Example: map(UUID, value) instead of new UUID(value)"
    echo "   Only use constructors if map() doesn't meet requirements"
  fi

  # Check for map() import
  if ! grep -q "import.*map.*from.*@zerobias-org/util-hub-module-utils" src/Mappers.ts; then
    echo "⚠️  WARNING: Mappers.ts missing map() import"
    echo "   Add: import { map } from '@zerobias-org/util-hub-module-utils';"
    echo "   map() handles optional values automatically and is preferred"
  fi
fi

# Check if using core profiles when applicable
# If connectionProfile.yml exists and is custom
if [ -f "connectionProfile.yml" ] && ! grep -q "\$ref.*types-core/schema" connectionProfile.yml; then
  echo "⚠️  WARNING: Custom connectionProfile.yml - verify core profile not applicable"
  echo "   Available core profiles:"
  echo "   - tokenProfile.yml (API token/key auth)"
  echo "   - oauthClientProfile.yml (OAuth client credentials)"
  echo "   - oauthTokenProfile.yml (OAuth token)"
  echo "   - basicConnection.yml (username/password or email/password)"
  echo "   For email/password auth, consider extending basicConnection.yml"
fi

# If connectionState.yml exists, validate it
if [ -f "connectionState.yml" ]; then
  # Check if using core state (recommended)
  if ! grep -q "\$ref.*types-core/schema" connectionState.yml; then
    echo "⚠️  WARNING: Custom connectionState.yml - verify core state not applicable"
    echo "   Available core states:"
    echo "   - tokenConnectionState.yml (access token + expiresIn)"
    echo "   - oauthTokenState.yml (OAuth with refresh capability)"
    echo "   All states MUST extend baseConnectionState.yml for expiresIn"
  fi

  # CRITICAL: Validate expiresIn field (required for server cronjobs)
  if ! grep -q "\$ref.*baseConnectionState\|expiresIn:" connectionState.yml; then
    echo "❌  CRITICAL ERROR: connectionState.yml missing expiresIn field"
    echo "   WHY: Server sets cronjobs based on expiresIn for automatic token refresh"
    echo "   FIX: Must extend baseConnectionState.yml OR include expiresIn property"
    echo "   UNIT: expiresIn must be in SECONDS (integer) until token expires"
  fi

  # Check for expiresAt (should be converted to expiresIn)
  if grep -q "expiresAt:" connectionState.yml; then
    echo "⚠️  WARNING: connectionState.yml has expiresAt field"
    echo "   REQUIRED: Calculate expiresIn (seconds) from expiresAt and DROP expiresAt"
    echo "   WHY: Server needs expiresIn (seconds) for cronjobs, not timestamps"
    echo "   FIX: In connect(), calculate: expiresIn = Math.floor((expiresAt - now) / 1000)"
  fi

  # If has refreshToken, MUST have expiresIn
  if grep -q "refreshToken:" connectionState.yml && ! grep -q "\$ref.*baseConnectionState\|expiresIn:" connectionState.yml; then
    echo "❌  CRITICAL ERROR: Has refreshToken but missing expiresIn"
    echo "   WHY: Cannot schedule token refresh without knowing when it expires"
    echo "   FIX: Must extend baseConnectionState.yml to include expiresIn"
  fi
fi

PROCEED ONLY IF:

  • ✅ NO
    any
    types in method signatures
  • ✅ Using generated types from generated/api/
  • ✅ ConnectorImpl extends ONLY generated interface (read from generated/api/index.ts)
  • ✅ metadata() exact boilerplate:
    async metadata(): Promise<ConnectionMetadata> { return { status: ConnectionStatus.Down } satisfies ConnectionMetadata; }
  • ✅ isSupported() exact boilerplate:
    async isSupported(_operationId: string) { return OperationSupportStatus.Maybe; }
  • ❌ NO customization of metadata() or isSupported() methods
  • ✅ Mappers implemented for field conversions (const output pattern)
  • ✅ Mappers use
    map()
    from
    @zerobias-org/util-hub-module-utils
    (NOT constructors directly)
  • Mapper runtime validation completed - ZERO missing fields confirmed (see validation section below)
  • ✅ Error handling uses core error types
  • ✅ NO connection context parameters (apiKey, token, baseUrl, organizationId) in producer methods
  • ✅ Using core connectionProfile/connectionState when applicable (or justified custom)
  • ✅ If custom connectionState: includes expiresIn (extends baseConnectionState.yml)
  • ✅ If refresh capability: connectionState has refreshToken + all refresh-relevant data
  • ✅ No TypeScript compilation errors

IF FAILED: Fix implementation before writing tests.

Mapper Runtime Validation (CRITICAL)

MANDATORY validation step after mapper implementation to detect missing fields.

Why Runtime Validation is Required

Static validation (counting interface fields) does NOT catch:

  • ❌ Field name mismatches (API
    mobilePhone
    vs mapper
    phoneNumber
    )
  • ❌ Fields in API but not in spec
  • ❌ Incorrect field lookups in mapper

Real example: User producer had 14 missing fields that passed TypeScript compilation and all tests.

Validation Process

  1. Add temporary debug logs in producer implementations BEFORE mapping:

    this.logger.debug('[TEMP-RAW-API] operationName:', JSON.stringify(response.data, null, 2));
    
  2. Run integration tests with debug logging:

    env LOG_LEVEL=debug npm run test:integration > /tmp/debug.log 2>&1
    
  3. Compare raw API vs mapped fields:

    # Extract raw API response
    grep -A 100 "\[TEMP-RAW-API\]" /tmp/debug.log
    
    # Compare with mapped result
    grep "Api\." /tmp/debug.log
    
  4. Fix missing fields if any found

  5. Remove temp debug logs after validation passes

Automated Validation (RECOMMENDED)

Use parallel mapping-engineer agents (one per producer):

# For each producer, launch agent to:
# 1. Read debug log
# 2. Extract raw API responses
# 3. Compare with mapper
# 4. Fix any missing fields
# 5. Report results

Success Criteria

  • ✅ All [TEMP-RAW-API] logs added
  • ✅ Integration tests run with LOG_LEVEL=debug
  • ✅ Raw API responses extracted
  • ✅ Field-by-field comparison completed
  • ZERO missing fields confirmed
  • ✅ All field name mismatches fixed
  • ✅ API-spec gaps documented
  • ✅ Temp debug logs removed

RULE: Gate 3 does NOT pass until mapper runtime validation shows ZERO missing fields.

Complete process: mapper-runtime-validation skill