Configuration¶
All configuration is via environment variables in .env. See .env.example in the repository for the complete list with comments.
Required Variables¶
Jira¶
| Variable | Description |
|---|---|
JIRA_BASE_URL |
Your Atlassian instance URL (e.g., https://your-org.atlassian.net) |
JIRA_USER_EMAIL |
Service account email |
JIRA_API_TOKEN |
Jira API token |
JIRA_WEBHOOK_SECRET |
Secret for validating Jira webhook signatures |
GitHub¶
| Variable | Description |
|---|---|
GITHUB_TOKEN |
Personal Access Token with repo and read:org scopes |
GITHUB_WEBHOOK_SECRET |
Secret for validating GitHub webhook signatures |
FORGE_BOT_COMMENT_PREFIX |
Prefix to add to all comments made by the Forge bot (e.g., signature or identifier), also used for webhook self-comment filtering to prevent loops. Note: This configuration is intended to allow development and testing with the same user API keys that are used to comment (to prevent webhook loops), and it should not be used in production. A warning is logged at startup when this is set. |
LLM¶
Use provider-neutral model connections. Gemini 3.5 Flash through Vertex AI is recommended. Connections declare permitted backends, models, locations, and capabilities without containing credentials; providers continue to use their native credential environment variables.
GOOGLE_CLOUD_PROJECT=your-gcp-project
GOOGLE_CLOUD_LOCATION=global
MODEL_CONNECTIONS={"vertex-prod":{"backend":"vertex-ai","project":"your-gcp-project","location":"global","allowed_models":["gemini-3.5-flash"],"capabilities":["structured_output","tools"]}}
MODEL_DEFAULT={"connection":"vertex-prod","model":"gemini-3.5-flash"}
Forge validates the backend, credentials, model allowlist, capabilities, and default target at startup.
Legacy single-model configuration
Deployments that do not need named connections or per-stage selection can
still set LLM_BACKEND and LLM_MODEL instead:
LLM_BACKEND=vertex-ai
GOOGLE_CLOUD_PROJECT=your-gcp-project
GOOGLE_CLOUD_LOCATION=global
LLM_MODEL=gemini-3.5-flash
LLM_BACKEND and LLM_MODEL are required together when
MODEL_CONNECTIONS and MODEL_DEFAULT are not configured. Provider
credentials must use the provider-native variables shown above; legacy
credential aliases are not supported.
The legacy CONTAINER_LLM_MODEL override remains supported. For exact
per-stage selection, administrators can add a MODEL_POLICY value to the
recommended connection configuration. Jira projects can then set
forge.model_policy, restricted to those connections and models:
MODEL_CONNECTIONS={"vertex-global":{"backend":"vertex-ai","project":"my-gcp-project","location":"global","allowed_models":["gemini-3.5-flash","claude-sonnet-5"],"capabilities":["structured_output","tools"]}}
MODEL_DEFAULT={"connection":"vertex-global","model":"gemini-3.5-flash"}
MODEL_POLICY={"generate_prd":{"connection":"vertex-global","model":"claude-sonnet-5"},"generate_spec":{"connection":"vertex-global","model":"gemini-3.5-flash"}}
forge project-setup MYPROJ \
--model generate_prd=vertex-production:gemini-3.5-pro \
--model implement_work=anthropic-production:claude-sonnet-4-6
# Set a separate project-wide fallback (individual --model overrides still win)
forge project-setup MYPROJ \
--model-all vertex-production:gemini-3.5-pro
# Remove one stage override and preserve the rest
forge project-setup MYPROJ --remove-model generate_prd
# Delete all per-stage overrides
forge project-setup MYPROJ --clear-model-policy
# Delete the project-wide fallback
forge project-setup MYPROJ --clear-model-default
# Validate against the local runtime configuration and print every target
forge get-config MYPROJ --models
Project overrides require administrators to configure MODEL_CONNECTIONS.
The user-facing project-setup command validates policy syntax and canonical
stage names but does not require access to the Forge deployment's connection
registry. Forge validates connection names, model allowlists, backends, and
capabilities when it executes a stage. Invalid runtime policy fails closed and
reports the available configured connections and models to Jira and, when an
active PR exists, GitHub. The implicit legacy connection remains restricted to
LLM_MODEL and CONTAINER_LLM_MODEL and is never exposed to Jira project
policy.
Canonical policy keys are deliberately specific; runtime prompt, skill, and graph-node names are not accepted in Jira configuration:
| Policy key | Execution |
|---|---|
generate_prd |
Initial PRD generation and every PRD revision |
generate_spec |
Initial specification generation and every specification revision |
decompose_epics |
Epic decomposition or revision |
generate_tasks |
Task generation or revision |
bug_triage |
Bug-report completeness triage |
automated_review_triage |
Classification of automated review feedback |
proposal_review_triage |
Classification of proposal review threads |
task_takeover_triage |
Existing-task takeover triage |
task_takeover_planning |
Existing-task implementation planning |
implement_work |
Container implementation for feature, bug, and task-takeover workflows |
task_takeover_review |
Existing-task qualitative review |
task_takeover_question |
Questions about task-takeover artifacts |
analyze_bug |
Root-cause analysis |
reflect_rca |
Root-cause analysis reflection |
plan_bug_fix |
Bug-fix planning |
bug_local_review |
Local qualitative review of a bug fix |
local_code_review |
Local feature code review |
code_review |
Pull-request code review |
implement_review_analysis |
Analysis of implementation review feedback |
implement_review_fix |
Applying implementation review fixes |
generate_pr_description |
Initial pull-request description generation |
sync_pr_description |
Pull-request description synchronization |
ci_analysis |
CI failure analysis |
ci_fix |
CI failure remediation |
answer_question |
Q&A about PRDs, specifications, plans, and other generated artifacts |
rebase |
Container-assisted rebase conflict resolution |
update_docs |
Documentation update generation |
Unknown keys fail validation instead of silently using the default target.
Keeping artifact generation, revision, and Q&A on one model¶
Revisions reuse the artifact's generation policy key. For example, both initial
PRD generation and later ! revision requests resolve generate_prd. Questions
submitted with ? do not regenerate the artifact and resolve the separate
answer_question key. Configure both keys when the answer must use the same
model that created the PRD:
MODEL_POLICY={"generate_prd":{"connection":"vertex-global","model":"claude-opus-4-6"},"answer_question":{"connection":"vertex-global","model":"claude-opus-4-6"}}
forge project-setup MYPROJ \
--model generate_prd=vertex-global:claude-opus-4-6 \
--model answer_question=vertex-global:claude-opus-4-6
The same pattern applies to generate_spec, decompose_epics, and
generate_tasks: revisions reuse their generation key, while Q&A uses
answer_question. Task-takeover artifact questions are the exception and use
task_takeover_question.
Related actions that use separate policy keys¶
Some user-visible flows contain multiple model invocations with intentionally different responsibilities. Forge resolves each invocation independently, so they may use different models unless every related key is mapped to the same target:
| Flow | Policy keys | Boundary |
|---|---|---|
| Generated artifact interaction | generate_prd, generate_spec, decompose_epics, or generate_tasks + answer_question |
Creation and revision use the generation key; Q&A uses answer_question |
| Task takeover | task_takeover_planning + task_takeover_question |
Plan creation and revision are separate from artifact Q&A |
| Automated artifact review | automated_review_triage + the relevant generation key |
Feedback classification occurs before an accepted revision is generated |
| Proposal review | proposal_review_triage + the relevant generation key |
Review-thread classification occurs before an accepted revision is generated |
| Implementation review | implement_review_analysis + implement_review_fix |
Feedback analysis and code modification are separate invocations |
| CI remediation | ci_analysis + ci_fix |
Failure diagnosis and code modification are separate invocations |
| Bug investigation | analyze_bug + reflect_rca + plan_bug_fix |
Initial analysis, reflection, and repair planning are separate stages |
| Pull-request maintenance | code_review + sync_pr_description |
Code review and description synchronization are separate tasks |
Using different models for these keys is supported and can optimize cost or
quality. When context continuity or consistent judgment is more important,
assign all keys in the flow to the same connection and model. Run
forge get-config MYPROJ --models to display the effective target for every
key before testing a workflow.
Forge owns stage capability requirements. Every stage requires a connection
declaring "capabilities": ["tools"] except the explicitly tool-free text or
classification stages automated_review_triage, proposal_review_triage,
generate_pr_description, and sync_pr_description. Jira policy cannot
weaken that requirement.
Per-target max_output_tokens is limited to 131072.
Resolution is project stage override (forge.model_policy), then the separate
project-wide fallback (forge.model_default), then the deployment stage mapping
(MODEL_POLICY), then the deployment default (MODEL_DEFAULT). Project policy
does not use a * key. --model-all writes forge.model_default and does not
modify per-stage overrides. When --model-policy and --model are combined,
individual --model entries overwrite matching JSON keys. Used without
--model-policy, --model preserves the project's other existing stage
overrides; --model-policy deliberately replaces the full stage property.
--remove-model removes only the named stage and deletes forge.model_policy
when no overrides remain. --clear-model-policy deletes all per-stage
overrides, while --clear-model-default independently deletes the project-wide
fallback.
When MODEL_CONNECTIONS is configured, Forge fetches the Jira project policy
before each host or container agent execution, so changes apply automatically
to the next stage or retry. The target remains fixed during that execution's
internal model/tool loop. Legacy and global-only configurations make no extra
Jira policy request. Projects without forge.model_policy fall back to the
global stage mapping, then the global default, and finally the legacy
LLM_BACKEND/LLM_MODEL settings.
Redis¶
| Variable | Default | Description |
|---|---|---|
REDIS_URL |
redis://localhost:6380/0 |
Redis connection URL |
Operator APIs¶
| Variable | Description |
|---|---|
FORGE_OPERATOR_TOKEN |
Bearer token required for execution and Org Pulse read APIs. The routes are disabled when it is empty. |
EFFECT_OPERATOR_TOKEN |
Bearer token required for durable-effect inspection and replay APIs. The routes are disabled when it is unset. |
Use distinct values when different operators should have workflow-read versus effect-replay authority. See operations for recovery rules.
Per-Project Repository Configuration¶
Production requirement
In production, Forge reads repository configuration from Jira project properties, not from environment variables. If not configured, Forge blocks the workflow and posts setup instructions on the ticket.
Set these properties per Jira project via the REST API:
# Available repos for this project
curl -X PUT \
"https://your-org.atlassian.net/rest/api/3/project/MYPROJ/properties/forge.repos" \
-H "Content-Type: application/json" \
-u "you@example.com:YOUR_API_TOKEN" \
-d '["org/repo1", "org/repo2"]'
# Alternatively, configure a repository with additional metadata (like enabling draft PRs) using an object:
curl -X PUT \
"https://your-org.atlassian.net/rest/api/3/project/MYPROJ/properties/forge.repos" \
-H "Content-Type: application/json" \
-u "you@example.com:YOUR_API_TOKEN" \
-d '[
"org/repo1",
{
"name": "org/repo2",
"draft": true
}
]'
# Default repo when no explicit assignment is made
curl -X PUT \
"https://your-org.atlassian.net/rest/api/3/project/MYPROJ/properties/forge.default_repo" \
-H "Content-Type: application/json" \
-u "you@example.com:YOUR_API_TOKEN" \
-d '"org/repo1"'
Setting up via the Forge CLI¶
Instead of using raw curl requests, you can use the forge project-setup CLI command to configure these properties. When running in automation, use the --json flag to retrieve structured mutation output:
# Set default repository and output changes as pretty-printed JSON
forge project-setup MYPROJ --default-repo org/repo1 --json
Output format on success:
{
"project": "MYPROJ",
"mutations": {
"forge.default_repo": {
"operation": "set",
"value": "org/repo1"
}
}
}
This JSON output is easily consumable with tools like jq:
# Get the mutated value of a specific property using jq
forge project-setup MYPROJ --default-repo org/repo1 --json | jq '.mutations["forge.default_repo"].value'
# Output: "org/repo1"
Error Handling in JSON Mode¶
When running with --json, if an error or exception occurs during execution:
- Standard output (stdout) is completely suppressed and remains empty.
- The error description is written directly to standard error (stderr).
- The command exits with a non-zero exit code (1).
Repository labels on managed tickets use repo:<owner>/<repo>. Forge validates
that assignment against the project's configured repositories before workspace
setup or implementation. A missing or invalid assignment blocks the workflow
instead of selecting a repository implicitly.
If the deployment uses FORGE_REPOS_CONFIG_PATH to load a repos.yaml
registry, the process caches that registry for its lifetime. Restart the gateway
and every worker after changing the file. See operations
for the safe deployment and recovery model.
GitLab repositories use explicit connections (there is no implicit GitLab default), which supports both GitLab.com and self-managed instances:
Set base_url to either the GitLab host/root URL (including any self-managed
path prefix) or an explicit REST API v4 URL. Forge normalizes host/root URLs
by appending /api/v4; explicit URLs ending in /api/v4 are accepted as-is.
connections:
engineering-gitlab:
provider: gitlab
base_url: https://gitlab.example.com
credential_env: ENGINEERING_GITLAB_TOKEN
webhook_secret_env: ENGINEERING_GITLAB_WEBHOOK_SECRET
repositories:
payments-api:
provider: gitlab
connection: engineering-gitlab
namespace: platform/payments-api
default_branch: main
change_request_mode: direct
Proposal review configuration¶
Projects can opt into GitHub pull-request review for PRDs and specifications.
Set forge.prd_proposals_repo to an owner/repo repository and optionally set
forge.prd_proposals_path to a base directory. Forge then creates prd.md and
design.md under {path}/{TICKET}/ on separate proposal branches; merge is
approval and review feedback requests regeneration.
Use forge project-setup MYPROJ --prd-proposals-repo owner/repo to configure
the repository and --prd-proposals-path path to configure the base path. Set
either option to an empty value to remove/reset it. When project configuration
is not required, PRD_PROPOSALS_REPO and PRD_PROPOSALS_PATH provide global
fallbacks. See proposal review for the distinction between this
workflow behavior and core-project design proposals.
Local Development Overrides¶
Use these to skip the Jira project property requirement during local development:
| Variable | Description |
|---|---|
FORGE_REQUIRE_PROJECT_CONFIG |
Set to false to use env var fallbacks instead of Jira project properties |
GITHUB_DEFAULT_REPO |
Default repo (org/repo) when FORGE_REQUIRE_PROJECT_CONFIG=false |
GITHUB_KNOWN_REPOS |
Comma-separated list of known repos |
CI and Validation¶
| Variable | Description |
|---|---|
CI_IGNORED_CHECKS |
Comma-separated list of check name substrings to permanently ignore (e.g., tide,queue) |
CI_MAX_FIX_ATTEMPTS |
Maximum CI fix attempts before blocking (default: 5) |
Container Execution¶
| Variable | Description |
|---|---|
CONTAINER_IMAGE |
Container image for task execution (default: forge-dev:latest) |
CONTAINER_MEMORY_LIMIT |
Memory limit for task containers (default: 4g) |
CONTAINER_CPU_LIMIT |
CPU limit for task containers (default: 2) |
Auto-Review¶
Settings for the automatic review loop that runs after skill execution. See the Auto-Review Guide for details.
| Variable | Default | Description |
|---|---|---|
AUTO_REVIEW_MAX_RETRIES |
3 |
Default maximum retry attempts when a skill's review.md doesn't specify max_retries |
AUTO_REVIEW_POLL_INTERVAL |
5.0 |
Polling interval in seconds for detecting review cycle files during container execution |
AUTO_REVIEW_RECORD_POLLED_FILES |
(none) | Recording mode for polled review cycle files: log logs cycle data at INFO level, copy copies files to recording directory |
Observability¶
Langfuse Tracing¶
| Variable | Description |
|---|---|
LANGFUSE_PUBLIC_KEY |
Langfuse public key |
LANGFUSE_SECRET_KEY |
Langfuse secret key |
LANGFUSE_HOST |
Langfuse host (defaults to cloud; set for self-hosted) |
LANGFUSE_TRACE_TAGS |
Comma-separated list of trace attributes to attach as Langfuse tags. Available values: ticket_key, ticket_type, project_id, workflow_step, repo, pr_number, ci_status, event_source, event_type, llm_model. Default: empty (no tags). |
LANGFUSE_TRACE_METADATA |
Comma-separated list of trace attributes to attach as Langfuse metadata. Available values: same as tags plus retry_count, system_prompt_length. Default: empty (no metadata). |
Grafana Dashboards¶
These variables are used by docker-compose.yml, devtools/docker-compose.dev.yml, and devtools/grafana/compose.grafana.yml.
| Variable | Description |
|---|---|
GRAFANA_PORT |
Host port for Grafana (default: 3010) |
GRAFANA_ADMIN_USER |
Grafana admin user (default: admin) |
GRAFANA_ADMIN_PASSWORD |
Grafana admin password (default: grafana) |
LANGFUSE_DOCKER_NETWORK |
External Docker/Podman network for self-hosted Langfuse when using devtools/grafana/compose.langfuse-network.yml (default: langfuse_default) |
CLICKHOUSE_HOST |
Langfuse ClickHouse host reachable from the Grafana container |
CLICKHOUSE_PORT |
Langfuse ClickHouse native protocol port (default: 9000) |
CLICKHOUSE_DATABASE |
Langfuse ClickHouse database (default: default) |
CLICKHOUSE_USER |
Langfuse ClickHouse user |
CLICKHOUSE_PASSWORD |
Langfuse ClickHouse password |
PROMETHEUS_HOST |
Prometheus host for standalone Grafana compose |
PROMETHEUS_PORT |
Prometheus port for standalone Grafana compose |
REDIS_HOST |
Redis host for standalone Grafana compose |
REDIS_PORT |
Redis port for standalone Grafana compose |
MCP Servers¶
MCP server configuration lives in mcp-servers.json, not .env. See the MCP servers section of the repository.
test-skill Commands¶
Local skill testing and evaluation. See Testing Skills Locally for the full guide.
forge test-skill run¶
Run a skill against test cases using deepagents.
| Flag | Required | Description |
|---|---|---|
--skill NAME |
Yes | Skill name (e.g., generate-prd) |
--skill-dir PATH |
Yes | Path to skill directory containing SKILL.md |
--output DIR |
Yes | Output directory for results and trace |
--input FILE |
One of input/dataset | Single input.yaml test case |
--dataset DIR |
One of input/dataset | Directory of test cases (runs all) |
--project NAME |
No | Project name for skill path (overrides config.yaml) |
--model MODEL |
No | Override model (default: from config.yaml) |
--references FILE |
No | JSON file with reference docs (same format as forge.references) |
--repos DIR [DIR...] |
No | Local repo directories to copy into workspace |
--mlflow URI |
No | MLflow tracking URI for auto-tracing |
--mlflow-experiment NAME |
No | MLflow experiment name (default: forge-skill-eval) |
forge test-skill eval¶
Evaluate skill outputs against gold standards. See Skill Evaluation for criteria format.
| Flag | Required | Description |
|---|---|---|
--criteria FILE |
Yes | Path to criteria YAML file |
--output DIR |
Yes | Output directory for reports |
--generated FILE |
Single mode | Path to generated artifact |
--gold FILE |
Single mode | Path to gold standard artifact |
--dataset DIR |
Batch mode | Dataset directory |
--results-dir DIR |
Batch mode | Runner output directory |
--mlflow URI |
No | MLflow tracking URI |
--mlflow-experiment NAME |
No | MLflow experiment name |