Corrective Action: HubSpot Beginners Course — Silent System Failures Across Full Education Stack
Date: March 12, 2026 Category: Verification Failure + Data Format Mismatch (compound) Impact: Entire HubSpot education system non-functional at the integration layer. Course progress, quiz completion, certificate issuance, and pipeline tracking all fail silently. Visitor-facing content has enforcement violations and critical dead ends. Zero relationship capture across 90-minute learning journey. Resolution Time: Identified during multi-agent audit. Repair pending.
Incident
What Happened
A multi-agent review of the HubSpot Beginners course (/learn/course/hubspot-beginners) revealed that the entire HubSpot integration layer is broken. Course progress tracking, quiz completion, certificate issuance, and pipeline stage transitions all fail silently because the API layer sends wrong data types (string stage names instead of numeric IDs), references non-existent HubSpot properties (4 certificate properties on Contact), uses a deprecated object (Signal), and sets read-only system properties (hs_object_id). The localStorage fallback for unauthenticated users masks these failures — the course appears to work for anonymous visitors, but no data persists to HubSpot for any user.
Simultaneously, the course content contains a critical dead-end page (lesson 6-2 "Your Next Steps" lists 8+ resources as text with zero clickable links), forbidden language on the live site ("quick wins", "plan phased improvements"), a visible rendering bug (duration shows blank due to course.duration vs totalDuration property mismatch), and zero email capture across the entire 90-minute learning journey.
Timeline
| Event | Detail |
|---|---|
| ~Jan 2026 | Course pages and APIs built. Education Hub launched. |
| Feb 16, 2026 | Signal object deprecated (migrated to Interest). quiz/complete.ts still calls createSignal. |
| Mar 12, 2026 | Multi-agent audit discovers 8 critical, 10 major, 12 minor issues. |
| Mar 12, 2026 | LMS Manager agent created to own education system integrity. CAR drafted. |
Root Cause
Primary: Speculative Integration — Code Written Against Assumed HubSpot Schema, Never Verified
The education system APIs were built with assumptions about the HubSpot portal that were never validated:
Pipeline stages as strings —
course-progress.tspasses'Enrolled','In Progress','Passed'tohs_pipeline_stage. HubSpot requires numeric stage IDs (e.g.,'1251442051'). This is the same pattern that was correctly handled in other parts of the codebase (e.g.,theater-seed-deployer.tsuses proper stage IDs). The education APIs simply never adopted the correct pattern.Non-existent contact properties — The certificate system writes to
vf_certificates_earned,vf_certificate_count,vf_last_certificate_earned, andvf_last_certificate_dateon Contact objects. None of these properties exist in HubSpot (verified againstcontact.jsonexported 2026-03-02). They were defined in TypeScript types but never created in the portal.Enum value mismatch —
course-progress.tsfilters courses bycourse_content_type === 'Learning Course'. The HubSpot property index shows the valid enum values are snake_case (trap_assessment,framework_education,bootcamp,capability_building).'Learning Course'may not be a valid value, which would cause every course lookup to return empty — triggering duplicate Course creation on every visit.Unverified association type —
hubspotClient.associateCourseToContactuses hardcodedassociationTypeId: 182with only a comment as documentation. This ID does not appear inassociations.json. If wrong, courses are created but never linked to contacts.
Secondary: Silent Error Handling Masks Failures
Every HubSpot API call in the education system is wrapped in try/catch blocks that return fallback responses instead of surfacing errors:
course-progress.tsreturns{ lessonProgress: [] }when HubSpot calls failcertificate.tsreturnsnullon failure, and the calling code silently continuesuseCourseProgress.tsfalls back to localStorage on any API error
This means: the system appears functional even when every HubSpot operation fails. Anonymous users get localStorage progress. Authenticated users get the same localStorage fallback after silent API failures. No one sees an error. No data reaches HubSpot.
Tertiary: Content Published Without Enforcement Scanning
Lesson 6-2 contains forbidden language ("quick wins" at line 167, "Plan phased improvements" at line 174) that should have been caught by enforcement scanning before the content shipped. Additionally, the page lists 8 resources (Education Hub pages, Record Builder, Theater, etc.) as text-only cards with zero <a> tags — creating a dead end at the course's most critical engagement moment.
Category Breakdown
| Category | Pattern | Specific Instance |
|---|---|---|
| Data Format Mismatch | Wrong value type sent to API | Pipeline stage strings instead of IDs |
| Missing Configuration | Properties defined in TypeScript but never created in HubSpot | 4 certificate properties, potentially 8 course properties |
| Verification Failure | Code assumed to work, never tested against live portal | All 4 API routes (course-progress, quiz/complete, quiz/history, certificate/issue) |
| API Behavior Assumption | Using deprecated object without migration | quiz/complete.ts calls createSignal (deprecated Feb 16) |
| API Behavior Assumption | Setting read-only system property | quiz/complete.ts sets hs_object_id |
| Content Integrity | Published content with enforcement violations | Forbidden language + dead-end links in lesson 6-2 |
Fix Applied
Immediate Resolution
LMS Manager agent created (.claude/agents/lms-manager.md) with:
- Full Known Issues Registry documenting all 30 findings
- 6 validation check categories for pre-deployment and ongoing monitoring
- Coordination model with concierge-learner, audit, lookout, q, and squire
Concierge-learner agent updated:
- Lesson count corrected from 18 to 20
- Status changed from "COMPLETE" to "NEEDS REPAIR"
Code/Configuration Changes Required (Not Yet Applied)
| Priority | File | Required Change |
|---|---|---|
| CRITICAL | src/pages/learn/course/hubspot-beginners.astro:57 |
Change course.duration to course.totalDuration |
| CRITICAL | src/pages/api/course-progress.ts:163,197-201 |
Replace string stage names with numeric pipeline stage IDs from HubSpot |
| CRITICAL | src/pages/api/course-progress.ts:21 |
Verify 'Learning Course' is valid enum for course_content_type or fix to match schema |
| CRITICAL | HubSpot portal | Create 4 certificate properties on Contact: vf_certificates_earned, vf_certificate_count, vf_last_certificate_earned, vf_last_certificate_date |
| CRITICAL | src/pages/api/quiz/complete.ts:66 |
Remove hs_object_id from Course creation payload |
| CRITICAL | src/pages/api/quiz/complete.ts:108 |
Replace createSignal with createInterest (Signal deprecated Feb 16) or wrap in try/catch |
| CRITICAL | src/pages/learn/course/hubspot-beginners/6-2-next-steps.astro |
Add links to all 8 resource mentions; remove "quick wins" and "plan phased improvements" |
| CRITICAL | Course journey | Add email capture mechanism (newsletter, registration prompt, or similar) |
| MAJOR | src/pages/api/quiz/complete.ts:188 |
Change 'all_unified_views' to 'multi_quiz' for Unified Views Specialist cert lookup |
| MAJOR | skills/hubspot/property-index/course.json |
Add pipeline stages with numeric IDs; verify course_content_type enum includes Learning Course |
| MAJOR | skills/hubspot/property-index/associations.json |
Verify and document Course ↔ Contact association type ID |
Verification Plan
Each fix must be verified, not assumed:
- Pipeline stages: Query HubSpot for Course pipelines, get actual stage IDs, update code and property index
- Certificate properties: Create properties in HubSpot, update property index, verify
updateContactsucceeds - Enum values: Query HubSpot for
course_content_typeenum, verify or update the value used in code - Association type: Query HubSpot for Course ↔ Contact association types, verify or update the hardcoded ID
- Signal → Interest: Verify Interest object accepts the same data shape, update the method call
- Duration bug: After fix, fetch live page and confirm duration displays
- Lesson 6-2: After adding links, verify every href resolves to a live page
Prevention Measures
Rules to Add
| Layer | File | Rule |
|---|---|---|
| Critical Lessons | memory/MEMORY.md |
"Every HubSpot API integration MUST be verified against the live portal before shipping. Silent try/catch fallbacks mask failures for months. Verify: (1) properties exist, (2) enum values match, (3) pipeline stage IDs are numeric, (4) association type IDs are correct. The education system shipped with ALL four wrong." |
| Self-Correction | skills/enforcement/vf-self-correction.md |
New Architecture Trigger: "Writing HubSpot pipeline stage values as human-readable strings" → "HubSpot requires numeric stage IDs. Check property-index for the pipeline, use the ID." |
| Self-Correction | skills/enforcement/vf-self-correction.md |
New Verification Trigger: "Building an API that catches HubSpot errors and returns fallback data without logging" → "Silent fallbacks mask failures. Log errors server-side. Return specific error codes so the frontend can distinguish 'no progress yet' from 'system failure'." |
| Content Enforcement | Enforcement scanning process | Every lesson page must pass forbidden language grep before deployment. LMS Manager Check 5 codifies this. |
Detection Triggers
Pipeline stage strings in any new API code — If code writes
hs_pipeline_stage: 'SomeName'instead ofhs_pipeline_stage: '12345678', the LMS Manager's Check 2 catches it.HubSpot property references without property-index verification — Any new code that writes to HubSpot properties should cross-reference the property index first.
Silent error fallbacks — APIs that catch HubSpot errors and return success-shaped responses should at minimum log the error server-side.
Deprecated object usage —
createSignalshould be grep-scanned during any code audit. Signal (2-53177182) was deprecated Feb 16, 2026.
Lessons
The education system shipped with a visually complete frontend and a fundamentally broken backend. The localStorage fallback — designed as a graceful degradation for unauthenticated users — accidentally became the ONLY functional path, masking total HubSpot integration failure for every user. This is the compound failure: silent error handling + speculative schema assumptions + no integration verification = a system that looks right but stores nothing.
The generalized principle: any system with a fallback path will silently fail for months if the primary path is never verified. The fallback becomes the de facto behavior, and no one notices the primary path is broken until someone audits the data layer.
This reinforces the existing verification rules in vf-self-correction.md ("VERIFY. Run the command, read the output, confirm. Then claim.") but extends them to a new category: integration verification. Reading TypeScript types is not verifying HubSpot properties exist. Writing try/catch is not verifying the API call succeeds. The verification must reach the actual destination system.
Related Incidents
| Date | Incident | Pattern |
|---|---|---|
| Feb 16, 2026 | Signal object deprecated, migrated to Interest | Object lifecycle — quiz code never updated |
| Mar 2, 2026 | Paragon document invisible (wrong listing_content_type) |
Enum value mismatch — exact same class of bug |
| Mar 10, 2026 | Paragon portal showing 40% of data | Silent data drift — exact same masking pattern |
| Mar 11, 2026 | Sponsor API creating Deals without Contact associations | Missing association — exact same pattern as Course ↔ Contact |
This is the fourth instance of "HubSpot writes that silently fail due to wrong values or missing associations." The pattern is systemic, not isolated.