Backend Architecture
The Foundation/Lens backend runs entirely on Atlassian Forge — a serverless platform for Jira Cloud apps. There is no standalone server; all backend code executes as Forge resolver functions, event handlers, and async consumers within the Forge runtime.
Architecture Overview
Section titled “Architecture Overview” Forge Bridge (invoke) ┌──────────────────┐ ──────────────────────> ┌──────────────────────┐ │ Custom UI │ │ Resolver Registry │ │ (React SPA) │ <────────────────────── │ (src/index.ts) │ └──────────────────┘ JSON response └─────────┬────────────┘ │ ▼ ┌───────────────────────────┐ │ Resolver Handlers │ │ (src/resolvers/*.ts) │ └───────────┬───────────────┘ │ ┌────────────────────────┼──────────────────────┐ ▼ ▼ ▼ ┌──────────────────┐ ┌──────────────────┐ ┌───────────────────┐ │ Services Layer │ │ Forge SQL │ │ Forge KVS │ │ (src/services/) │ │ (MySQL 5.7) │ │ (Key-Value) │ └────────┬─────────┘ └──────────────────┘ └───────────────────┘ │ ▼ ┌──────────────────┐ │ Jira REST API │ │ (asUser/asApp) │ └──────────────────┘Design Principles
Section titled “Design Principles”- Server-Centric: The backend assembles complete views. The frontend is a thin rendering layer — it never fetches data from Jira directly.
- Issue Cache in Forge SQL: A local copy of Jira issue data, kept fresh by free product event triggers (
avi:jira:updated:issue). Users read from the cache, not from the Jira API. - Event-Driven Sync: Jira fires product events on issue/sprint/link changes. The backend catches these, fetches the updated issue (2 API points), and upserts the cache.
- Rate-Limit Aware: Every Jira API call goes through a KVS-backed point counter (
utils/rate-limiter.ts). Budget is checked before calls and refunded on failure.
Directory Structure
Section titled “Directory Structure”foundation/src/├── index.ts # Resolver registry -- ALL endpoints defined here├── db/│ ├── schema.ts # Fresh-install DDL + ensureSchema() orchestrator│ └── migrations.ts # Sequential migration runner (v2 -> v32)├── resolvers/ # API endpoint handlers (invoked via Forge bridge)│ ├── lenses.ts # Lens CRUD│ ├── hierarchy.ts # Tree operations (getLensView, add/move/delete nodes)│ ├── views.ts # View CRUD│ ├── inline-edit.ts # Field updates, transitions, priorities│ ├── sync-agents.ts # Sync agent (generator) CRUD + execution│ ├── permissions.ts # ACL management, user/group search│ ├── dependencies.ts # Issue link CRUD, dependency lag overrides│ ├── admin.ts # Admin stats, cache refresh, CSS overrides, reset│ ├── announcements.ts # Announcement popup dismissal (KVS)│ ├── ai.ts # Portfolio intelligence analysis│ ├── team.ts # Team CRUD + membership│ ├── holiday.ts # Holiday calendar management│ ├── absence.ts # Resource absence tracking│ ├── baseline.ts # Baseline snapshot CRUD│ ├── leveling.ts # Resource leveling jobs│ ├── skills.ts # Skill taxonomy + resource skill assignments│ ├── placeholder.ts # Placeholder resource management│ ├── resource-workload.ts # Resource workload analysis│ ├── cross-lens-workload.ts # Cross-lens workload aggregation│ ├── resource-matcher.ts # Best-match resource finder│ ├── gdpr.ts # GDPR data deletion + reporting│ ├── field-metadata.ts # Jira field discovery (standard + custom)│ ├── field-options.ts # Custom field option caching│ ├── job-status.ts # Async job polling│ ├── realtime.ts # Forge Realtime auth token│ ├── budget.ts # Rate limit budget status│ ├── search.ts # Issue search for link picker│ ├── sprint.ts # Sprint data fetching│ ├── preferences.ts # User preferences (KVS)│ ├── support-chat.ts # In-app support chat (KVS)│ ├── feedback.ts # In-app feedback widget│ ├── monitoring.ts # OPS monitoring dashboard│ ├── feature-flags.ts # Feature flag management│ ├── last-lens.ts # Last-visited lens tracking│ ├── init-context.ts # Lens context initialization│ ├── diff.ts # Incremental diff after sync│ ├── sync-freshness.ts # Sync agent freshness check│ ├── jql-validation.ts # JQL query validation│ ├── issue-context.ts # Issue context panel (lenses for an issue)│ ├── projects.ts # Project search + importable project listing│ ├── jpd.ts # Jira Product Discovery integration│ ├── import-projects.ts # Project-based hierarchy import│ ├── remove-project-import.ts # Remove imported project│ ├── bulk-add.ts # Bulk add issues by key│ ├── csv-import.ts # CSV/Excel import│ ├── structure-import.ts # Third-party hierarchy import│ ├── bigpicture-import.ts # Third-party PPM import│ ├── export-file-import.ts # DC-to-Cloud export file import│ ├── delete.ts # Delete issues from Jira│ ├── sync.ts # Manual sync trigger│ └── work-issue-hierarchy.ts # Work issue hierarchy resolution├── services/ # Business logic layer│ ├── hierarchy-engine.ts # Tree manipulation (gap=100 positioning)│ ├── cache-manager.ts # Issue cache CRUD (upsert with ON DUPLICATE KEY)│ ├── permission-service.ts # checkPermission(), group/role resolution│ ├── view-service.ts # View persistence + default view creation│ ├── sync-agent-service.ts # Sync agent persistence + execution queue│ ├── jira-sync.ts # Jira API fetch + custom field parsing│ ├── realtime.ts # Forge Realtime channel publish│ ├── lens-service.ts # Lens persistence│ ├── license-service.ts # Forge licensing checks│ ├── edition-service.ts # App edition detection│ ├── team-service.ts # Team persistence│ ├── holiday-service.ts # Holiday calendar persistence│ ├── absence-service.ts # Absence persistence│ ├── baseline-service.ts # Baseline snapshot persistence│ ├── skills-service.ts # Skill taxonomy persistence│ ├── resource-workload.ts # Workload calculation engine│ ├── cross-lens-workload.ts # Cross-lens workload aggregation│ ├── resource-leveling.ts # Resource leveling algorithm│ ├── resource-matcher.ts # Best-match scoring│ ├── capacity-calculator.ts # Capacity calculation with holidays/absences│ ├── timeline-scheduler.ts # Auto-scheduling engine│ ├── ai-service.ts # Portfolio intelligence analysis│ ├── auto-sync.ts # Auto-sync staleness detection│ ├── deploy-version.ts # Deploy-triggered cache refresh│ ├── error-reporter.ts # Sentry error forwarding│ ├── resolver-metrics.ts # Resolver performance tracking│ ├── monitoring-service.ts # Monitoring data aggregation│ ├── feedback-service.ts # Feedback persistence│ ├── feature-flags.ts # Feature flag persistence│ ├── csv-transform.ts # CSV data transformation│ ├── structure-client.ts # Structure API client│ ├── structure-transform.ts # Structure data transformation│ ├── bigpicture-client.ts # BigPicture API client│ └── bigpicture-transform.ts # BigPicture data transformation├── actions/ # Rovo agent action handlers (invoked via manifest actions)│ ├── add-issues.ts # Add issues by JQL│ ├── create-flex-item.ts # Create flex item│ ├── move-node.ts # Move node in hierarchy│ ├── update-field.ts # Bulk field updates│ ├── create-and-add-issue.ts # Create Jira issue + add to lens│ ├── execute-sync-agents.ts # Trigger sync agent execution│ ├── remove-nodes.ts # Remove nodes from hierarchy│ ├── orchestrate-lens.ts # Multi-step lens orchestration│ ├── build-wbs.ts # AI-driven WBS generation│ ├── analyze-section-gaps.ts # Gap analysis│ ├── get-summary.ts # Lens summary for Rovo│ ├── get-lens-context.ts # Lens context for Rovo│ ├── find-nodes.ts # Search nodes in hierarchy│ ├── bulk-move-nodes.ts # Bulk move nodes│ ├── lens-crud.ts # Lens CRUD for Rovo│ ├── sync-agent-crud.ts # Sync agent CRUD for Rovo│ ├── view-crud.ts # View CRUD for Rovo│ ├── view-perm-dispatch.ts # View/permission dispatcher│ ├── r1-dispatch.ts # hierMeta dispatcher│ ├── r2r5-dispatch.ts # R2R5 destructive operations dispatcher│ ├── r2r5-read-dispatch.ts # R2G read-only operations dispatcher│ └── ... # ~40+ additional action handlers├── events/ # Jira product event handlers│ ├── issue-handler.ts # Issue created/updated/deleted/linked -> cache sync│ ├── sprint-handler.ts # Sprint started/closed/updated -> cache sync│ ├── cache-integrity.ts # Hourly: stale cache re-fetch│ ├── daily-health-check.ts # Daily: health snapshots + alert evaluation│ ├── sync-agent-executor.ts # Async: sync agent JQL execution (15-min timeout)│ ├── leveling-executor.ts # Async: resource leveling│ ├── cache-refresh-executor.ts # Async: per-project cache refresh│ ├── import-executor.ts # Async: hierarchy import│ ├── wbs-executor.ts # Async: AI WBS generation│ ├── uninstall-handler.ts # App uninstall cleanup│ └── alert-engine.ts # Alert threshold evaluation└── utils/ ├── rate-limiter.ts # KVS-backed rate limit point counter ├── resolver-guards.ts # Composable auth/schema/permission middleware ├── kvs-lease.ts # Distributed locking via KVS ├── retry.ts # Retry with exponential backoff ├── retry-helpers.ts # Jira API retry wrapper ├── uuid.ts # UUID v4 generation ├── visible-issues.ts # Jira BROWSE permission checking ├── cost-model.ts # API point cost estimation ├── diff-accumulator.ts # Incremental diff builder ├── reset-recovery.ts # Reset-all-data crash recovery └── import-*.ts # Import utilitiesHow Resolvers Work
Section titled “How Resolvers Work”All resolvers are registered in src/index.ts using the defineResolver() helper:
function defineResolver( name: string, handler: (args: any) => Promise<any>, options?: { skipMetrics?: boolean })Each resolver receives { payload, context } where:
payload— parameters sent by the frontend viainvoke(name, payload)context.accountId— the current user’s Jira account IDcontext.cloudId— the Jira Cloud site ID
The frontend calls resolvers through the @forge/bridge invoke() function:
// Frontend (api/client.ts)const result = await invoke('getLensView', { lensId: '...' });
// Backend (src/resolvers/hierarchy.ts)export async function getLensViewHandler({ payload, context }) { const { lensId } = payload; // ... returns JSON-serializable response}Resolver Guards
Section titled “Resolver Guards”Composable middleware wrappers are defined in src/utils/resolver-guards.ts:
| Guard | Purpose |
|---|---|
withSchema | Ensures Forge SQL schema is initialized before handler runs |
withAuth | Requires context.accountId to exist |
withAdmin | Requires the user to be a Jira site admin |
withLensPermission(level) | Requires the user to have at least level permission on the lens |
withLicense | Checks that the app license is active |
Guards are composed with compose():
defineResolver('getAdminStats', compose(withSchema, withAdmin)(getAdminStatsHandler));defineResolver('listViews', compose(withSchema, withLensPermission('view'))(listViewsHandler));Metrics Wrapper
Section titled “Metrics Wrapper”Every resolver is automatically wrapped with withMetrics() (from services/resolver-metrics.ts) which records execution time to the monitoring_metrics table. Lightweight utility resolvers opt out with { skipMetrics: true }.
Error Handling Patterns
Section titled “Error Handling Patterns”Critical Forge bug (ECO-277): Never return bare undefined from resolvers — the frontend receives {} (truthy). Always return explicit null or { error: string }.
Standard error responses:
// Validation errorsreturn { error: 'lensId is required' };return { error: 'Name is required' };
// Permission denied (from permissionService.denyAccess())return { error: 'Permission denied' };
// Rate limit exhaustedreturn { error: 'budget_exhausted', remaining: status.remaining, resetsAt: status.resetsAt };
// License inactivereturn { error: 'license_inactive', message: '...' };
// Success responsesreturn { success: true };return { lenses: [...] };return { lens: { id, name, ... } };Storage Architecture
Section titled “Storage Architecture”The backend uses two storage systems:
Forge SQL (Relational Data)
Section titled “Forge SQL (Relational Data)”- MySQL 5.7 compatible, 1 GiB per installation
- 150 DML operations/second
- All hierarchical, relational, and cached data
- See Database Schema for full documentation
Forge KVS (Key-Value Store)
Section titled “Forge KVS (Key-Value Store)”- Simple key-value pairs, JSON-serializable values
- Used for: user preferences, rate limit counters, job status, dismissed announcements, session data, distributed locks (leases), feature flags, auto-sync timestamps
- Key naming convention:
namespace:identifier(e.g.,user_prefs:accountId,job:jobId,active-gen-job:lensId)
Cross-References
Section titled “Cross-References”- API Reference — every resolver documented with parameters, returns, and permissions
- Database Schema — full Forge SQL schema with all tables and migrations
- Services — business logic layer documentation
- Events & Async Actions — event handlers and async consumers
- Data Flow — end-to-end load, edit, and sync cycles