forge-app-review
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseForge App Review
Forge应用审查
Deep pre-deploy review of Forge apps across Security, Architecture, Cost, Performance, and Triggers & Scheduling. Produces a severity-sorted issue list with actionable fixes.
对Forge应用进行深度预部署审查,涵盖安全、架构、成本、性能以及触发器与调度五大维度。生成按严重程度排序的问题列表,并提供可执行的修复方案。
Forge Pricing Reference
Forge定价参考
Forge uses a consumption-based pricing model. Charges only apply above free monthly allowances. Use this table to assess cost impact of findings:
| Capability | Billing Unit | Free Monthly Allowance | Overage Price (USD) |
|---|---|---|---|
| Functions: Duration | GB-seconds | 100,000 GB-seconds | $0.000025 / GB-second |
| KVS: Reads | GB read | 0.1 GB | $0.055 / GB |
| KVS: Writes | GB written | 0.1 GB | $1.090 / GB |
| Logs: Writes | GB written | 1 GB | $1.005 / GB |
| SQL: Compute duration | Hours | 1 hour | $0.143 / hour |
| SQL: Compute requests | Per 1M requests | 100,000 requests | $1.929 / 1M requests |
| SQL: Data stored | GB-hours | 730 GB-hours | $0.00076850 / GB-hour |
Key cost insight: KVS writes are ~20× more expensive than reads. Logging is ~$1/GB over the free tier. The cost formula for functions is: GB-seconds = (memoryMiB ÷ 1024) × duration in seconds.
Free capabilities (not billed): UI modules (UI Kit and Custom UI frontends run in the browser), Jira expressions, Forge Remote invocations (though the remote function runtime is billed), entity properties (stored by the product, not by Forge Storage).
Forge采用基于用量的定价模式,仅在超出每月免费额度时产生费用。使用下表评估审查发现的成本影响:
| 功能项 | 计费单位 | 每月免费额度 | 超额价格(美元) |
|---|---|---|---|
| Functions: Duration | GB-seconds | 100,000 GB-seconds | $0.000025 / GB-second |
| KVS: Reads | GB read | 0.1 GB | $0.055 / GB |
| KVS: Writes | GB written | 0.1 GB | $1.090 / GB |
| Logs: Writes | GB written | 1 GB | $1.005 / GB |
| SQL: Compute duration | Hours | 1 hour | $0.143 / hour |
| SQL: Compute requests | Per 1M requests | 100,000 requests | $1.929 / 1M requests |
| SQL: Data stored | GB-hours | 730 GB-hours | $0.00076850 / GB-hour |
关键成本洞察:KVS写入成本约为读取的20倍。超出免费额度后,日志写入成本约为1美元/GB。函数成本计算公式为:GB-seconds = (memoryMiB ÷ 1024) × 运行时长(秒)。
免费功能(不计费):UI模块(UI Kit和Custom UI前端在浏览器中运行)、Jira表达式、Forge Remote调用(但远程函数运行时会计费)、实体属性(由产品存储,不占用Forge Storage配额)。
Execution Mandate
执行要求
When triggered, immediately:
- Read — this is the source of truth for permissions, modules, egress, triggers, scheduled triggers, and function memory settings
manifest.yml - Read — check dependencies, versions, scripts
package.json - Scan all resolver files in — check patterns, error handling, data flow, API call patterns (N+1, missing fields, sequential calls), logging verbosity
src/ - Scan UI code (Custom UI or UI Kit) — check component patterns, bridge usage, whether API calls and logic could be moved to the frontend, product context usage, invoke patterns (chatty, per-render)
- Check for Forge Storage / Entity Store usage patterns — TTL strategy, write frequency, query vs iteration, entity properties vs KVS
- Check trigger and scheduling configuration — frequency, filtering, ignoreSelf, early exit, polling vs event-driven
- Check for Forge Remote usage or opportunities — compute offloading, trade-offs
- Compile all findings into a severity-sorted issue list
Do NOT ask the user what to review. Review everything. Do NOT modify any code unless explicitly asked. Do NOT skip categories — even if the app looks clean, confirm it explicitly.
触发审查后,立即执行以下步骤:
- 读取— 这是权限、模块、出站访问、触发器、调度触发器以及函数内存设置的权威来源
manifest.yml - 读取— 检查依赖项、版本、脚本
package.json - 扫描目录下所有resolver文件 — 检查代码模式、错误处理、数据流、API调用模式(N+1问题、缺失字段、顺序调用)、日志冗余度
src/ - 扫描UI代码(Custom UI或UI Kit) — 检查组件模式、bridge使用方式、API调用和逻辑是否可迁移至前端、产品上下文使用、invoke模式(频繁调用、每次渲染调用)
- 检查Forge Storage / Entity Store使用模式 — TTL策略、写入频率、查询与迭代对比、实体属性与KVS选择
- 检查触发器和调度配置 — 频率、过滤规则、ignoreSelf设置、提前退出机制、轮询与事件驱动对比
- 检查Forge Remote的使用情况或适用场景 — 计算卸载、权衡利弊
- 将所有审查发现整理为按严重程度排序的问题列表
请勿询问用户需要审查哪些内容,需全面审查所有维度。除非用户明确要求,否则请勿修改任何代码。请勿跳过任何分类 — 即使应用看起来无问题,也需明确确认。
Review Process
审查流程
Step 1: Manifest Analysis (manifest.yml
)
manifest.yml步骤1:Manifest分析(manifest.yml
)
manifest.ymlRead the manifest first. Extract:
- Permissions/scopes — list all and
scopesentriespermissions - Modules — list all module types and their function key references
- Egress — check or
app.connect.remotesURLspermissions.external.fetch.backend - Environment variables — check for or environment variable declarations
app.storage
Cross-reference every key in modules against actual resolver calls.
functionresolver.define()优先读取manifest文件,提取以下信息:
- 权限/范围 — 列出所有和
scopes条目permissions - 模块 — 列出所有模块类型及其关联的function密钥
- 出站访问 — 检查或
app.connect.remotes中的URLpermissions.external.fetch.backend - 环境变量 — 检查或环境变量声明
app.storage
将模块中的每个密钥与实际resolver的调用进行交叉验证。
functionresolver.define()Step 2: Dependencies (package.json
)
package.json步骤2:依赖项检查(package.json
)
package.jsonCheck:
- Node.js engine compatibility (Forge requires Node 18.x+)
- Unnecessary large dependencies (e.g., when only one function is used,
lodashinstead of native Date ormoment)dayjs - Missing ,
@forge/api, or@forge/bridgedepending on app type@forge/ui - Dev dependencies leaking into production
- Outdated packages
@forge/*
检查以下内容:
- Node.js引擎兼容性(Forge要求Node 18.x+)
- 不必要的大型依赖项(例如仅使用一个函数却引入,使用
lodash而非原生Date或moment)dayjs - 根据应用类型,是否缺失、
@forge/api或@forge/bridge依赖@forge/ui - 开发依赖项泄漏至生产环境
- 包版本过时
@forge/*
Step 3: Resolver Code
步骤3:Resolver代码检查
Read all files that contain or imports. Check for:
resolver.defineResolver- Error handling patterns
- API call patterns (,
requestJira,requestConfluence,api.asUser())api.asApp() - Data validation and sanitization
- Storage operations
- External fetch calls
读取所有包含或导入的文件,检查:
resolver.defineResolver- 错误处理模式
- API调用模式(、
requestJira、requestConfluence、api.asUser())api.asApp() - 数据验证与清理
- 存储操作
- 外部fetch调用
Step 4: UI Code
步骤4:UI代码检查
For UI Kit apps — scan for imports, component usage, hooks.
For Custom UI apps — scan for usage, calls, CSP compliance.
@forge/react@forge/bridgeinvoke()In both cases, check for:
- Frontend offloading opportunities: Are there resolvers that only do read-only /
requestJira()calls that could instead userequestConfluence()directly from the browser?@forge/bridge - Product context via resolver: Is the app invoking a resolver just to get issue/project/space key? Use (UI Kit) or
useProductContext()(Custom UI) instead.view.getContext() - Invoke on every render: Is called without proper
invoke()with empty dependency array, causing re-invocation on every render?useEffect - Client-side logic: Is data formatting, sorting, filtering, or validation done in a resolver when it could run in the browser for free?
对于UI Kit应用 — 扫描导入、组件使用、钩子函数。
对于Custom UI应用 — 扫描使用、调用、CSP合规性。
@forge/react@forge/bridgeinvoke()两种场景下均需检查:
- 前端卸载机会:是否存在仅执行只读/
requestJira()调用的resolver,可改为直接在浏览器中使用requestConfluence()?@forge/bridge - 通过resolver获取产品上下文:是否仅为获取issue/project/space密钥而调用resolver?UI Kit应用应使用,Custom UI应用应使用
useProductContext()替代。view.getContext() - 每次渲染调用invoke:是否在未添加空依赖数组的中调用
useEffect,导致每次渲染都重新调用?invoke() - 客户端逻辑:数据格式化、排序、过滤或验证是否在resolver中执行,而这些操作本可在浏览器中免费运行?
Step 5: Storage Analysis
步骤5:存储分析
Search for usage of:
- ,
storage.get,storage.set— check for unnecessary writes, short TTLs (KVS writes are ~20× more expensive than reads), and missing caching patternsstorage.delete - (Entity Store) — check for proper use of indexes,
storage.query, and.where()instead of fetching all items and filtering in code.limit() - Entity properties (to
requestJira) — note these are free and stored by the product (not Forge Storage quota), suitable for small per-entity metadata (max 32 KB per property). Good for flags, markers, timestamps attached to Jira issues or Confluence pages. Queryable via JQL for Jira entity properties. Not suitable for sensitive data — visible to other apps and users via REST API./properties/ - Cache patterns — is app-level data that rarely changes (e.g., custom field IDs, project configs, workflow statuses) being fetched from APIs on every invocation? Should be cached in KVS with a TTL (1 hour+ preferred to minimize writes)
- Write amplification — a 1-minute TTL cache with 100 calls/hour causes ~60 writes/hour; a 1-hour TTL causes ~1 write/hour at ~60× less cost
搜索以下用法:
- 、
storage.get、storage.set— 检查不必要的写入、过短的TTL(KVS写入成本约为读取的20倍)、缺失的缓存模式storage.delete - (Entity Store) — 检查是否正确使用索引、
storage.query和.where(),而非获取所有数据后在代码中过滤.limit() - 实体属性(通过调用
requestJira) — 注意这些是免费的,由产品存储(不占用Forge Storage配额),适用于小型实体元数据(每个属性最大32 KB)。适合存储Jira问题或Confluence页面的标记、状态、时间戳。Jira实体属性可通过JQL查询。不适用于敏感数据 — 其他应用和用户可通过REST API查看。/properties/ - 缓存模式 — 对于极少变化的应用级数据(例如自定义字段ID、项目配置、工作流状态),是否每次调用都从API获取?应缓存到KVS并设置TTL(推荐1小时以上,以减少写入次数)
- 写入放大 — 1分钟TTL的缓存每小时被调用100次,会产生约60次写入;1小时TTL的缓存每小时仅产生约1次写入,成本降低约60倍
Step 6: Trigger & Scheduling Analysis
步骤6:触发器与调度分析
Check the manifest for and modules:
scheduledTriggertrigger- Scheduled triggers: Is the interval appropriate? (is rarely justified — prefer
fiveMinutes,hour, orday)week - Polling vs events: Is a scheduled trigger polling for changes that could be caught by a product event trigger?
- Event filtering: Do product event triggers have to limit invocations to relevant events?
filter.expression - ignoreSelf: If the app writes to entities and listens to events on those entities, is set? (Jira only)
filter.ignoreSelf: true - Early exit: Do trigger handler functions check for work to do before running expensive operations?
- External polling: Could scheduled triggers polling external services be replaced with web triggers?
检查manifest中的和模块:
scheduledTriggertrigger- 调度触发器:时间间隔是否合理?(很少有必要 — 优先选择
fiveMinutes、hour或day)week - 轮询vs事件驱动:调度触发器是否在轮询变化,而这些变化可通过产品事件触发器捕获?
- 事件过滤:产品事件触发器是否设置以限制仅调用相关事件?
filter.expression - ignoreSelf:如果应用写入实体并监听这些实体的事件,是否设置了?(仅适用于Jira)
filter.ignoreSelf: true - 提前退出:触发器处理函数是否在执行昂贵操作前检查是否有实际工作要做?
- 外部轮询:调度触发器轮询外部服务是否可替换为web触发器?
Step 7: Forge Remote Analysis
步骤7:Forge Remote分析
Check if the app uses Forge Remote ( section in manifest):
remotes:- If present, note that Forge Remote offloads compute to an external backend — the Forge function is not executed for those calls, saving FaaS invocations. But the app loses "Runs on Atlassian" eligibility.
- If not present, check whether the app would benefit from Forge Remote:
- Compute-intensive operations (ML inference, image processing, complex report generation)
- Long-running operations that approach the 25-second function timeout
- Existing backend services the app duplicates logic from
- Large-scale storage needs exceeding Forge Storage limits
- Note: For most apps, staying on-platform is simpler. Only recommend Forge Remote when there's a genuine need.
检查应用是否使用Forge Remote(manifest中的部分):
remotes:- 如果已使用,需注意Forge Remote将计算卸载至外部后端 — Forge函数不会为这些调用执行,节省FaaS调用次数,但应用将失去「Runs on Atlassian」资格。
- 如果未使用,检查应用是否可从Forge Remote获益:
- 计算密集型操作(ML推理、图像处理、复杂报告生成)
- 接近25秒函数超时的长时间运行操作
- 应用重复了现有后端服务的逻辑
- 存储需求超出Forge Storage限制
- 注意:对于大多数应用,留在平台内更简单。仅当确实有需求时才推荐使用Forge Remote。
Step 8: Compile Findings
步骤8:整理审查发现
Produce a single issue list sorted: Critical → Warning → Info.
生成单一问题列表,按严重→警告→信息排序。
Security Checks
安全检查
| ID | Check | Severity | What to Look For |
|---|---|---|---|
| SEC-01 | Overly broad scopes | Critical | |
| SEC-02 | Missing egress restrictions | Critical | External |
| SEC-03 | Hardcoded secrets | Critical | API keys, tokens, passwords, or credentials in source code instead of using Forge environment variables ( |
| SEC-04 | Missing input sanitization | Critical | User-provided data passed directly to API calls, storage keys, or rendered in Custom UI without sanitization. SQL/NoSQL injection patterns in storage queries. |
| SEC-05 | Unsafe Custom UI CSP | Warning | |
| SEC-06 | Missing auth checks | Warning | Resolvers that don't verify user context before performing write operations. Missing |
| SEC-07 | Sensitive data in storage | Warning | PII, tokens, or credentials stored in Forge Storage without encryption or with overly broad access. |
| SEC-08 | Excessive permissions | Warning | |
| SEC-09 | Classic scopes used | Info | Using classic (coarse-grained) scopes like |
| SEC-10 | Unnecessary asApp() usage | Warning | Using |
| ID | 检查项 | 严重程度 | 检查要点 |
|---|---|---|---|
| SEC-01 | 权限范围过宽 | 严重 | 仅需 |
| SEC-02 | 缺失出站访问限制 | 严重 | Resolver中存在外部 |
| SEC-03 | 硬编码密钥 | 严重 | 源代码中包含API密钥、令牌、密码或凭据,而非使用Forge环境变量( |
| SEC-04 | 缺失输入清理 | 严重 | 用户提供的数据直接传入API调用、存储密钥或在Custom UI中渲染,未做清理。存储查询中存在SQL/NoSQL注入模式。 |
| SEC-05 | 不安全的Custom UI CSP | 警告 | Custom UI资源配置中存在 |
| SEC-06 | 缺失身份验证检查 | 警告 | Resolver在执行写入操作前未验证用户上下文。缺失 |
| SEC-07 | 存储中包含敏感数据 | 警告 | Forge Storage中存储了PII、令牌或凭据,未加密或访问权限过宽。 |
| SEC-08 | 权限过度 | 警告 | |
| SEC-09 | 使用经典权限范围 | 信息 | 使用 |
| SEC-10 | 不必要的asApp()使用 | 警告 | UI触发的resolver中使用 |
Architecture Checks
架构检查
| ID | Check | Severity | What to Look For |
|---|---|---|---|
| ARC-01 | Function key mismatch | Critical | |
| ARC-02 | Monolithic resolver | Warning | Single resolver file with 5+ |
| ARC-03 | Missing error handling | Warning | |
| ARC-04 | Incorrect API usage | Warning | Using |
| ARC-05 | Module type mismatch | Warning | Using a |
| ARC-06 | Missing resolver validation | Warning | Resolver accepts payload from UI but doesn't validate shape/types before processing. |
| ARC-07 | Poor code organization | Info | All code in a single file. No separation between API logic, business logic, and data access. |
| ARC-08 | Unused modules | Info | Modules defined in |
| ARC-09 | Missing TypeScript | Info | JavaScript used instead of TypeScript. TypeScript catches many issues at build time. |
| ARC-10 | Forge Remote trade-offs not considered | Info | App uses Forge Remote ( |
| ID | 检查项 | 严重程度 | 检查要点 |
|---|---|---|---|
| ARC-01 | Function密钥不匹配 | 严重 | |
| ARC-02 | 单体Resolver | 警告 | 单个resolver文件包含5个以上 |
| ARC-03 | 缺失错误处理 | 警告 | |
| ARC-04 | API使用错误 | 警告 | 应使用 |
| ARC-05 | 模块类型不匹配 | 警告 | 使用 |
| ARC-06 | 缺失Resolver验证 | 警告 | Resolver接受UI传来的负载,但在处理前未验证其格式/类型。 |
| ARC-07 | 代码组织混乱 | 信息 | 所有代码都在单个文件中。未分离API逻辑、业务逻辑和数据访问层。 |
| ARC-08 | 未使用的模块 | 信息 | |
| ARC-09 | 未使用TypeScript | 信息 | 使用JavaScript而非TypeScript。TypeScript可在构建时捕获许多问题。 |
| ARC-10 | 未考虑Forge Remote的权衡 | 信息 | 应用使用了Forge Remote(manifest中的 |
Cost Checks
成本检查
| ID | Check | Severity | What to Look For |
|---|---|---|---|
| CST-01 | Chatty resolvers | Warning | UI making multiple |
| CST-02 | No pagination / missing maxResults | Warning | Product API calls (e.g., search issues, get pages) without |
| CST-03 | Unnecessary storage ops | Warning | Reading the same storage key multiple times in a single invocation. Writing to storage on every invocation when data hasn't changed. KVS writes are ~20× more expensive than reads — minimize write frequency. Short TTL caches (e.g., 1-minute TTL) cause excessive writes; prefer longer TTLs (1 hour+) where data allows. |
| CST-04 | Bloated bundle | Warning | Dependencies that significantly increase bundle size: |
| CST-05 | Redundant API calls | Warning | Fetching the same data from product APIs multiple times in one resolver execution. Cache results in variables. |
| CST-06 | Logic in resolver that could run client-side | Warning | Data formatting, sorting, filtering, validation, or transformation done in a resolver when it could run in the browser for free. UI Kit and Custom UI frontends run entirely in the browser and are not subject to function invocation costs. Look for resolvers that only reshape data — move that logic to the frontend. |
| CST-07 | Resolver used for product context | Warning | Invoking a resolver just to get the current issue key, project key, or space key. UI Kit apps should use |
| CST-08 | API calls via resolver instead of bridge | Warning | Using a Forge resolver/function to make read-only |
| CST-09 | Resolver called on every render | Warning | Calling |
| CST-10 | N+1 API calls | Warning | Fetching a list of items then making a separate API call for each item to get details. Use bulk endpoints instead: |
| CST-11 | Missing field selection on API calls | Warning | API calls (especially search/list endpoints) that don't specify a |
| CST-12 | Verbose logging in hot paths | Warning | |
| CST-13 | Large resolver payloads | Info | Resolver returning more data than the UI needs. Trim response objects to only include fields the UI consumes. |
| CST-14 | Unused dependencies | Info | Packages in |
| CST-15 | Memory over-provisioning | Info | Function |
| CST-16 | Entity properties not used for free storage | Info | App stores small per-entity metadata (flags, timestamps, status markers) in Forge KVS when Jira entity properties or Confluence content properties could be used instead. Entity properties are free (stored by the product, no Forge Storage quota), travel with the entity during export/import, and Jira entity properties are queryable via JQL. Max 32 KB per property. Not suitable for sensitive data (visible via REST API). |
| CST-17 | Pre-filtering not pushed to API layer | Warning | Resolver fetches all items from an API and filters in code (e.g., fetching all issues then checking status in a loop). Push filtering to the API layer: use JQL conditions ( |
| CST-18 | Polling external service instead of web trigger | Info | Scheduled trigger polls an external third-party service for updates. Consider replacing with a Forge web trigger — register the web trigger URL as a webhook with the external service so it calls your app only when something changes. Web trigger invocations have no flagfall or network cost, though function runtime is still billed. Eliminates all empty polling invocations. |
| ID | 检查项 | 严重程度 | 检查要点 |
|---|---|---|---|
| CST-01 | 频繁调用的Resolver | 警告 | UI在加载时多次调用 |
| CST-02 | 无分页/缺失maxResults | 警告 | 产品API调用(例如搜索问题、获取页面)未设置 |
| CST-03 | 不必要的存储操作 | 警告 | 单次调用中多次读取相同存储密钥。数据未变化时却每次调用都写入存储。KVS写入成本约为读取的20倍 — 尽量减少写入频率。短TTL缓存(例如1分钟TTL)会导致过多写入;数据允许时优先选择更长的TTL(1小时以上)。 |
| CST-04 | 臃肿的包 | 警告 | 显著增加包大小的依赖项: |
| CST-05 | 冗余API调用 | 警告 | 单个resolver执行中多次从产品API获取相同数据。将结果缓存到变量中。 |
| CST-06 | Resolver中执行可客户端化的逻辑 | 警告 | 数据格式化、排序、过滤、验证或转换在resolver中执行,而这些操作本可在浏览器中免费运行。UI Kit和Custom UI前端完全在浏览器中运行,不受函数调用成本限制。寻找仅重塑数据的resolver — 将此类逻辑迁移至前端。 |
| CST-07 | 使用Resolver获取产品上下文 | 警告 | 仅为获取当前issue密钥、项目密钥或空间密钥而调用resolver。UI Kit应用应使用 |
| CST-08 | 通过Resolver而非bridge调用API | 警告 | 使用Forge resolver/函数执行只读 |
| CST-09 | 每次渲染调用Resolver | 警告 | 在组件渲染体中或未添加空依赖数组的 |
| CST-10 | N+1 API调用 | 警告 | 获取项目列表后,为每个项目单独调用API获取详情。使用批量端点替代:问题使用 |
| CST-11 | API调用未指定字段 | 警告 | API调用(尤其是搜索/列表端点)未指定 |
| CST-12 | 高频路径中的冗余日志 | 警告 | 在高频函数(例如产品事件触发器或热门resolver)中使用 |
| CST-13 | Resolver返回过大Payload | 信息 | Resolver返回的数据超出UI实际需求。裁剪响应对象,仅保留UI需要的字段。 |
| CST-14 | 未使用的依赖项 | 信息 | |
| CST-15 | 内存过度配置 | 信息 | manifest中设置的函数 |
| CST-16 | 未使用免费的实体属性存储 | 信息 | 应用将小型实体元数据(标记、时间戳、状态标记)存储在Forge KVS中,而本可使用Jira实体属性或Confluence内容属性替代。实体属性是免费的(由产品存储,不占用Forge Storage配额),在导出/导入时随实体迁移,且Jira实体属性可通过JQL查询。每个属性最大32 KB。不适用于敏感数据(可通过REST API查看)。 |
| CST-17 | 未将预过滤推至API层 | 警告 | Resolver从API获取所有项目后在代码中过滤(例如获取所有问题后在循环中检查状态)。将过滤推至API层:使用JQL条件( |
| CST-18 | 轮询外部服务而非使用web触发器 | 信息 | 调度触发器轮询外部第三方服务获取更新。考虑替换为Forge web触发器 — 将web触发器URL注册为外部服务的webhook,仅当发生变化时才调用应用。web触发器调用无额外成本,仅函数运行时计费。可消除所有空轮询调用。 |
Trigger & Scheduling Checks
触发器与调度检查
| ID | Check | Severity | What to Look For |
|---|---|---|---|
| TRG-01 | Excessive scheduled trigger frequency | Warning | Scheduled triggers using |
| TRG-02 | Polling instead of event triggers | Warning | Scheduled triggers that poll for changes (e.g., checking if issues were updated) instead of using product event triggers ( |
| TRG-03 | Missing trigger event filters | Warning | Product event triggers without a |
| TRG-04 | Missing ignoreSelf on triggers | Warning | App that writes to Jira entities AND listens to events on those same entities, without |
| TRG-05 | No early exit in trigger handler | Info | Trigger handler functions that don't check whether there is real work to do before performing expensive operations. Add a lightweight guard at the top of the function (e.g., check a timestamp, check event fields) and return early if no action is needed. |
| TRG-06 | Polling external service instead of web trigger | Info | Scheduled trigger polling an external service for updates. Consider replacing with a Forge web trigger — register the web trigger URL as a webhook with the external service so it calls you only when something changes. |
| ID | 检查项 | 严重程度 | 检查要点 |
|---|---|---|---|
| TRG-01 | 调度触发器频率过高 | 警告 | 调度触发器使用 |
| TRG-02 | 使用轮询而非事件触发器 | 警告 | 调度触发器轮询变更(例如检查问题是否更新),而本可使用产品事件触发器( |
| TRG-03 | 缺失触发器事件过滤 | 警告 | 产品事件触发器在manifest中未设置 |
| TRG-04 | 触发器缺失ignoreSelf设置 | 警告 | 应用写入Jira实体并监听这些实体的事件,但manifest中未设置 |
| TRG-05 | 触发器处理函数无提前退出 | 信息 | 触发器处理函数在执行昂贵操作前未检查是否有实际工作要做。在函数顶部添加轻量级守卫(例如检查时间戳、检查事件字段),无需操作时提前返回。 |
| TRG-06 | 轮询外部服务而非使用web触发器 | 信息 | 调度触发器轮询外部服务获取更新。考虑替换为Forge web触发器 — 将web触发器URL注册为外部服务的webhook,仅当发生变化时才调用应用。 |
Performance Checks
性能检查
| ID | Check | Severity | What to Look For |
|---|---|---|---|
| PRF-01 | Sequential API calls | Warning | Multiple independent API calls made with |
| PRF-02 | Cold start imports | Warning | Heavy libraries imported at the top level of resolver files. Use dynamic |
| PRF-03 | Missing loading states | Warning | UI Kit: No |
| PRF-04 | Large storage entities | Warning | Storing large objects (>100KB) in a single Forge Storage key. Split into smaller chunks or use Entity Store with indexed queries. |
| PRF-05 | Blocking resolver logic | Warning | CPU-intensive operations (JSON parsing of large payloads, complex string manipulation, sorting large arrays) in resolvers without consideration of the 25-second timeout. |
| PRF-06 | Storage iteration instead of query | Warning | Fetching all items from Forge Storage via |
| PRF-07 | No caching of stable data | Warning | Repeated product API calls for data that rarely changes (e.g., custom field IDs, project metadata, workflow statuses) without any caching strategy. Cache in Forge Storage with a TTL (1 hour+ recommended). Use entity properties (free, stored by the product) for small per-entity data that doesn't need Forge Storage quota. |
| PRF-08 | Unnecessary re-renders | Info | UI Kit: State updates in loops or effects that trigger excessive re-renders. Custom UI: Missing |
| PRF-09 | Unoptimized images | Info | Custom UI apps serving large unoptimized images or assets. Use compressed formats and lazy loading. |
| PRF-10 | Code-side filtering instead of API filtering | Warning | Fetching all items from an API and filtering in resolver code. Push filtering to the API layer using JQL, CQL, or API query parameters so only relevant items are returned. For scheduled jobs, use date-based filters ( |
| ID | 检查项 | 严重程度 | 检查要点 |
|---|---|---|---|
| PRF-01 | 顺序API调用 | 警告 | 多次独立API调用使用 |
| PRF-02 | 冷启动时加载重库 | 警告 | resolver文件顶部导入重型库。对极少使用的重型依赖,在resolver处理函数内使用动态 |
| PRF-03 | 缺失加载状态 | 警告 | UI Kit:获取resolver数据时无 |
| PRF-04 | 大型存储实体 | 警告 | 在单个Forge Storage密钥中存储大型对象(>100KB)。拆分为更小的块或使用带索引查询的Entity Store。 |
| PRF-05 | 阻塞性Resolver逻辑 | 警告 | resolver中执行CPU密集型操作(大payload的JSON解析、复杂字符串操作、大型数组排序),未考虑25秒超时限制。 |
| PRF-06 | 存储迭代而非查询 | 警告 | 通过 |
| PRF-07 | 未缓存稳定数据 | 警告 | 多次调用产品API获取极少变化的数据(例如自定义字段ID、项目元数据、工作流状态),未使用任何缓存策略。缓存到Forge Storage并设置TTL(推荐1小时以上)。对于小型实体数据,使用实体属性(免费,由产品存储),不占用Forge Storage配额。 |
| PRF-08 | 不必要的重新渲染 | 信息 | UI Kit:循环或effect中的状态更新触发过多重新渲染。Custom UI:昂贵计算未使用 |
| PRF-09 | 未优化图片 | 信息 | Custom UI应用提供大型未优化图片或资源。使用压缩格式和懒加载。 |
| PRF-10 | 代码端过滤而非API层过滤 | 警告 | 从API获取所有项目后在resolver代码中过滤。使用JQL、CQL或API查询参数将过滤推至API层,仅返回相关项目。对于调度任务,使用基于日期的过滤( |
Output Format
输出格式
ALWAYS present findings as a single flat list sorted by severity (Critical first, then Warning, then Info). Do NOT group issues by category — interleave categories within the severity-sorted list. Use this template:
undefined始终将审查发现整理为按严重程度排序的单一扁平列表(严重优先,其次警告,最后信息)。请勿按分类分组 — 同一严重程度内可混合不同分类的问题。使用以下模板:
undefinedForge App Review Results
Forge应用审查结果
Summary
摘要
- 🔴 Critical: X issues
- 🟡 Warning: Y issues
- 🔵 Info: Z issues
- 🔴 严重:X个问题
- 🟡 警告:Y个问题
- 🔵 信息:Z个问题
Issues
问题列表
🔴 SEC-01: Overly broad scopes
Location: line ~X
Detail: Scope is declared but only is used in resolvers.
Fix: Replace with granular scope . Remove unused write scope.
manifest.ymlwrite:jira-workread:issue:jiraread:issue:jira🟡 PRF-01: Sequential API calls
Location: line ~Y
Detail: Three calls awaited sequentially. These are independent and can run in parallel.
Fix: Wrap in .
src/resolvers/index.jsrequestJiraPromise.all([call1, call2, call3])🔵 ARC-09: Missing TypeScript
Location: Project root
Detail: Project uses JavaScript. TypeScript would catch type errors at build time.
Fix: Consider migrating to TypeScript. Run with TypeScript template for reference.
forge create
When no issues are found in a category, explicitly state it:✅ Security: No issues found
✅ Triggers & Scheduling: No issues found (or: No triggers defined)
---🔴 SEC-01: 权限范围过宽
位置: 第X行左右
详情:声明了权限,但resolver中仅使用了权限。
修复方案:替换为细粒度权限,移除未使用的写入权限。
manifest.ymlwrite:jira-workread:issue:jiraread:issue:jira🟡 PRF-01: 顺序API调用
位置: 第Y行左右
详情:三个调用被顺序await。这些调用相互独立,可并行执行。
修复方案:使用包裹。
src/resolvers/index.jsrequestJiraPromise.all([call1, call2, call3])🔵 ARC-09: 未使用TypeScript
位置:项目根目录
详情:项目使用JavaScript。TypeScript可在构建时捕获类型错误。
修复方案:考虑迁移至TypeScript。参考的TypeScript模板。
forge create
若某分类未发现问题,需明确说明:✅ 安全:未发现问题
✅ 触发器与调度:未发现问题(或:未定义触发器)
---Anti-Patterns — Do NOT Do These
反模式 — 请勿执行以下操作
- Do NOT modify code unless the user explicitly asks for fixes
- Do NOT skip reading — it is the foundation of the review
manifest.yml - Do NOT guess about permissions — cross-reference every scope against actual API calls in code
- Do NOT report issues without a specific file/line location when possible
- Do NOT combine multiple issues into one finding — each gets its own entry
- Do NOT only check one category — always review all five (Security, Architecture, Cost, Performance, Triggers & Scheduling)
- Do NOT suggest adding dependencies to fix issues — prefer built-in solutions
- Do NOT report issues about test files or dev tooling unless they affect production
- 除非用户明确要求修复,否则请勿修改代码
- 请勿跳过读取— 这是审查的基础
manifest.yml - 请勿猜测权限 — 每个权限范围都需与代码中的实际API调用交叉验证
- 可能的情况下,请勿报告无具体文件/行位置的问题
- 请勿将多个问题合并为一个发现 — 每个问题单独成项
- 请勿仅检查一个分类 — 始终审查所有五个维度(安全、架构、成本、性能、触发器与调度)
- 请勿建议添加依赖项来修复问题 — 优先使用内置解决方案
- 除非影响生产环境,否则请勿报告测试文件或开发工具相关问题
Edge Cases
边缘情况
Minimal App (Hello World)
极简应用(Hello World)
If the app is very simple (1 module, 1 resolver, minimal UI), still run all checks but expect mostly clean results. Report Info-level suggestions for future growth (e.g., "Consider TypeScript as the app grows").
若应用非常简单(1个模块、1个resolver、极简UI),仍需执行所有检查,但预期大部分结果无问题。报告信息级别的未来增长建议(例如:「随着应用扩展,考虑使用TypeScript」)。
Custom UI vs UI Kit
Custom UI vs UI Kit
Detect which type by checking:
- UI Kit: imports from , uses
@forge/react, JSX with Forge componentsForgeReconciler - Custom UI: has a directory or
static/in manifest, imports fromresources@forge/bridge - Both: some apps use both — check each separately
通过以下方式检测类型:
- UI Kit:导入,使用
@forge/react,使用Forge组件的JSXForgeReconciler - Custom UI:存在目录或manifest中的
static/,导入resources@forge/bridge - 混合类型:部分应用同时使用两者 — 分别检查
Monorepo / Multi-Module
单体仓库 / 多模块
If the manifest declares multiple modules, trace each module's function key to its resolver independently. Don't assume all modules share the same issues.
若manifest声明了多个模块,需独立跟踪每个模块的function密钥对应的resolver。不要假设所有模块存在相同问题。
No Manifest Found
未找到Manifest
If doesn't exist in the workspace, stop and tell the user: "No found. Are you in the correct Forge app directory?" Do NOT proceed without the manifest.
manifest.ymlmanifest.yml若工作区中不存在,停止审查并告知用户:「未找到。您是否在正确的Forge应用目录中?」无manifest时请勿继续审查。
manifest.ymlmanifest.yml