App Configuration
This document covers the Forge manifest, package structure, build tooling, Claude Code hooks, MCP configuration, and git worktree conventions.
Manifest (foundation/manifest.yml)
Section titled “Manifest (foundation/manifest.yml)”The manifest is the central configuration file for the Forge app. It defines modules, permissions, scopes, triggers, consumers, and resources.
App Identity
Section titled “App Identity”app: id: ari:cloud:ecosystem::app/650bca70-387f-4693-96fc-d85cd80a344f runtime: name: nodejs22.x licensing: enabled: true- App ID: The Marketplace-submitted app identifier
- Runtime: Node.js 22.x (Forge-managed)
- Licensing: Enabled for paid Marketplace listing (Tier 2 rate limits confirmed)
Modules
Section titled “Modules”| Module Type | Key(s) | Purpose |
|---|---|---|
jira:globalPage | fm | Main app page (blank layout, Custom UI) |
jira:issueContext | fic | Issue context side panel |
jira:adminPage | fad | Admin settings page |
function | r, ie, se, ci, sa, le, uh, f1-fw, g1-g3, dh, cr, ih, wb | Backend handler functions |
consumer | sae, lve, cre, ime, wbe | Async event queue consumers |
trigger | ic, iu, id, ilc, ild, spe, oau | Jira product event triggers |
action | 35 action entries | Rovo Agent action endpoints |
rovo:agent | agt | AI portfolio manager agent |
sql | main | Forge SQL database (MySQL engine) |
scheduledTrigger | cic, dht | Hourly cache integrity + daily health check |
Permissions & Scopes
Section titled “Permissions & Scopes”permissions: scopes: - read:jira-work # Read issues, projects, boards - write:jira-work # Update issues (inline editing) - read:jira-user # Read user profiles (assignee picker) - read:sprint:jira-software # Read sprint data - read:board-scope:jira-software # Read board scope - read:project:jira # Read project metadata - storage:app # Forge KVS + SQL access content: styles: - 'unsafe-inline' # Required for ADS CSS variables + AG Grid inline styles external: fetch: backend: - address: "*.ingest.sentry.io" # Error tracking inScopeEUD: falseResources
Section titled “Resources”resources: - key: mu # Main UI build output path: static/main/build - key: icu # Issue Context UI build output path: static/issue-context/build - key: au # Admin UI build output path: static/admin/build - key: ar # Rovo Agent prompt file path: resource/agentTriggers
Section titled “Triggers”The app subscribes to Jira product events for cache synchronization. These triggers are free (no rate limit cost).
| Trigger Key | Event | Handler | Purpose |
|---|---|---|---|
ic | avi:jira:created:issue | ie | New issue created |
iu | avi:jira:updated:issue | ie | Issue updated (ignoreSelf: true) |
id | avi:jira:deleted:issue | ie | Issue deleted |
ilc | avi:jira:created:issuelink | ie | Issue link created |
ild | avi:jira:deleted:issuelink | ie | Issue link deleted |
spe | avi:jira:started/closed/updated:sprint | se | Sprint state changes |
oau | avi:forge:uninstalled:app | uh | App uninstallation cleanup |
Async Consumers
Section titled “Async Consumers”Queued event consumers for long-running operations:
| Consumer | Queue | Handler | Timeout |
|---|---|---|---|
sae | generator-queue | sa (sync-agent-executor) | 900s |
lve | leveling-queue | le (leveling-executor) | 900s |
cre | cache-refresh-queue | cr (cache-refresh-executor) | 120s |
ime | import-queue | ih (import-executor) | 900s |
wbe | wbs-queue | wb (wbs-executor) | 900s |
Scheduled Triggers
Section titled “Scheduled Triggers”| Key | Handler | Interval | Purpose |
|---|---|---|---|
cic | ci (cache-integrity) | Hourly | Periodic cache cleanup and validation |
dht | dh (daily-health-check) | Daily | System health metrics (900s timeout) |
CaaS Manifest Byte Limit (ECO-1310)
Section titled “CaaS Manifest Byte Limit (ECO-1310)”The Forge CaaS (Container as a Service) manifest conversion has a hard byte size limit of approximately 257KB after internal conversion. The CaaS conversion inflates the raw manifest by 8-10x.
Current State
Section titled “Current State”- Raw manifest budget: 23,500 bytes
- Current size: ~22,850 bytes (35 actions)
- Headroom: ~650 bytes
Slim Manifest Convention
Section titled “Slim Manifest Convention”To minimize bytes, the manifest uses a compressed format:
| Element | Convention | Example |
|---|---|---|
| Function keys | 2-char identifiers | f1, f2, g1 |
| Module/trigger/consumer keys | 2-3 char abbreviations | fm, ic, sae |
| Action descriptions | Single dot | description: . |
| Input titles | Single char | title: x |
Input required: false | Omitted (defaults to false) | — |
Action title: field | Removed (optional) | — |
Checking the Budget
Section titled “Checking the Budget”Before adding any new action to the manifest:
cd foundation && npm run check:manifestOutput example:
[manifest-check] manifest-bytes=22850/23500[manifest-check] functions=42/50[manifest-check] action-modules=35[manifest-check] rovo-agent:agt=actions:33[manifest-check] OKDispatcher Pattern
Section titled “Dispatcher Pattern”To fit more functionality within the byte budget, related actions are routed through dispatcher functions using an _action parameter:
| Dispatcher | actionVerb | Routed Actions |
|---|---|---|
hierMeta | TRIGGER | getNodeDetails, getSubtreeStats, groupNodesBy, sortNodes, renameFlexItem, flattenSubtree, mergeFlexItems |
viewPerm | TRIGGER | deleteView, grantPermission, revokePermission |
jiraUtil | GET | validateJql, analyzeJiraProject, createFromTemplate |
r2g | GET | 19 read-only intelligence/analytics/alerts/onboarding actions |
r2r5 | TRIGGER | 14 destructive operations/automation actions |
Package Structure
Section titled “Package Structure”foundation-1/ # Repository root├── foundation/ # Forge app root│ ├── manifest.yml # Forge app configuration│ ├── package.json # Backend dependencies + scripts│ ├── .npmrc # legacy-peer-deps=true│ ├── tsconfig.json # Backend TypeScript config│ ├── src/ # Backend source (resolvers, services, events, actions)│ ├── scripts/ # Build, deploy, and verification scripts│ └── static/│ ├── main/ # Main UI (React + AG Grid + dnd-kit)│ │ ├── package.json # Frontend dependencies│ │ ├── .npmrc # legacy-peer-deps=true│ │ └── src/ # React components, hooks, utils│ ├── issue-context/ # Issue Context panel UI│ │ ├── package.json│ │ └── .npmrc│ └── admin/ # Admin page UI│ ├── package.json│ └── .npmrc├── docs/ # Design docs, plans, mockups, research├── research/ # PRD, competitive research├── documentation/ # Generated documentation (this directory)└── .claude/ # Claude Code configuration ├── settings.json # Hook definitions ├── hooks/ # Hook scripts └── worktrees/ # Git worktree working directoriesnpm Scripts (Backend foundation/package.json)
Section titled “npm Scripts (Backend foundation/package.json)”| Script | Command | Purpose |
|---|---|---|
test | jest | Run backend Jest test suite |
test:watch | jest --watch | Watch mode for backend tests |
check:manifest | node scripts/check-manifest-limits.js | Validate manifest byte budget |
predeploy:check | check:manifest && generate-deploy-hash && tsc --noEmit | Pre-deploy validation |
verify:local | bash scripts/verify-local.sh --full | Full CI-equivalent local verification |
verify:quick | bash scripts/verify-local.sh --quick | Quick pre-push checks |
lint | forge lint | Forge manifest linting |
deploy | bash scripts/deploy.sh | Smart deploy wrapper |
deploy:dev | bash scripts/deploy.sh --environment dev | Deploy to dev environment |
deploy:production | bash scripts/deploy.sh --environment production | Deploy to production |
release:production:local | bash scripts/release-production.sh | Full production release pipeline |
setup:env | bash scripts/setup-env.sh | Environment variable setup guide |
npm Scripts (Frontend foundation/static/main/package.json)
Section titled “npm Scripts (Frontend foundation/static/main/package.json)”| Script | Command | Purpose |
|---|---|---|
start | react-scripts start | Development server (rarely used — Forge tunnel preferred) |
build | react-scripts build | Production build for deployment |
test | node scripts/test.js | Run frontend Jest test suite |
Claude Code Hooks
Section titled “Claude Code Hooks”Hooks are configured in .claude/settings.json and enforce safety rules during Claude Code sessions.
Hook Configuration
Section titled “Hook Configuration”{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "bash \"$(cd \"$(git rev-parse --git-common-dir)/..\" && pwd)/.claude/hooks/protect-worktrees.sh\"" }, { "type": "command", "command": "bash \"$(cd \"$(git rev-parse --git-common-dir)/..\" && pwd)/.claude/hooks/setup-worktree.sh\"" } ] } ], "PostToolUse": [ { "matcher": "EnterWorktree", "hooks": [ { "type": "command", "command": "bash \"$(cd \"$(git rev-parse --git-common-dir)/..\" && pwd)/.claude/hooks/setup-worktree.sh\"" } ] } ] }}protect-worktrees.sh (PreToolUse: Bash)
Section titled “protect-worktrees.sh (PreToolUse: Bash)”Blocks all worktree deletion commands. Claude is prohibited from:
rmtargeting.claude/worktrees/pathsgit worktree removegit worktree prunegit branch -D/-donworktree-*branchesgh pr merge --delete-branch
Worktree cleanup is the user’s responsibility.
setup-worktree.sh (PreToolUse: Bash + PostToolUse: EnterWorktree)
Section titled “setup-worktree.sh (PreToolUse: Bash + PostToolUse: EnterWorktree)”Creates node_modules symlinks from worktree packages to main tree packages. This avoids rerunning npm install in every worktree.
Behavior:
- Main tree: Exits immediately (no-op)
- Worktree: For each of the four packages, checks if
node_modulesexists:- Real directory: Skips (already installed)
- Valid symlink to main tree: Skips
- Stale or wrong-target symlink: Replaces with correct symlink
- Missing: Creates symlink to main tree’s
node_modules - Main tree
node_modulesmissing: Falls back tonpm ci --prefer-offline
Hook Path Resolution
Section titled “Hook Path Resolution”Hook paths use $(cd "$(git rev-parse --git-common-dir)/.." && pwd) to always resolve to the main tree root. This works correctly in both the main tree and worktrees. The previous approach (--show-toplevel) returned the worktree root in worktrees, breaking hook resolution.
Applying Hook Changes
Section titled “Applying Hook Changes”Hook changes require starting a new Claude Code session to take effect.
MCP Configuration (.mcp.json)
Section titled “MCP Configuration (.mcp.json)”The project uses Atlassian’s Forge MCP server for in-context Forge documentation:
{ "mcpServers": { "forge": { "type": "http", "url": "https://mcp.atlassian.com/v1/forge/mcp" } }}This provides tools for searching Forge documentation, querying manifest guides, Atlassian Design System tokens, and module references during development.
Git Worktree Conventions
Section titled “Git Worktree Conventions”Branching Rules
Section titled “Branching Rules”- Main tree: Always work directly on
mainunless explicitly asked to create a branch. - Worktree sessions: Commit to the worktree’s branch, not to
main. Open a PR to merge back. - After merging a PR: Always
git pull origin mainin the main working tree.
Worktree Safety Rules
Section titled “Worktree Safety Rules”- Never deploy from a worktree.
forge deployandforge tunnelmust only run frommainin the main working tree. - Never delete worktrees or worktree branches. No
rmon worktree dirs, nogit worktree remove/prune, nogit branch -D worktree-*, nogh pr merge --delete-branch. - The
protect-worktrees.shhook enforces these rules automatically.
node_modules in Worktrees
Section titled “node_modules in Worktrees”- Worktree
node_modulesare symlinks to the main tree (created bysetup-worktree.sh). node_modulessymlinks must never be tracked by git. The.gitignoreusesnode_modules(no trailing slash) to match both directories and symlinks.- The main tree’s
node_modulesmust always be a real directory (fromnpm install), never a symlink. - If the project is moved to a new path, existing worktree symlinks may be stale. The
setup-worktree.shhook detects and re-links these automatically.
.gitignore Key Entries
Section titled “.gitignore Key Entries”.DS_Storenode_modules # No trailing slash -- matches both dirs and symlinks.env.worktrees/.claude/worktrees/.claude/projects/.claude/todos/.claude/*.md.obsidian/The node_modules entry (without trailing slash) is critical. A trailing slash (node_modules/) only matches directories, not symlinks — and git treats symlinks as files. This caused a previous bug where worktree symlinks were tracked by git.