From abridge-pack
Plans Abridge API version upgrades and EHR migrations (Epic, Athena) using TypeScript adapters, canary rollouts, and step-by-step procedures for zero-downtime in healthcare apps.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin abridge-packThis skill is limited to using the following tools:
Procedures for upgrading Abridge API integrations and migrating between EHR systems. Healthcare migrations are high-risk — clinical documentation cannot have gaps.
Provides reference architecture for Abridge clinical AI integration with EHRs (Epic/Athena) via FHIR R4 APIs. Includes HIPAA-compliant data flows, project structure for multi-site health systems.
Provides OpenEvidence migration strategies, checklist for API mapping and data plans, and resources. Useful for planning SaaS switchover in healthcare apps.
Implements low-downtime API migrations between versions or frameworks using strangler fig, traffic shadowing, endpoint mapping, adapters, and phased cutovers.
Share bugs, ideas, or general feedback.
Procedures for upgrading Abridge API integrations and migrating between EHR systems. Healthcare migrations are high-risk — clinical documentation cannot have gaps.
| Scenario | Complexity | Downtime | Risk |
|---|---|---|---|
| API version bump (v1 → v2) | Medium | Zero (dual-version) | Low |
| EHR migration (Epic → Athena) | High | Planned window | High |
| New specialty onboarding | Low | Zero | Low |
| Note template changes | Medium | Zero | Medium |
| Multi-site rollout | High | Per-site windows | Medium |
// src/migration/api-version-adapter.ts
// Dual-version adapter for zero-downtime API upgrades
interface ApiVersionConfig {
v1BaseUrl: string; // Current production
v2BaseUrl: string; // New version (canary)
canaryPercent: number; // Percentage of traffic to v2
}
class AbridgeVersionAdapter {
constructor(private config: ApiVersionConfig) {}
getBaseUrl(): string {
// Gradual canary rollout
const useV2 = Math.random() * 100 < this.config.canaryPercent;
return useV2 ? this.config.v2BaseUrl : this.config.v1BaseUrl;
}
// Map v1 response to v2 format (or vice versa)
normalizeNoteResponse(response: any, version: 'v1' | 'v2'): any {
if (version === 'v1') {
return {
...response,
// v2 adds quality_metrics — provide defaults for v1
quality_metrics: response.quality_metrics || {
confidence_score: response.confidence || 0,
completeness_score: 0,
coding_accuracy: 0,
},
};
}
return response;
}
}
// src/migration/ehr-migration.ts
interface EhrMigrationPlan {
sourceEhr: 'epic' | 'athena' | 'cerner' | 'eclinicalworks';
targetEhr: 'epic' | 'athena' | 'cerner' | 'eclinicalworks';
migrationDate: Date;
providerCount: number;
steps: MigrationStep[];
}
interface MigrationStep {
order: number;
name: string;
description: string;
rollbackable: boolean;
estimatedMinutes: number;
}
function generateMigrationPlan(source: string, target: string): EhrMigrationPlan {
return {
sourceEhr: source as any,
targetEhr: target as any,
migrationDate: new Date(),
providerCount: 0, // Set per org
steps: [
{ order: 1, name: 'Freeze new enrollments', description: 'Stop new provider enrollments on source EHR', rollbackable: true, estimatedMinutes: 5 },
{ order: 2, name: 'Export note templates', description: 'Export all custom note templates and SmartPhrases', rollbackable: true, estimatedMinutes: 30 },
{ order: 3, name: 'Configure target EHR', description: 'Set up FHIR endpoints and OAuth for target EHR', rollbackable: true, estimatedMinutes: 60 },
{ order: 4, name: 'Parallel run', description: 'Run both EHRs for 1 week — compare note output', rollbackable: true, estimatedMinutes: 10080 },
{ order: 5, name: 'Provider re-enrollment', description: 'Re-enroll providers on target EHR', rollbackable: true, estimatedMinutes: 120 },
{ order: 6, name: 'Cutover', description: 'Switch primary EHR integration to target', rollbackable: true, estimatedMinutes: 15 },
{ order: 7, name: 'Decommission source', description: 'Disable source EHR integration after 30-day soak', rollbackable: false, estimatedMinutes: 30 },
],
};
}
// src/migration/template-migration.ts
interface NoteTemplate {
id: string;
name: string;
specialty: string;
sections: string[];
smartPhrases: Record<string, string>; // Epic-specific
}
async function migrateTemplates(
sourceApi: any,
targetApi: any,
): Promise<{ migrated: number; failed: string[] }> {
const { data: templates } = await sourceApi.get('/note-templates');
const failed: string[] = [];
let migrated = 0;
for (const template of templates) {
try {
// Remove EHR-specific fields
const { smartPhrases, ...portable } = template;
await targetApi.post('/note-templates', {
...portable,
// Map SmartPhrases to target EHR equivalent if applicable
});
migrated++;
} catch (err) {
failed.push(template.id);
}
}
return { migrated, failed };
}
#!/bin/bash
# scripts/abridge-migration-rollback.sh
echo "=== Migration Rollback ==="
echo "Step 1: Revert FHIR endpoint to source EHR"
echo "Step 2: Re-enable source EHR Abridge module"
echo "Step 3: Notify providers of rollback"
echo "Step 4: Verify note generation on source EHR"
echo "=== Rollback Complete ==="
| Error | Cause | Solution |
|---|---|---|
| Template incompatible | EHR-specific fields | Strip EHR-specific data before migration |
| Provider enrollment fails | Credentials not migrated | Re-issue provider credentials on target |
| Note format mismatch | Different FHIR profiles | Map FHIR profiles between EHR systems |
For CI/CD pipeline setup, see abridge-ci-integration.