Add a VIN decoding button to the "Add Vehicle" and "Edit Vehicle" dialogs that uses the NHTSA vPIC API to automatically populate vehicle fields based on the entered VIN.
Feature Details
Tier Restriction
Pro and Enterprise only - This feature should be gated to Pro and Enterprise subscription tiers.
User Interface
Button placed to the right of the VIN input field on the same row
Available on both "Add Vehicle" and "Edit Vehicle" dialogs
Should include appropriate loading state while API call is in progress
Error handling for invalid VINs or API failures
Behavior
Only fill empty fields - Do not overwrite values the user has already entered
Fuzzy matching required - NHTSA vPIC results won't match dropdown options exactly; implement fuzzy matching to find the best match
Leave unmatched fields empty - If no reasonable match is found for a field, leave it empty for the user to fill manually
Fields to Populate
Populate all available fields where data exists in the NHTSA response, including but not limited to:
Dropdowns: Year, Make, Model, Trim, Body Type, Engine, Transmission, Drive Type, Fuel Type
Text fields: Engine displacement, horsepower, and any other available specifications
Technical Implementation
API calls should go through the backend (not direct frontend calls to NHTSA)
VIN decode button appears next to VIN field in Add Vehicle dialog
VIN decode button appears next to VIN field in Edit Vehicle dialog
Button is only visible/enabled for Pro and Enterprise tier users
Clicking button calls backend endpoint which queries NHTSA vPIC API
Fuzzy matching algorithm maps NHTSA responses to existing dropdown options
Only empty fields are populated (existing user input is preserved)
Appropriate loading state shown during API call
Error messages displayed for invalid VINs or API failures
Works on both mobile and desktop viewports
Priority
Medium
## Summary
Add a VIN decoding button to the "Add Vehicle" and "Edit Vehicle" dialogs that uses the NHTSA vPIC API to automatically populate vehicle fields based on the entered VIN.
## Feature Details
### Tier Restriction
- **Pro and Enterprise only** - This feature should be gated to Pro and Enterprise subscription tiers.
### User Interface
- Button placed **to the right of the VIN input field** on the same row
- Available on both "Add Vehicle" and "Edit Vehicle" dialogs
- Should include appropriate loading state while API call is in progress
- Error handling for invalid VINs or API failures
### Behavior
- **Only fill empty fields** - Do not overwrite values the user has already entered
- **Fuzzy matching required** - NHTSA vPIC results won't match dropdown options exactly; implement fuzzy matching to find the best match
- **Leave unmatched fields empty** - If no reasonable match is found for a field, leave it empty for the user to fill manually
### Fields to Populate
Populate all available fields where data exists in the NHTSA response, including but not limited to:
- **Dropdowns**: Year, Make, Model, Trim, Body Type, Engine, Transmission, Drive Type, Fuel Type
- **Text fields**: Engine displacement, horsepower, and any other available specifications
### Technical Implementation
- API calls should go through the **backend** (not direct frontend calls to NHTSA)
- Use NHTSA vPIC API: https://vpic.nhtsa.dot.gov/api/
- No rate limiting concerns for this implementation
## Acceptance Criteria
- [ ] VIN decode button appears next to VIN field in Add Vehicle dialog
- [ ] VIN decode button appears next to VIN field in Edit Vehicle dialog
- [ ] Button is only visible/enabled for Pro and Enterprise tier users
- [ ] Clicking button calls backend endpoint which queries NHTSA vPIC API
- [ ] Fuzzy matching algorithm maps NHTSA responses to existing dropdown options
- [ ] Only empty fields are populated (existing user input is preserved)
- [ ] Appropriate loading state shown during API call
- [ ] Error messages displayed for invalid VINs or API failures
- [ ] Works on both mobile and desktop viewports
## Priority
Medium
Add a VIN decode button to vehicle dialogs that calls NHTSA vPIC API through backend, with tiered auto-fill based on match confidence. Pro/Enterprise tier restriction enforced via existing tier gating system.
Architectural Decision (from Decision Critic)
Verdict: REVISE to Tiered Hybrid Approach
Instead of complex fuzzy matching, implement tiered confidence levels:
Tier 1 (Auto-fill): Year (exact), Make (case-insensitive exact)
Tier 2 (Suggest): Model, Trim - show NHTSA value, pre-select if exact match
Tier 3 (Display): Engine, Transmission, Body Type, Drive Type, Fuel Type - show as reference hints
This eliminates false positive risk while still providing value from VIN decoding.
// In VehicleForm.tsx decode handler:
consthandleDecode=async()=>{constdecoded=awaitdecodeVin(vin);// Only apply to empty fields (preserve user input)
if(!watchedYear&&decoded.year)setValue('year',decoded.year);if(!watchedMake&&decoded.make)setValue('make',decoded.make);if(!watchedModel&&decoded.model)setValue('model',decoded.model);if(!watchedTrim&&decoded.trimLevel)setValue('trimLevel',decoded.trimLevel);// ... etc for all fields
};
2. Additional Test Cases Required
Test Case
Expected Result
Decode VIN with existing year filled
Year field unchanged, other empty fields populated
Decode VIN with all fields filled
No fields overwritten
VIN decode button visible (pro user)
Button renders in form
Loading spinner during decode
Button shows loading state, disabled during call
Minor Recommendations (Non-Blocking)
Specify dropdown option source for matching
Document behavior for unknown makes/models
Verify Enterprise tier inherits Pro access
Verdict: NEEDS_REVISION | Next: Update plan with fixes, re-review
## QR Review: Plan Completeness
**Phase**: Plan-Review | **Agent**: Quality Reviewer | **Status**: NEEDS_REVISION
---
### Findings
**CRITICAL GAP**: Acceptance criterion "Only empty fields are populated" not explicitly addressed in plan.
### Required Changes
#### 1. Milestone 4 Addition - Preserve Existing Values Logic
Add to Milestone 4 deliverables:
```typescript
// In VehicleForm.tsx decode handler:
const handleDecode = async () => {
const decoded = await decodeVin(vin);
// Only apply to empty fields (preserve user input)
if (!watchedYear && decoded.year) setValue('year', decoded.year);
if (!watchedMake && decoded.make) setValue('make', decoded.make);
if (!watchedModel && decoded.model) setValue('model', decoded.model);
if (!watchedTrim && decoded.trimLevel) setValue('trimLevel', decoded.trimLevel);
// ... etc for all fields
};
```
#### 2. Additional Test Cases Required
| Test Case | Expected Result |
|-----------|-----------------|
| Decode VIN with existing year filled | Year field unchanged, other empty fields populated |
| Decode VIN with all fields filled | No fields overwritten |
| VIN decode button visible (pro user) | Button renders in form |
| Loading spinner during decode | Button shows loading state, disabled during call |
---
### Minor Recommendations (Non-Blocking)
1. Specify dropdown option source for matching
2. Document behavior for unknown makes/models
3. Verify Enterprise tier inherits Pro access
---
*Verdict*: NEEDS_REVISION | *Next*: Update plan with fixes, re-review
Add API method in frontend/src/features/vehicles/api/vehicles.api.ts
Handle decoded data with field preservation:
consthandleDecode=async()=>{setIsDecoding(true);try{constdecoded=awaitvehiclesApi.decodeVin(vinValue);// Only populate empty fields (preserve existing user input)
if(!watchedYear&&decoded.year)setValue('year',decoded.year);if(!watchedMake&&decoded.make)setValue('make',decoded.make);if(!watchedModel&&decoded.model)setValue('model',decoded.model);if(!watchedTrim&&decoded.trimLevel)setValue('trimLevel',decoded.trimLevel);if(!watchedEngine&&decoded.engine)setValue('engine',decoded.engine);if(!watchedTransmission&&decoded.transmission)setValue('transmission',decoded.transmission);if(!watchedDriveType&&decoded.driveType)setValue('driveType',decoded.driveType);if(!watchedFuelType&&decoded.fuelType)setValue('fuelType',decoded.fuelType);if(!watchedBodyType&&decoded.bodyType)setValue('bodyType',decoded.bodyType);}catch(error){// Show error toast
}finally{setIsDecoding(false);}};
Error handling for invalid VINs, API failures
Test Plan (Updated)
Test Case
Expected Result
Decode valid VIN (pro user)
Returns decoded data, empty fields populated
Decode valid VIN (free user)
Returns 403 TIER_REQUIRED
Decode invalid VIN
Returns 400 with error message
Decode cached VIN
Returns cached data, no NHTSA call
Exact make match
Auto-fills make dropdown
No model match
Shows NHTSA model as hint, dropdown empty
Mobile viewport
Button stacks below VIN input
Desktop viewport
Button inline with VIN input
Decode with existing year filled
Year unchanged, other empty fields populated
Decode with all fields filled
No fields overwritten
VIN button visibility (pro user)
Button renders in form
Loading spinner during decode
Button shows loading state, disabled
Dropdown Option Behavior Clarified
Unknown makes: Return NHTSA value as display hint, dropdown stays empty (user selects manually)
Unknown models: Return NHTSA value as display hint, user confirms or selects from dropdown
Tier inheritance: Enterprise inherits Pro access (handled by getTierLevel() comparison)
Verdict: REVISED | Next: TW plan-scrub review
## Plan Update: Addressing QR Feedback
**Phase**: Planning | **Agent**: Planner | **Status**: REVISED
---
### Milestone 4 (Revised): Frontend Decode Button
**Deliverables:**
1. Modify `frontend/src/features/vehicles/components/VehicleForm.tsx`:
- Add decode button next to VIN input (flex layout)
- Button shows loading state during API call
- Tier check with `useTierAccess()` hook
- Show `UpgradeRequiredDialog` for free tier users
2. Add API method in `frontend/src/features/vehicles/api/vehicles.api.ts`
3. **Handle decoded data with field preservation:**
```typescript
const handleDecode = async () => {
setIsDecoding(true);
try {
const decoded = await vehiclesApi.decodeVin(vinValue);
// Only populate empty fields (preserve existing user input)
if (!watchedYear && decoded.year) setValue('year', decoded.year);
if (!watchedMake && decoded.make) setValue('make', decoded.make);
if (!watchedModel && decoded.model) setValue('model', decoded.model);
if (!watchedTrim && decoded.trimLevel) setValue('trimLevel', decoded.trimLevel);
if (!watchedEngine && decoded.engine) setValue('engine', decoded.engine);
if (!watchedTransmission && decoded.transmission) setValue('transmission', decoded.transmission);
if (!watchedDriveType && decoded.driveType) setValue('driveType', decoded.driveType);
if (!watchedFuelType && decoded.fuelType) setValue('fuelType', decoded.fuelType);
if (!watchedBodyType && decoded.bodyType) setValue('bodyType', decoded.bodyType);
} catch (error) {
// Show error toast
} finally {
setIsDecoding(false);
}
};
```
4. Error handling for invalid VINs, API failures
---
### Test Plan (Updated)
| Test Case | Expected Result |
|-----------|-----------------|
| Decode valid VIN (pro user) | Returns decoded data, empty fields populated |
| Decode valid VIN (free user) | Returns 403 TIER_REQUIRED |
| Decode invalid VIN | Returns 400 with error message |
| Decode cached VIN | Returns cached data, no NHTSA call |
| Exact make match | Auto-fills make dropdown |
| No model match | Shows NHTSA model as hint, dropdown empty |
| Mobile viewport | Button stacks below VIN input |
| Desktop viewport | Button inline with VIN input |
| **Decode with existing year filled** | **Year unchanged, other empty fields populated** |
| **Decode with all fields filled** | **No fields overwritten** |
| **VIN button visibility (pro user)** | **Button renders in form** |
| **Loading spinner during decode** | **Button shows loading state, disabled** |
---
### Dropdown Option Behavior Clarified
- **Unknown makes**: Return NHTSA value as display hint, dropdown stays empty (user selects manually)
- **Unknown models**: Return NHTSA value as display hint, user confirms or selects from dropdown
- **Tier inheritance**: Enterprise inherits Pro access (handled by `getTierLevel()` comparison)
---
*Verdict*: REVISED | *Next*: TW plan-scrub review
The plan is well-structured and implementable, with clear milestones and strong alignment to project patterns. However, 4 minor clarifications are needed for developer implementation:
Endpoint naming inconsistency - plan says POST /api/vehicles/decode-vin but should clarify exact field/body structure
Cache table reference - plan mentions vin_cache table but not confirmed in project
Dropdown matcher behavior documentation - needs specificity on empty/unknown results
Frontend hook dependency - useTierAccess() hook may not exist yet
CLARITY - Excellent
The plan is clear and well-written:
Executive summary frames the feature concisely
Milestone breakdown is logical and specific
Test plan is comprehensive with concrete test cases
Code snippets are appropriate for TypeScript developers
Terminology is consistent (mostly - see below)
No clarity issues identified.
CONSISTENCY - Minor Issues
1. Endpoint Naming - Request Body Structure
Issue: Plan states:
POST /api/vehicles/decode-vin
Clarification Needed: The request body structure should be explicitly documented.
Recommendation:
// Add to Milestone 2:
POST/api/vehicles/decode-vinBody:{vin: string}Response: DecodedVehicleData(seetypesdefinition)
Rationale: Project pattern shows detailed endpoint documentation (see /api/vehicles/dropdown/... in routes.ts). Developers will need the exact request/response contracts.
2. Cache Table Verification
Issue: Milestone 1 references vin_cache table:
Cache results in existing `vin_cache` table
Clarification Needed: Verify this table exists in schema.
Recommendation:
Either: "Cache results in existing vin_cache table (migration pending)"
Or: Replace with documented cache key pattern like vehicles:${vin}:nhtsa
Rationale: Current vehicles.service.ts shows Redis cache with key prefixes (e.g., vehicles: prefix). Confirm whether DB caching is needed alongside Redis.
The matchField function in backend/src/features/vehicles/domain/vehicles.service.ts only does exact case-insensitive matching. NHTSA returns partial names (e.g., "Sierra") that don't match dropdown options (e.g., "Sierra 1500", "Sierra 2500HD").
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Add a VIN decoding button to the "Add Vehicle" and "Edit Vehicle" dialogs that uses the NHTSA vPIC API to automatically populate vehicle fields based on the entered VIN.
Feature Details
Tier Restriction
User Interface
Behavior
Fields to Populate
Populate all available fields where data exists in the NHTSA response, including but not limited to:
Technical Implementation
Acceptance Criteria
Priority
Medium
Plan: VIN Decoding Feature
Phase: Planning | Agent: Planner | Status: AWAITING_REVIEW
Executive Summary
Add a VIN decode button to vehicle dialogs that calls NHTSA vPIC API through backend, with tiered auto-fill based on match confidence. Pro/Enterprise tier restriction enforced via existing tier gating system.
Architectural Decision (from Decision Critic)
Verdict: REVISE to Tiered Hybrid Approach
Instead of complex fuzzy matching, implement tiered confidence levels:
This eliminates false positive risk while still providing value from VIN decoding.
Milestone 1: Backend NHTSA Integration
Deliverables:
Create
backend/src/features/vehicles/external/nhtsa/nhtsa.client.tshttps://vpic.nhtsa.dot.gov/api/vehicles/decodevin/{VIN}?format=jsonvin_cachetableCreate
backend/src/features/vehicles/external/nhtsa/nhtsa.types.tsNHTSADecodeResponseinterfaceDecodedVehicleDatainterface with normalized fieldsAdd tier gating in
backend/src/core/config/feature-tiers.ts:Files: 3 new, 1 modified
Milestone 2: VIN Decode Endpoint
Deliverables:
Add route in
backend/src/features/vehicles/api/vehicles.routes.ts:Add controller method in
vehicles.controller.ts:Add service method in
vehicles.service.ts:Create unit tests for NHTSA client and decode logic
Files: 3 modified, 1 test file
Milestone 3: Dropdown Matching Service
Deliverables:
Create
backend/src/features/vehicles/domain/dropdown-matcher.ts:matchMake(nhtsaValue, makes[]): Case-insensitive exact matchmatchModel(nhtsaValue, models[]): Case-insensitive exact matchmatchTrim(nhtsaValue, trims[]): Case-insensitive exact match{ value: string | null, confidence: 'high' | 'medium' | 'none' }Integrate with decode endpoint to:
Unit tests for matching logic
Files: 1 new, 1 modified, 1 test file
Milestone 4: Frontend Decode Button
Deliverables:
Modify
frontend/src/features/vehicles/components/VehicleForm.tsx:useTierAccess()hookUpgradeRequiredDialogfor free tier usersAdd API method in
frontend/src/features/vehicles/api/vehicles.api.ts:Handle decoded data:
Error handling for invalid VINs, API failures
Files: 2 modified
Milestone 5: Mobile + Desktop UI Polish
Deliverables:
Files: 1 modified (VehicleForm.tsx)
Milestone 6: Integration Testing + Documentation
Deliverables:
Files: 1 test file, 1 doc file
Test Plan
File Summary
New Files (5):
backend/src/features/vehicles/external/nhtsa/nhtsa.client.tsbackend/src/features/vehicles/external/nhtsa/nhtsa.types.tsbackend/src/features/vehicles/domain/dropdown-matcher.tsbackend/src/features/vehicles/tests/unit/nhtsa.client.test.tsbackend/src/features/vehicles/tests/integration/vin-decode.integration.test.tsModified Files (5):
backend/src/core/config/feature-tiers.tsbackend/src/features/vehicles/api/vehicles.routes.tsbackend/src/features/vehicles/api/vehicles.controller.tsbackend/src/features/vehicles/domain/vehicles.service.tsfrontend/src/features/vehicles/components/VehicleForm.tsxfrontend/src/features/vehicles/api/vehicles.api.tsVerdict: AWAITING_REVIEW | Next: QR plan-completeness review
QR Review: Plan Completeness
Phase: Plan-Review | Agent: Quality Reviewer | Status: NEEDS_REVISION
Findings
CRITICAL GAP: Acceptance criterion "Only empty fields are populated" not explicitly addressed in plan.
Required Changes
1. Milestone 4 Addition - Preserve Existing Values Logic
Add to Milestone 4 deliverables:
2. Additional Test Cases Required
Minor Recommendations (Non-Blocking)
Verdict: NEEDS_REVISION | Next: Update plan with fixes, re-review
Plan Update: Addressing QR Feedback
Phase: Planning | Agent: Planner | Status: REVISED
Milestone 4 (Revised): Frontend Decode Button
Deliverables:
Modify
frontend/src/features/vehicles/components/VehicleForm.tsx:useTierAccess()hookUpgradeRequiredDialogfor free tier usersAdd API method in
frontend/src/features/vehicles/api/vehicles.api.tsHandle decoded data with field preservation:
Error handling for invalid VINs, API failures
Test Plan (Updated)
Dropdown Option Behavior Clarified
getTierLevel()comparison)Verdict: REVISED | Next: TW plan-scrub review
TW Plan-Scrub Review: VIN Decoding Feature
Phase: Technical Review | Agent: Technical Writer | Status: NEEDS_MINOR_REVISIONS
Executive Summary
The plan is well-structured and implementable, with clear milestones and strong alignment to project patterns. However, 4 minor clarifications are needed for developer implementation:
POST /api/vehicles/decode-vinbut should clarify exact field/body structurevin_cachetable but not confirmed in projectuseTierAccess()hook may not exist yetCLARITY - Excellent
The plan is clear and well-written:
No clarity issues identified.
CONSISTENCY - Minor Issues
1. Endpoint Naming - Request Body Structure
Issue: Plan states:
Clarification Needed: The request body structure should be explicitly documented.
Recommendation:
Rationale: Project pattern shows detailed endpoint documentation (see
/api/vehicles/dropdown/...in routes.ts). Developers will need the exact request/response contracts.2. Cache Table Verification
Issue: Milestone 1 references
vin_cachetable:Clarification Needed: Verify this table exists in schema.
Recommendation:
vin_cachetable (migration pending)"vehicles:${vin}:nhtsaRationale: Current vehicles.service.ts shows Redis cache with key prefixes (e.g.,
vehicles:prefix). Confirm whether DB caching is needed alongside Redis.3. Case Convention - Variable Naming
Issue: Plan uses mixed case in code examples:
decoded.year,decoded.make,decoded.trimLevel(camelCase) ✓Recommendation: Add clarification to Milestone 1:
Rationale: Project naming standard is strict (database snake_case → TypeScript camelCase). Mapping NHTSA response fields to internal types needs explicit documentation.
STRUCTURE - Excellent
Milestone flow is logical:
Dependencies are clear - each milestone builds on previous ones.
TERMINOLOGY - Minor Consistency Issue
Current usage:
Suggestion: The plan uses "VIN decode" inconsistently in some places. Standardize:
Recommendation:
/decode-vin(lowercase with hyphen)decodeVin()(camelCase)Apply consistently throughout. Current draft is close but would benefit from explicit terminology section.
TECHNICAL ACCURACY - Strong
Architecture patterns align with codebase:
No technical issues detected.
MISSING DETAILS (Non-Blocking Recommendations)
1. Error Cases - Expand Test Plan
Current test plan covers happy path and tier restriction. Consider adding:
2. Frontend Error Handling - Toast Configuration
Plan mentions "error toast" but doesn't specify:
toastService.error(message, { duration: 5000 }))Recommend documenting error messages for:
3. Route Registration Order
Plan should note: "Route must be registered before dynamic
:idroutes" (per existing vehicles.routes.ts pattern on line 34).FILE PATH VERIFICATION
All file paths follow project conventions:
backend/src/features/vehicles/external/nhtsa/backend/src/features/vehicles/domain/dropdown-matcher.tsfrontend/src/features/vehicles/components/backend/src/features/vehicles/tests/unit/&/integration/No path issues.
RECOMMENDATIONS SUMMARY
Required Changes (block implementation):
vin_cachetable or Redis? Add migration if DB table.POST /api/vehicles/decode-vinRecommended Changes (improve clarity, non-blocking):
useTierAccess()hook source or create if missingREADY TO IMPLEMENT?
Status: ✅ YES - with clarifications below
The plan is implementation-ready once these three items are addressed:
Estimated implementation time: 8-12 story points across 6 milestones
Verdict: NEEDS_MINOR_REVISIONS | Next: Planner updates clarifications, TW re-reviews
TW Review: Plan Scrub
Phase: Plan-Review | Agent: Technical Writer | Status: PASS with minor notes
Clarity: EXCELLENT
Consistency: MINOR NOTES
Endpoint Contract: Add request/response documentation
Cache Clarification: The
vin_cachetable exists in vehicles migrations (001_create_vehicles_tables.sql). No new migration needed.NHTSA Field Mapping: Document key mappings:
ModelYear→year(number)Make→make(string)Model→model(string)Trim→trimLevel(string)BodyClass→bodyType(string)DriveType→driveType(string)FuelTypePrimary→fuelType(string)Structure: EXCELLENT
decode-vinroute should be registered before:idroutesTerminology: CONSISTENT
/decode-vindecodeVin()Verdict: PASS | Next: QR plan-code review
QR Review: Plan Code Quality
Phase: Plan-Review | Agent: Quality Reviewer | Mode: plan-code | Status: PASS_WITH_CONCERNS
RULE 0 (CRITICAL) - Production Reliability
1. VIN Validation Missing in NHTSA Client
/^[A-HJ-NPR-Z0-9]{17}$/regex check before API call2. No Timeout Configuration
3. NHTSA API Error Handling
RULE 1 (HIGH) - Project Standards
4. Mobile Responsive Specification
flexDirection: { xs: 'column', sm: 'row' }minWidth: { xs: '100%', sm: '120px' }5. NHTSA Field Mapping
RULE 2 (SHOULD_FIX) - Structural Quality
6. Field Preservation Logic
7. Cache TTL Strategy
Code Snippets for Fixes
Verdict: PASS_WITH_CONCERNS (implement fixes during development) | Next: QR plan-docs review
QR Review: Plan Documentation
Phase: Plan-Review | Agent: Quality Reviewer | Mode: plan-docs | Status: NEEDS_REVISION
Critical Gaps
1. Missing CLAUDE.md for New Directory (MANDATORY)
backend/src/features/vehicles/external/nhtsa/backend/src/features/vehicles/external/CLAUDE.mdAdd to Milestone 1:
2. API Endpoint Documentation Incomplete
Required content for
POST /api/vehicles/decode-vin:{ vin: string }DecodedVehicleDatawith confidence levels3. Test Coverage Not Specified
Milestone 6 Revision
Update to specify:
Documentation Checklist
Verdict: NEEDS_REVISION | Next: Planner updates plan with documentation requirements
Plan: Final Revision
Phase: Planning | Agent: Planner | Status: APPROVED
All QR review feedback incorporated. Plan is ready for implementation.
Summary of Revisions
From QR plan-completeness:
From TW plan-scrub:
From QR plan-code:
From QR plan-docs:
Updated Milestone 1
Deliverables:
Create
backend/src/features/vehicles/external/nhtsa/nhtsa.client.ts/^[A-HJ-NPR-Z0-9]{17}$/before API callCreate
backend/src/features/vehicles/external/nhtsa/nhtsa.types.tsCreate
backend/src/features/vehicles/external/CLAUDE.mdAdd tier gating in
backend/src/core/config/feature-tiers.tsUpdated Milestone 5
Mobile Responsive Specification:
Updated Milestone 6
Deliverables:
backend/src/features/vehicles/README.md:{ vin: string }Updated Test Plan (12 cases)
Verdict: APPROVED | Next: Create branch, begin implementation
Milestone Update: Implementation Complete
Phase: Execution | Agent: Developer | Status: COMPLETE
Completed Milestones
Milestone 1: Backend NHTSA Integration - COMPLETE
external/nhtsa/nhtsa.client.tswith VIN validation and cachingexternal/nhtsa/nhtsa.types.tswith TypeScript interfacesexternal/CLAUDE.mddocumenting integration patternvehicle.vinDecodefeature key to tier configMilestone 2: VIN Decode Endpoint - COMPLETE
POST /api/vehicles/decode-vinroute with tier gatingMilestone 3: Dropdown Matching Service - COMPLETE
mapNHTSAResponse()to VehiclesServicematchField()with confidence levels (high/medium/none)Milestone 4: Frontend Decode Button - COMPLETE
useTierAccess()hookUpgradeRequiredDialogfor free tier usersMilestone 5: Mobile + Desktop UI Polish - COMPLETE
flex-col sm:flex-rowMilestone 6: Documentation - COMPLETE
Commits
feat: Add VIN decoding with NHTSA vPIC API (refs #9)- 760 insertionsdocs: Update vehicles README with VIN decode endpoint (refs #9)Files Changed
New (6):
backend/src/features/vehicles/external/CLAUDE.mdbackend/src/features/vehicles/external/nhtsa/index.tsbackend/src/features/vehicles/external/nhtsa/nhtsa.client.tsbackend/src/features/vehicles/external/nhtsa/nhtsa.types.tsModified (8):
backend/src/core/config/feature-tiers.tsbackend/src/features/vehicles/api/vehicles.controller.tsbackend/src/features/vehicles/api/vehicles.routes.tsbackend/src/features/vehicles/domain/vehicles.service.tsbackend/src/features/vehicles/README.mdfrontend/src/features/vehicles/api/vehicles.api.tsfrontend/src/features/vehicles/components/VehicleForm.tsxfrontend/src/features/vehicles/types/vehicles.types.tsVerdict: COMPLETE | Next: Push to remote, create PR
Plan: Add Fuzzy Matching to VIN Decode
Phase: Execution | Agent: Orchestrator | Status: IN_PROGRESS
Problem
The
matchFieldfunction inbackend/src/features/vehicles/domain/vehicles.service.tsonly does exact case-insensitive matching. NHTSA returns partial names (e.g., "Sierra") that don't match dropdown options (e.g., "Sierra 1500", "Sierra 2500HD").Current Response Example
Solution
Add prefix/partial matching to
matchFieldfunction:highconfidencemediumconfidencemediumconfidencemediumconfidencenoneconfidenceDesign Decision
Ambiguity handling: Return first match when multiple options match (user can adjust from dropdown).
Files to Modify
backend/src/features/vehicles/domain/vehicles.service.tsmatchFieldfunction with prefix/contains matchingAcceptance Criteria
Verdict: APPROVED | Next: Delegate to feature-agent for implementation
Milestone: Fuzzy Matching Implementation
Phase: Execution | Agent: feature-agent | Status: PASS
Changes Made
File:
backend/src/features/vehicles/domain/vehicles.service.tsEnhanced
matchFieldfunction with 4-level matching hierarchy:highconfidencemediumconfidencemediumconfidencemediumconfidenceExamples
Validation
QR Review: RULE 0/1/2 Validation
Phase: Review | Agent: quality-agent | Status: PASS
RULE 0 (CRITICAL): Production Reliability
RULE 1 (HIGH): Project Standards
RULE 2 (SHOULD_FIX): Structural Quality
Verdict: PASS | Next: Commit and push changes