Add AI validators and post-functions to Jira workflows in plain English — plus deterministic conditions and premade checks that run at zero AI cost.
A semantic layer for Jira workflows — with a deterministic floor. Where meaning matters, CogniRunner sends field content, attachments, and related issues to an AI model and evaluates them against criteria you describe in plain English — then validates, decides, and acts on every transition. Where structure is enough, ~27 premade deterministic checks and ten condition types run instantly at zero AI cost. Four rule types cover the full loop, and configuring one takes about thirty seconds.
Block a transition when field content fails your rule, showing the user the AI's reasoning.
Deterministic checks evaluated by Jira itself — zero AI, zero per-transition cost, enforced on every surface: the issue view, REST, automation, and bulk.
After a transition, the AI reads a field, decides, and writes a target field — e.g. summarize work into Release Notes.
AI generates JavaScript once, then runs it at zero AI cost on every transition — chain up to 50 sandboxed steps.
CogniRunner works across company-managed and team-managed Jira projects, supports all standard and custom Jira field types, analyzes the content of attached files and images, and runs on your choice of AI provider — bring your own key, or use the zero-key Atlassian Forge LLM.
Jira already has ways to gate a transition. Here's honestly where each one fits — and the gap CogniRunner fills.
Good at
Fast, free, structural gates — required fields, regex, not-empty, field-equals-value.
Where it stops
Blind to meaning. They can confirm a description exists; they can't tell a real bug report from "it's broken pls fix."
Good at
Powerful, deterministic scripted logic for teams that have the Groovy/JS skills and the appetite to maintain it.
Where it stops
You write and own the code. Semantic judgment ("is this acceptance criteria testable?") is hard to express as a script at all.
Good at
Plain-English AI rules where meaning matters — plus ~27 premade deterministic checks and ten condition types at zero AI cost. Open source, runs on Forge, bring your own AI key.
And then some
Reads meaning across fields, attachments, and related issues — and explains its decision back to the user in their own terms.
Describe the rule the way you'd explain it to a new hire. No Groovy to write, nothing to maintain when requirements change.
Every block, skip, or update comes with the AI's reasoning — visible to the user and saved in the log. No silent black box.
Bring your own AI provider, or keep everything inside Atlassian with the zero-key Forge LLM. Content goes only where you point it.
Apache-2.0, full source on GitHub. Audit it, fork it, or build on it — a permissive license means the rules you write are never trapped behind a vendor.
Concrete jobs CogniRunner does on day one — each is a single plain-English rule on a transition.
Block "In Progress → Done" until the description, acceptance criteria, and test evidence are genuinely present — not just non-empty.
So that Reviewers stop bouncing tickets back for missing basics; the gate does it the moment someone tries to move the card.
On new bugs, have the AI search the project for similar reports and block with the matching issue key when it finds one.
So that Fewer duplicate tickets reaching the backlog, and the reporter sees exactly which existing issue to follow instead.
When an issue hits Done, summarize the work into a customer-facing Release Notes field automatically.
So that Release notes write themselves as work completes, instead of being reverse-engineered from commits at ship time.
Reject transitions when a field — or an attached document or screenshot — contains PII, secrets, or placeholder text like "TBD".
So that Sensitive data is caught at the workflow boundary, not in a quarterly audit after it's already shared.
Require steps to reproduce, expected vs. actual behavior, and an affected component before a bug can leave triage.
So that Engineers receive bugs they can actually act on, cutting the back-and-forth that stalls the first day of every fix.
Confirm an attached spec, mockup, or contract is the real thing and covers the required sections — reading the file, not the filename.
So that "Looks done" stops meaning "a file is attached" and starts meaning "the right, complete file is attached."
CogniRunner integrates into Jira's native workflow editor. Add a validator or condition in three steps.
Select a transition and click the Rules panel on the right side. You'll see rule categories: Restrict transition, Request input, Validate details, and Perform actions.
Click the + button next to "Validate details" (for validators) or "Restrict transition" (for conditions). Find CogniRunner Field Validator under Marketplace Rules.
For AI validators and post-functions: select the field and describe your rule in plain English, optionally attaching context docs. For conditions and premade checks: just pick the deterministic check — no prompt at all. Prefer not to touch the workflow editor? Use + Add Rule in the admin panel's guided wizard.


When you add or edit an AI validator, you'll see the configuration form with three settings.
Which Jira field the AI should evaluate
Your criteria described in natural language
Optional duplicate detection via JQL queries

Conditions decide whether a transition is even offered — and in CogniRunner they are deterministic: zero AI, zero per-transition cost, zero added latency. Jira evaluates the check itself, so it is enforced on every surface — the issue view, the REST API, automation rules, and bulk operations alike.
You don't write a prompt; you pick one of ten checks. And because Jira runs the check natively — no app code executes — conditions produce no execution-log entries.
The three field checks work on custom fields, with support verified per field kind:
| Field kind | Has value / Is empty | Equals a value |
|---|---|---|
| Text (single-line) | Yes | Yes — case-insensitive |
| URL | Yes | Yes |
| Date | Yes | Yes |
| Number | Yes | Yes |
| Select (single choice) | Yes | Yes |
| Radio buttons | Yes | Yes |
| Multi-value kinds (multi-select, checkboxes, labels…) | Yes | No — use has value / is empty |
The field picker annotates unsupported fields with “Not supported for conditions” instead of letting you configure a check that can't run.


The field selector dropdown shows all fields available on your project's edit/view screens, grouped by system fields and custom fields. Each entry shows the field name, its internal ID, and its type.

The validation prompt is where you describe your quality criteria in natural language. The AI evaluates the field content against this prompt and returns a pass/fail result with reasoning.
When a rule needs context beyond the field itself, CogniRunner goes agentic: the AI autonomously builds and runs JQL queries against the issue's own project — over multiple rounds — to find similar or related work before deciding. The Jira Search setting has three modes.
This is the difference between “is this field filled in?” and “has someone already reported this?” — a question that previously needed a human to remember, search, and judge. Catching a duplicate at the point of creation saves the triage meeting, the merged tickets, and the work that gets done twice.
| Mode | Behavior |
|---|---|
| Auto-detect from prompt | CogniRunner analyzes your prompt text and automatically enables JQL search if it mentions duplicates, similarity, existing issues, or cross-referencing. This is the recommended setting. |
| Always enabled | JQL search is always active, regardless of prompt wording. The AI can run up to 3 rounds of JQL queries to find related issues. |
| Always disabled | No JQL search is performed. Validation is based solely on the field content and prompt. Fastest option. |
When agentic search is active, the AI generates and executes JQL queries to find related issues. It can run up to 3 rounds of queries, each returning up to 10 results, and is confined to the rule's own project for safety. The AI then uses what it finds to inform the decision — and the full query trail is logged.

When you select Attachment as the field to validate, CogniRunner downloads and analyzes the actual content of attached files. The AI can read documents, spreadsheets, presentations, and understand images.
Every other workflow check treats an attachment as a checkbox: a file is present, so the gate passes. That's exactly how an empty template, a stock photo, or last sprint's spec sails through. Reading the file closes that gap — the requirement becomes the right, complete document, not any document.
| Format | Extensions |
|---|---|
.pdf | |
| Word | .docx, .doc |
| Rich Text | .rtf, .odt |
| Excel | .xlsx, .xls |
| CSV / TSV | .csv, .tsv |
| PowerPoint | .pptx, .ppt |
| Format | Extensions |
|---|---|
| PNG | .png |
| JPEG | .jpg, .jpeg |
| GIF | .gif |
| WebP | .webp |
All image formats are processed via AI vision — the AI understands image content, not just metadata.
Unsupported file types are gracefully skipped — the AI receives metadata about skipped files (filename, size, type) so it can still reference them in its reasoning.

When a user attempts a workflow transition, CogniRunner evaluates the configured field against your prompt. If validation fails, the transition is blocked and the AI's reasoning is displayed as an error message.

Validators check; post-functions act. A semantic post-function runs after a transition: the AI reads a source field, decides whether to act, generates a value, and writes it to a target field — all described in plain English.
This is the busywork that quietly eats team hours: copying a summary into release notes, keeping an audit field current, turning a description into a checklist. Each is a tiny task no one wants to own — and the first thing dropped under pressure. Hand it to a post-function and it happens consistently, on every transition, with the reasoning logged.
When should this run? The AI evaluates the source field and decides to update or skip.
What should it write? The AI generates a value to the rules you describe.
Where does it go? Schema-aware formatting writes the value into the field you choose.

For deterministic automation, the AI writes JavaScript once at setup. After that it runs on every transition at zero AI cost. Chain up to 50 steps with variable passing, a built-in code editor with autocomplete, and a sandboxed Jira API.
This is the bridge for platform engineers who'd otherwise reach for ScriptRunner: you describe the automation in plain English, the AI drafts the code, and you keep an editor open to read, tweak, and test it against real issue data before it ever touches production. The output is plain JavaScript you fully control — predictable on every run, with no AI in the hot path once published.
Query Jira for related issues.
Read or write any Jira resource.
Call allow-listed external services.
Create or update Confluence pages.
Inspect values while you build.
Per-step timeouts, retries, and a Test Run.

The Rules tab is a real control plane, not just a list. Enable, disable, delete, import, export — and every action tells the truth about what it did to your Jira workflows.
Pause a rule without losing its config. For conditions, the disabled flag is written into the workflow rule itself, so Disabled is enforced by Jira — never a lie. A failed write refuses rather than half-applies.
Per-row and bulk Delete remove the rule from the Jira workflow, not just from the list — behind a preview dialog that predicts the outcome for each rule before you commit.
See real site-wide pressure against the registry's true capacity — 500 rules, 240 KiB. Import checks the cap before attaching anything.
An Owner column with an explicit Unowned chip. “My Rules” shows only rules you authored — claiming or discovering a rule never mis-attributes it to you.
Find rules attached outside the panel — REST automation, copied workflows — and claim them in one click, with a report of what was skipped and why.
Export rules as self-contained JSON, static post-function code included. Import shows a preview and lets you rebind fields to the target site's IDs.

CogniRunner rules are ordinary Forge workflow rules — so you can attach them with Jira's own workflow REST API, using your Jira credentials, no CogniRunner API involved. Bulk-provision a rule across fifty projects, migrate rules between sites, or keep them in version control.
The Rules tab's “Automating rule creation” panel gives you everything install-specific: this installation's extension ARIs with copy buttons, the ruleKey and target slot for each rule type, and the size caps (a rule's config must stay under Jira's 32 KB cap; the panel manages up to 500 rules).

id inside the rule's config: without it the rule can be claimed but never disabled, because that id is the identity the runtime matches on.exprProp and exprKind (plus valueNum for number fields). A mismatched exprKind is the one way a condition can fail closed — copy the config shape from a UI-made rule rather than hand-crafting it.The CogniRunner Admin panel is the hub for every rule across your Jira workflows, with tabs for Rules, Documentation, Skills, Memories, Permissions, and Settings. Access it from the Jira Apps menu.

Every validator, condition, and post-function across all workflows — filter by type or ownership, edit, disable, or delete (with workflow detach) inline, individually or in bulk.
Create a rule without touching the workflow editor — pick project, workflow, transition, and type, then configure and publish.
A shared documentation library, reusable Skills, and learned Memories give the AI grounded, instance-specific context.
Control who can do what with a role and scope model. Jira site admins always have access, and the last admin is protected from removal.
This is what lets a Jira admin let go safely. Team leads can author and tune their own workflow rules without an admin in the loop, and without the keys to everyone else's rules or the AI provider settings — so governance scales past a single gatekeeper.
Can view rules and execution logs.
Can create, edit, disable, and manage rules and documents.
Full access, including permissions and AI provider settings.

Give the AI grounded context. Upload API docs, JSON schemas, business rules, or code snippets once, then attach them to any rule so the AI validates and generates against your standards — not just its training data.

CogniRunner doesn't just run on a model's generic knowledge — you teach it. Skills are reusable instruction packs; Memories are facts it learns about your instance. Both are fed into the AI so its output fits how your Jira actually works.
Teach the code generator domain-specific patterns — Jira REST calls, ADF formatting, duplicate detection. Attach a skill to a rule, or let CogniRunner auto-match by the step's description. Ships with built-ins; add your own once and reuse them everywhere.
Short facts about this Jira — custom field IDs, option lists, workflow quirks — captured once and injected into every generation, so the AI stops repeating the same mistakes. Add them yourself or let it learn as it runs.


CogniRunner sits between your AI and the outside world. Through the Model Context Protocol (MCP), a rule can call real tools — and CogniRunner brokers every call, so your AI provider never sees the tool's URL or credentials.
Live library & SDK docs. The AI checks code against the current documentation for any library — not the model's training cutoff.
resolve-library-id · query-docs
Real-time web search plus page and PDF extraction, so a rule can research a topic or verify a claim while it runs.
full-web-search · get-page-content · get-pdf-content
Read attached files and generate real DOCX, PDF, Excel & PPTX — a rule can produce a deliverable and attach it to the issue.
read-doc · create-pdf · create-docx · create-excel

Every AI run — validator or post-function — is logged with full context: pass/skip/error status, the AI's reasoning, the JQL it ran, and an execution trace. Logs are available in both the Admin panel and the individual rule view. Deterministic conditions are evaluated natively by Jira, so they produce no log entries — no app code runs.
This is what makes AI-in-the-loop something you can defend rather than something you have to trust blindly. When a user asks “why was I blocked?” the answer is one entry away, verbatim — the field it read, the queries it ran, the verdict, and a suggested fix. Tuning a rule stops being guesswork.

Logs are stored in Forge storage with a maximum of 50 entries (FIFO — oldest entries are removed when the limit is reached).
CogniRunner extracts readable text from any Jira field type and sends it to the AI for validation. All field values are converted to plain text before evaluation.
| Category | Field Types |
|---|---|
| Text | Single-line text, Multi-line text, URL |
| Rich Text | Description, Comments, and other ADF fields — full text extraction from rich content |
| Select | Select list (single/multi), Radio buttons, Checkboxes, Cascading select |
| People | User picker (single/multi), Group picker (single/multi), Reporter, Assignee |
| Date & Time | Date picker, Date/Time picker |
| Numeric | Number, Time tracking (original estimate, remaining estimate, time spent) |
| System | Priority, Status, Resolution, Issue type, Labels, Components, Versions, Sprint |
| Reference | Issue links, Parent issue, Project picker |
| Files | Attachments — with full content analysis |
| Third-Party | Checklist for Jira, Xray, Assets/Insight, ScriptRunner, Tempo, Elements Connect, and more |
| Custom | Any custom field with a readable value — extracted generically |
| Limit | Value | Notes |
|---|---|---|
| Max attachment size (per file) | 10 MB | Files larger than 10 MB are skipped |
| Max total attachment size | 20 MB | Combined size across all attachments per validation |
| Validation timeout | 25 seconds | Forge function time limit; agentic mode budgets 22 seconds |
| JQL search rounds | 3 rounds max | Each round can execute multiple JQL queries |
| JQL results per query | 10 issues | Top 10 matching issues returned per query |
| Static post-function steps | 50 steps | Chained operations per static post-function |
| Execution log history | 50 entries | Oldest entries removed when limit is reached (FIFO) |
| Prompt log truncation | 200 characters | Prompts in log entries are capped at 200 characters (field values at 300) |
| Rule registry capacity | 500 rules · 240 KiB | Byte-accurate usage meter on the Rules tab; imports check the cap before attaching |
CogniRunner ships with no embedded API key. Connect your own key from any supported provider — or use the zero-key Atlassian Forge LLM that runs inside Atlassian's platform. Each provider stores its own key, so switching never loses your settings.
For a security or platform team, this answers the first question they'll ask: where does our issue data go? The answer is wherever you decide — your own provider account under your existing data agreement, or never leaving Atlassian at all with the Forge LLM. No third party sits between Jira and the model, and switching providers later is a dropdown, not a migration.
The first Jira app to integrate LM Studio — inference and your field data stay on your own hardware. Absolute privacy and zero per-token cost, for the security- and budget-conscious. The multi-model pool spreads validator calls across every model you have loaded, so one busy model never becomes the bottleneck.
One of the first apps to ship the Atlassian Forge LLM provider — AI that runs inside Atlassian's platform with no API key and no external egress.
Claude models, your key
Your OpenAI API key
Your Azure deployment
One key, many models
Converse API, your account
Self-hosted on your own hardware
Zero-key — runs inside Forge

The complete reference: every provider and where your data goes, how a validation run actually unfolds, the full post-function and automation rulebook, rules, skills and memories, the permission model, the limits, and what to do when a verdict surprises you. 71 sections.
Every rule, key and number here is taken from the app's own source and checked against it.
Workflow rules that read meaning: AI-powered validators and post-functions, deterministic zero-cost conditions, and a catalog of premade no-AI checks — all inside Jira's own workflow editor.
CogniRunner is a Forge app for Jira Cloud (app id ari:cloud:ecosystem::app/36415848-6868-4697-9554-3c3ad87b8da9, Node.js 22 runtime, Apache-2.0 open source). Jira's native workflow rules check structure — a field is set, a value equals X. CogniRunner adds rules that read meaning: you write the check in plain English ("the description must include steps to reproduce, expected behavior, and actual behavior") and an AI model evaluates the actual field content on every transition. It ships as four workflow extensions — a validator, a condition, and two post-functions — plus an admin app, and works in both company-managed and team-managed projects (projectTypes in manifest.yml).
Every validator can run in one of two lanes. The AI lane sends the field content to the AI provider you configure and can run an agentic loop — the model may call a JQL search tool mid-validation to look for duplicates or related issues, up to a 20-second inner budget (AGENTIC_TIMEOUT_MS = 20000). The premade lane is a catalog of deterministic checks (required field, regex, text length, date bounds, sub-tasks resolved…) that runs entirely inside the app: zero AI calls, zero token cost, instant. Conditions are ALWAYS deterministic — Jira evaluates them itself as a sandboxed Jira expression with no network access, so an AI condition is structurally impossible (the manifest documents this at the jira:workflowCondition module).
CogniRunner ships with no embedded API key. You connect one of seven providers in Settings: Anthropic, OpenAI, Azure OpenAI, OpenRouter, AWS Bedrock, a self-hosted LM Studio server, or the zero-key Atlassian (Forge LLM) — Claude models served inside the Atlassian platform via @forge/llm, no key and no egress. The provider labels here are the exact dropdown entries in OpenAIConfig.jsx.
executePostFunction always returns { result: true }. The exact messages for every failure path are catalogued in "When the AI cannot answer" below.The editor's "Rule kind" toggle switches a validator between a plain-English AI prompt and a deterministic catalog check; conditions skip the toggle entirely and always open in the premade form.
When you configure a validator, the first choice is the Rule kind toggle: "AI prompt — Describe the check in words; AI evaluates each transition" or "Premade rule — Pick a ready-made check — no AI, instant, zero cost" (exact button copy from config-ui App.js). Conditions never show the toggle — the editor forces ruleKind = "premade" for them, because Jira evaluates conditions as an expression and an AI condition can never exist. The toggle is hidden rather than shown-and-disabled, deliberately: the app does not offer something that cannot work.
| Catalog key | Label in the picker |
|---|---|
field-required | Field is required |
field-changed | Field must be changed |
field-comparison | Field compares to a value (equals / does not equal / greater / at least / less / at most / contains) |
field-regex | Field matches a pattern |
allowed-values | Field is one of… |
text-length | Text length is within bounds |
date-relative | Date is in the future / within N days |
sub-tasks-resolved | All sub-tasks must be resolved |
attachment-required | An attachment is required |
comment-required | A comment is required |
field-cardinality | Field value count is within bounds |
A premade condition is compiled into the Jira expression declared in manifest.yml, so Jira hides the transition without ever calling the app. Exactly ten types are expression-backed (EXPRESSION_BACKED_CONDITIONS): issue-type-is, issue-is-resolved, resolution-is, priority-is, parent-status-is, current-user-is-assignee, current-user-is-reporter, and three field checks — field-has-value, field-empty, field-equals — for CUSTOM fields only, with the comparison strategy chosen per live-probed field kind (CONDITION_FIELD_KINDS: text, URL, date and number fields compare as values; select/radio compare their option; multi-value kinds support has/empty only).
The Semantic Post Function is the AI-at-runtime one: after a transition, the model evaluates a condition you describe and modifies a target field — the editor tags it "AI cost per run". The Static Post Function inverts the cost: AI writes sandboxed JavaScript for each step at CONFIGURATION time, and at runtime the generated code executes with no AI call at all — tagged "No AI cost at runtime". A static rule chains up to 50 steps (maxSteps in src/shared/rule-portability.js), each with a 22-second execution budget (PF_BUDGET_MS = 22000).
Ten object kinds, each with its own storage key family in Forge KVS — knowing them makes every later chapter concrete.
config_registry KVS key (capped at 500 rows, REGISTRY_MAX_ROWS in src/shared/registry-limits.js) so the admin panel can list, disable and audit rules. New rules get a per-instance id ending ::i-<6 alnum> so two same-type rules on one transition never share an identity.ai-text-field-validator module. Runs in the app backend as validate(args) and returns { result: boolean, errorMessage?: string } — result: false blocks the transition and Jira shows the message. Both lanes (AI prompt and premade) run here.ai-text-field-condition module. NOT executed by the app: Jira evaluates the manifest's Jira expression directly. Anything the expression does not recognise — including every pre-release saved config — deliberately evaluates to true, because an erroring condition would hide the transition for everyone.ai-semantic-post-function and ai-static-post-function modules. Always return { result: true } — they act after the transition and can never block it. Static-rule step code larger than 24,576 bytes is offloaded from the workflow config to its own content-addressed KVS key pf_code:{id}:{hash} (the workflow editor caps an embedded rule config at 32,768 bytes).ruleKind: "premade" plus a catalog ruleType. Runs deterministically — the validator executor lives in src/premade-rules.js, the condition variant in the manifest expression. Zero AI cost, and it short-circuits before any provider/credential work.COGNIRUNNER_AI_PROVIDER names the provider; each provider keeps its own COGNIRUNNER_KEY_{provider}, COGNIRUNNER_MODEL_{provider} and COGNIRUNNER_BASEURL_{provider} slots, so switching providers never loses the others' settings.log_entry:<inverted-timestamp>_<random> — per-entry keys so concurrent writers (validator + post-function on the same transition) can never lose entries to a read-modify-write race. 30-day TTL, pruned to the newest 50 (MAX_LOGS). Premade runs log metadata only, never field values.skill_repo:{id} with a skill_repo_index; capped at 100 custom skills, ≤45,000 chars serialized each (instructions ≤24,000, examples ≤16,000). Builtins are exempt from the cap.pf_memories array — cap 200 items / 200,000 chars, with near-duplicates (Jaccard ≥ 0.85) reinforced instead of duplicated. Settings live in COGNIRUNNER_MEMORY_SETTINGS; defaults are { autoCapture: false, injection: true, runtimeInjection: false } — capture and runtime injection are opt-in.doc_repo:{id} with doc_repo_index; the index is capped at 50 documents (MAX_DOCS), of which 10 are shipped builtins (src/shared/builtin-docs.js) that are exempt from eviction. Deleting a builtin flips it to disabled — reseeding never resurrects it.Four workflow extensions in the workflow editor's rule pickers, a global page under Apps, an admin settings page, and a read-only glance panel on every issue.
| Module | Key | Name shown in Jira | Manifest description |
|---|---|---|---|
jira:workflowValidator | ai-text-field-validator | CogniRunner Field Validator | "Validates a text field value against a custom AI prompt on workflow transition." |
jira:workflowCondition | ai-text-field-condition | CogniRunner Field Condition | "Shows transition only if a text field value passes AI validation." (Legacy wording — conditions are deterministic-only; the config UI itself offers no AI option.) |
jira:workflowPostFunction | ai-semantic-post-function | CogniRunner Semantic Post Function | "AI evaluates a condition and modifies a target field after workflow transition." |
jira:workflowPostFunction | ai-static-post-function | CogniRunner Static Post Function | "Chains multiple operations with AI-generated code after workflow transition." |
| Module | Key | Title | What you get |
|---|---|---|---|
jira:globalPage | cognirunner-global-page | CogniRunner | The main app under Jira's Apps menu. Seven tabs: Rules, Execution Logs, Documentation, Skills, Memories, plus admin-only Permissions and Settings. |
jira:adminPage | cognirunner-admin-settings | CogniRunner Settings | The SAME app served from Jira's admin settings area — guaranteed reachable for site admins even if the Apps menu entry is hidden from them. |
jira:issueContext | cognirunner-issue-glance | CogniRunner | A read-only right-rail panel on the issue view showing recent validator/condition/post-function activity for that issue (resolver getIssueActivity, gated by an as-user view check). |
All four workflow extensions share one configuration UI (config-ui-resource for create/edit) and one read-only summary (config-view-resource for the workflow editor's view mode). Company-managed classic editor: open the workflow, select a transition, then the Validators / Conditions / Post Functions tab → Add; the CogniRunner entries appear under the names above. The new workflow editor and team-managed projects reach the same picker through the transition's rules panel.
One async consumer carries every long-running AI job, the llm module enables the zero-key Forge LLM provider, and three web triggers exist — one of them dev-only.
| Module | Key | Budget / gating | Purpose |
|---|---|---|---|
consumer | async-ai-consumer | queue async-ai-queue, timeoutSeconds: 120 | Runs the six async task types registered in src/async-handler.js: review, postfunction, codegen, fixcode, skilldistill, memory_distill. |
llm | cogni-llm | model: claude | Enables the Atlassian (Forge LLM) provider — Claude served inside the platform via @forge/llm. No key, no egress; token costs are billed to the vendor, which is why only Claude Haiku is offered (isForgeLlmModelAllowed, enforced at list, save, load AND the chat adapter). |
webtrigger | harness-test-state | Gated by HARNESS_SECRET; returns 404 in production | Development test-harness endpoint only. |
webtrigger | attachment-bridge | One-shot capability token (URL token + Authorization bearer) | Serves a single Jira attachment as base64 JSON so a self-hosted LM Studio model can READ attachments through the doc-reader tool. |
webtrigger | attachment-upload | Same one-shot token model | The write side: accepts a generated document envelope and attaches it to the bound issue via api.asApp(). |
Validators and conditions ALWAYS run synchronously inside the transition — they never queue. The queue carries configuration-time AI work (code generation, code fixing, rule reviews, skill distillation) and post-function execution when the provider is LM Studio: self-hosted models are too slow for the 25-second sync resolver cap, so those resolvers detect provider === "lmstudio" and queue instead, with the frontends polling every 3 seconds up to 40 tries (120 s). The admin panel says exactly this next to its jobs list: "Queued and running async AI jobs (LM Studio post-functions, code-gen, reviews) appear here while they run. Validators & conditions run synchronously and don't queue."
async_task:{id}) are deleted the moment a frontend collects them. Finished jobs surface under "Recently completed" in Execution Logs and clear themselves after ~20 minutes.Sixteen scopes in manifest.yml — reads for validation and pickers, writes for post-function actions, workflow read/write for the rule registry, and screen-scheme reads for the field picker.
| Scope | Why the app needs it |
|---|---|
read:jira-work | Reading issues and field values at validation time (when modifiedFields lacks the field, the value is fetched over REST), the agentic JQL search tool, attachment reads, and the issue glance. |
write:jira-work | Post-function writes: updating fields, adding comments, creating sub-tasks and links, worklogs, transitions — every mutating sandbox API method. |
read:jira-user | User lookups for pickers and sandbox methods like setAssignee / addWatcher. |
read:workflow:jira | Reading published workflows to discover which transitions actually carry CogniRunner rules — the Rules tab's registry scan and orphan cleanup. |
write:workflow:jira | POST /rest/api/3/workflows/update: propagating a rule's disabled flag into the workflow's own embedded condition config (Jira cannot read app storage when evaluating the expression), and REST rule provisioning. |
read:project:jira | Project lists for pickers and per-project rule context. |
storage:app | The Forge KVS — where every object in the glossary above lives. |
read:issue-type-screen-scheme:jira, read:screen-scheme:jira, read:screen-tab:jira, read:screenable-field:jira | Resolving project → issue-type screen scheme → screen → tabs → fields, so the "Field to Validate" picker can list the fields that actually exist on the transition's screens. |
manage:jira-configuration | The admin check: GET /rest/api/3/group/member against jira-administrators, site-admins and system-administrators requires it. |
manage:jira-project | Sandbox methods that create project-admin objects: createVersion (POST /rest/api/3/version) and createComponent (POST /rest/api/3/component). |
write:sprint:jira-software, write:board-scope:jira-software, write:issue:jira-software | The agile sandbox methods: moveToSprint / moveToBacklog and rankIssue. |
llm module was a major-version upgrade that existing installations had to approve. What you see in the scope list is the complete, stable surface.Workflow editor → add a validator → pick CogniRunner Field Validator → write one plain-English sentence → test against a real issue → publish.
claude-haiku-4-5-20251001) — paste your API key, choose a model. Or pick "Atlassian (Forge LLM)" and skip the key entirely.AI Validation failed: <the model's reason>.Every degraded path allows the transition and says so in the log — the exact strings, so you can recognise each one.
A workflow rule that fails closed on an outage locks a whole team out of their board. CogniRunner's law is the opposite: a rule only blocks a transition on a genuine isValid: false verdict from a completed evaluation. Every infrastructure failure degrades to ALLOW, with a distinct reason recorded in the execution log.
| Situation | What happens | Exact reason string |
|---|---|---|
| License inactive | Transition allowed, AI skipped entirely | (logged) "License inactive — skipping AI validation (fail open)" |
| Rule disabled in the admin panel | Transition allowed; matching is by rule identity, never by field alone | (logged) "Rule … is disabled in KVS — skipping AI validation" |
| No provider API key | Transition allowed | "AI validation is not configured (no provider API key) — transition allowed (fail-open). Set the provider API key in CogniRunner settings." |
| Provider 429 / 5xx | Transition allowed, marked transient | "AI service temporarily unavailable (<status>) — transition allowed (fail-open)." |
| Other provider error | Transition allowed | "AI service error (<status>) — transition allowed (fail-open). Check the AI provider/key in CogniRunner settings." |
| AI call exceeds the 21 s validator deadline | Transition allowed — bounded below Forge's hard 25 s kill so the return is graceful | "AI validation timed out — transition allowed (fail-open)." |
| Agentic loop runs out of tool-call rounds | Transition allowed | "Validation reached maximum tool-call rounds without a final answer. Transition allowed." |
| Jira throttles the field/attachment read | Transition allowed | "Field could not be read (Jira throttled the request) — transition allowed (fail-open)." / "Attachments could not be read (Jira throttled the request) — transition allowed (fail-open)." |
Conditions carry the same philosophy in Jira's own engine: the manifest expression's default branch is TRUE, so an unrecognised or unparsed config shows the transition instead of hiding it for everyone. Post-functions can never block by construction — executePostFunction always returns { result: true }, and every skip writes a postfunction-skipped log entry so silence is still visible.
Workflow rules follow Jira's own editor permissions; inside the app a three-level role model (viewer / editor / admin, scoped own or all) governs the panel, with Jira site admins always admins.
Attaching a rule to a workflow happens in Jira's workflow editor, so Jira's rules apply: Administer Jira for company-managed workflows, project administrators for team-managed projects (both projectTypes are declared). CogniRunner adds its own layer for the admin panel: every user has a role of viewer, editor or admin, plus a scope of own or all, stored in the app_admins KVS key and managed from the admin-only Permissions tab.
| Role | Panel access | Rules |
|---|---|---|
| viewer | Sees Rules, Execution Logs, Documentation, Skills, Memories | Read-only |
editor (scope own) | Same tabs | Create rules; enable/disable/edit rules they created — plus legacy ownerless rows. Deleting is stricter: canDeleteConfig requires genuine authorship, so an ownerless row is NOT deletable by an own-scope editor. |
editor (scope all) | Same tabs | Act on every rule |
| admin | Everything, plus the Permissions and Settings tabs (both marked adminOnly) | Full control, including provider keys, usage limits and the user list |
You rarely have to bootstrap by hand. First, members of jira-administrators, site-admins or system-administrators are ALWAYS { role: "admin", scope: "all" } — checked live against Jira group membership. Second, if the app-users list is completely empty, the first person to open the app is auto-registered as admin with the display name "Auto (first user)".
requireRole / requireAdmin / canActOnConfig — a project rule the core contract enforces on new code), so the role model is real enforcement, not just hidden buttons.Everything is Forge KVS inside your own Atlassian site — no external database. The key families, their caps, and their TTLs.
| Key(s) | Holds | Bounds |
|---|---|---|
config_registry | The rule registry (one row per configured rule) | 500 rows; rows are slimmed on every write |
pf_code:{id}:{hash} | Offloaded static-PF step code, content-addressed and immutable | Written when serialized steps exceed 24,576 bytes; ≤220 KiB per bundle |
log_entry:<inverted-ts>_<rand> | Execution history, one entry per run | Newest 50 kept (MAX_LOGS); 30-day TTL; probabilistic prune on ~10% of writes |
doc_repo_index, doc_repo:{id}, doc_repo_seed_meta | Documentation library | 50 docs; 10 builtins eviction-exempt |
skill_repo_index, skill_repo:{id}, skill_repo_seed_meta | Skills | 100 custom; ≤45,000 chars serialized each |
pf_memories | Learned memories (single array) | 200 items / 200,000 chars; each ≤400 chars |
COGNIRUNNER_AI_PROVIDER, COGNIRUNNER_KEY_{p}, COGNIRUNNER_MODEL_{p}, COGNIRUNNER_BASEURL_{p} | Provider connection (BYOK slots) | Admin panel only — never environment variables |
COGNIRUNNER_MEMORY_SETTINGS | Memory feature toggles | Defaults { autoCapture: false, injection: true, runtimeInjection: false } |
COGNIRUNNER_USAGE | The AI usage meter (calls + tokens, per provider, month/day buckets) | Provider set clamped to the seven known providers so it can never grow unbounded |
async_task:{id}, pf_exec:{id} | Async job status and post-function execution records | TTL-bound (2 h active / 20 min terminal); polled rows deleted on collection |
app_admins | The app's user/role list | — |
Provider API keys deserve a plain statement: they are stored in Forge KVS on your site, entered only through the admin panel, and sent only to the provider they belong to. The usage meter is an honest best-effort UNDER-count — it reads and re-writes a single key without compare-and-set, so concurrent runs can lose increments; the optional monthly call ceiling built on it is therefore a soft ceiling that real spend can modestly overshoot (the meter's own header comment says exactly this).
Cloud BYOK providers receive issue content; LM Studio sends it only to your own machine; the Forge LLM never leaves Atlassian. The exact hosts are pinned in the manifest.
When an AI rule runs, the prompt contains the validated field's value, the issue context the rule needs, any doc-library documents attached to the rule, JQL search results when the search tool fires, and — if enabled — learned memories. All of it is injected inside guarded <<<MARKER … MARKER>>> fences and defanged, but fencing is a prompt-injection defence, not privacy: whatever is in the prompt reaches the model's operator. Forge egress is CSP-pinned to exactly the hosts in the manifest — nothing else is reachable at runtime.
| Provider (dropdown label) | Endpoint | Issue content leaves Atlassian? | Default model |
|---|---|---|---|
| Anthropic | api.anthropic.com (/v1/messages, x-api-key) | Yes — to Anthropic, under your key and their data terms | claude-haiku-4-5-20251001 |
| OpenAI | api.openai.com/v1/chat/completions | Yes — to OpenAI | gpt-5.4-mini |
| Azure OpenAI | {resource}.openai.azure.com/openai/v1 (api-key header; model = your deployment name) | Yes — to your Azure tenant. Same code path as OpenAI, but end-to-end behavior is honestly flagged as mostly untested (no live Azure deployment in the harness) | gpt-5.4-mini |
| OpenRouter | openrouter.ai/api/v1 (plus HTTP-Referer / X-OpenRouter-Title attribution headers) | Yes — to OpenRouter AND the upstream vendor of whichever model you pick from its 300+ catalogue | openai/gpt-5.4-mini |
| AWS Bedrock | bedrock-runtime.<region>.amazonaws.com (Converse API, Bedrock API key as plain bearer — no SigV4) | Yes — to your AWS account in your chosen region | eu.anthropic.claude-sonnet-4-6 (a cross-region inference-profile id; admins pick their own) |
| LM Studio | Your own machine via its Tailscale Funnel URL (https://your-machine.tailXXXX.ts.net) | Leaves Atlassian, but ONLY to hardware you control — no third-party AI vendor is involved | None — you pick from the models loaded on your server |
| Atlassian (Forge LLM) | In-platform via @forge/llm — no network egress at all | No. Content never leaves the Atlassian platform | claude-haiku-4-5-20251001 (Haiku-only by policy; text-only, no image/file input) |
*.ts.net ONLY, and Forge honours the default HTTPS port alone. The setup help in Settings says it outright: "Only *.ts.net (Tailscale Funnel) is allowlisted in the app's egress. Other tunnel providers (ngrok, Cloudflare Tunnel) will not work — requests would be blocked by Forge before leaving the cloud."Two further manifest hosts complete the honest picture: mcp.context7.com (a hosted documentation MCP the LM Studio agentic path can consult for library/SDK docs) and the same *.ts.net wildcard for self-hosted MCP servers. The Bedrock wildcard *.amazonaws.com covers both the runtime and control-plane hosts across regions. If you configure no cloud provider — Forge LLM or LM Studio only — no issue content ever reaches a third-party AI vendor; that is the accurate version of a "no egress" claim for this app, and it is provider-dependent, not absolute.
COGNIRUNNER_USAGE (calls, prompt/completion tokens, per provider, month and day buckets), and admins can set a monthly call ceiling. Forge LLM usage is billed to the app vendor, not to you — which is exactly why it is capped to Haiku-class models.Install from the Marketplace, point it at a transition, and write your first rule in plain English. Open source under Apache-2.0 — the full source is on GitHub.