forge-autopilot
Skill0 starsChanged 5 days agoMITView on GitHub
Routes a request to Forge by ShipToday — ONLY when the user asks for Forge by name. Invoke this skill when the user's current message contains "@forge", "forge", "@shiptoday" or "shiptoday" (any case) and asks Forge to do something: plan, define, implement, fix, review, estimate, ship, check status, or map architecture. Also invoke when a Forge hook directive (FORGE ROUTING, FORGE OBSERVATION, FORGE CHECKPOINT) explicitly tells you to. Do NOT invoke for any message that does not name Forge — however much it looks like product or engineering work, and even when it contains a tracked work item key such as "PROJ-123": handle it normally. If the mention is ambiguous (for example "we deploy with Laravel Forge"), ask whether the user meant Forge before invoking.
2 other projects use a version of this.
Adopted by
Most recently taken up by
- tusharpandey13/forgetheir own version, first seen Jun 16, 2026
- tonmoy007/forge-pluginstheir own version, first seen Jun 9, 2026
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- tonmoy007/forge-plugins★ 2same name
- tusharpandey13/forge★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository2 other files it gives its agents.
- forge-feedbackSkill
- forge-workflowSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
53 rules. About 0 are common, 21 are distinctive, and 32 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈21
Distinctive · estimate
This author's own choices. Worth a look.
32
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 31
Before calling any Forge tool, build the connected_tools array by checking which MCP tools are available in the current session:
- Very specific: line 48
Only include connectors you can verify.
- Distinctive: line 64
When in doubt, include it — over-declaring a relevant skill is harmless.
- Very specific: line 66
Do NOT include skills unrelated to product/SDLC work (e.g.
- Very specific: line 76
If no relevant local skills are found, omit the local_skills parameter entirely.
- Very specific: line 80
When a current checkout is available, read git remote get-url origin (or reuse the remote already observed for this request) and send it as …
- Distinctive: line 86
If there is no checkout, no origin, or the read is unavailable, omit the field.
- Very specific: line 107
If the user explicitly says to run directly or not delegate, their instruction wins: include delegation_preference: { choice: "direct", …
- Distinctive: line 131
Prefer a compatible blocking control.
- Distinctive: line 180
Whether the user asked is your call: No hook reads their words to route, approve or block a start.
- Very specific: line 187
Do NOT pass an explicit workflow parameter on the initial call and do NOT try to route to a server-side skill.
- Distinctive: line 191
If the request names no outcome and no lifecycle verb (for example "help me with the roadmap"), do not match yet: ask one question about …
- Distinctive: line 203
If multiple catalog workflows genuinely fit, present only those, always with a final "None of these fit" option — a user handed the wrong …
- Very specific: line 215
Never invent a workflow, expose a server-side skill id as an option, or set classification_complete: true without an explicit workflow — …
- Very specific: line 230
Pages: When the line says page 1 of N, the reply lists that page only.
- Very specific: line 242
A reply that did not arrive whole: If you see fewer workflows than the Catalog line states, no end line, or any part of the reply was cut …
- Very specific: line 260
If they align, re-call forge__start_workflow with the same arguments plus the returned admission_token and start_confirmed: true.
- Very specific: line 262
If they do not align, do not confirm and do not call forge__abandon_workflow — no workflow has started.
- Very specific: line 267
Never set start_confirmed: true without the server-issued admission_token from the immediately preceding proposal.
- Distinctive: line 269
When the proposal is an approach Forge worked out for the request ("How Forge will approach this"), show it to the person exactly as …
- Very specific: line 308
Follow the returned instructions to present a tracking nudge to the user.
- Very specific: line 309
Do NOT classify this as a build/bug/architecture request — it's a passive check.
- Very specific: line 313
When a workflow returns Workflow complete., look at the final state_updates payload you sent.
- Very specific: line 336
If follow_up is null or absent, do not chain.
- Very specific: line 356
Do NOT start a workflow, do NOT classify this as a build/bug/architecture request, and do NOT surface anything to the user — it is a …
- Very specific: line 364
After calling start_workflow, Forge returns step-by-step instructions.
- Very specific: line 370
After completing each step, call forge__update_state with the results AND the step_token from the most recent response (see below)
- Very specific: line 372
If Forge returns needsDisambiguation or needsIntentClassification, present options or pick a workflow per the returned instructions and …
- Very specific: line 381
Layer 1 — CHECKPOINT enforcement: When the orchestrator returns a CHECKPOINT response from forge__update_state (a relayed-question skill is …
- Very specific: line 411
Every step transition publishes a Tool Permissions: cat1, cat2, … line listing the categories the active step is meant to use.
- Distinctive: line 462
If you receive a deny decision for a tool you genuinely need, the right move is to get the user's answer or the write plan's approval first …
- Very specific: line 468
Every start_workflow and update_state response also includes a line:
- Very specific: line 474
When you call forge__update_state, include the most recent token via state_updates.step_token: "<token>".
- Distinctive: line 482
NOT rotate — re-use the same token until the workflow actually advances to a new step.
- Very specific: line 495
If you delegate a step to a sub-agent, pass the current step's token into the sub-agent prompt verbatim — the sub-agent threads it through …
- Distinctive: line 501
MUST return that response to you (the parent) VERBATIM.
- Very specific: line 512
If both sentinels are present AND the opening sentinel declares bytes=N, compute the UTF-8 byte length of the body between them and compare …
- Very specific: line 516
If both sentinels are present but bytes=N is absent (a common smaller-model paraphrase mode — the agent keeps the sentinel shape but drops …
- Very specific: line 530
When the current FORGE_DISPLAY_VERBATIM event was already shown, make the recovery call with include_display: false.
- Distinctive: line 539
Diagnostic phrasing: when this happens, describe it as a fetch ("the envelope isn't in the sub-agent's return — fetching canonical state") …
- Very specific: line 558
If the workflow no longer applies — the user redirected to unrelated work, the classifier picked the wrong workflow, or scope changed …
- Very specific: line 582
After abandoning, you may start a fresh workflow with forge__start_workflow or proceed without one.
- Distinctive: line 607
Follow the instructions in the Model Routing block returned by Forge:
- Very specific: line 625
Do not display workflow-state JSON, catalogs, routes, tokens, tool examples, question schemas, or FORGE_NEXT_STEP instructions merely …
- Distinctive: line 642
balanced: - default reasoning model tier
- Distinctive: line 645
If you already match the recommended tier, execute the instructions directly — no delegation needed.
- Distinctive: line 672
When it comes out yes, three things matter:
- Distinctive: line 686
Reuse the reviewer, never the author: Do not resume an agent that helped build the thing.
- Distinctive: line 695
When you cannot spawn one, run it inline and label it: *"Reviewed on my own context — this is a self-review."* A self-review is a fine …
- Distinctive: line 727
Follow the per-step Worth-it check.
- Distinctive: line 778
When a read-only / planning mode is active AND a Forge workflow is requested,
- Distinctive: line 790
Do NOT execute those writes while the read-only mode is active.
- Distinctive: line 802
Anti-pattern to avoid: do NOT abandon the Forge workflow mid-step because a read-only mode is active.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Forge Autopilot
You are routing requests to Forge via the forge MCP server. Forge runs only
when the user asks for it by name — see the Trigger rule in Step 2. A
request that does not name Forge gets a normal response, however SDLC-shaped
it is.
Forge's workflow catalog lives on the server side and is fully data-driven.
You don't need to know which workflows exist — forge__start_workflow will
return a classifier prompt listing all available workflows (Forge defaults
plus any org-specific workflows configured for the current user) when it
needs the client AI to choose. Just call it and follow the instructions.
Step 1: Detect connected tools
Before calling any Forge tool, build the connected_tools array by checking
which MCP tools are available in the current session:
| Connector | Look for these tool names |
| jira | searchJiraIssuesUsingJql, createJiraIssue, updateJiraIssue |
| linear | save_issue, save_comment, list_issue_statuses |
| github | GitHub MCP tools: search_issues, add_issue_comment, search_code, get_file_contents, list_pull_requests — or the gh CLI installed and authenticated |
| slack | slack_send_message, slack_search_users, slack_search_channels |
| confluence | searchConfluenceUsingCql, getConfluencePage |
| notion | notion-search, notion-fetch, notion-create-pages |
| google_drive | native integration — include only if connected |
| gmail | native integration — include only if connected |
| granola | search_meetings, get_meeting_transcript |
| figma | get_design_context, get_screenshot, get_metadata |
| local_code | filesystem/shell access (always include in coding environments) |
Only include connectors you can verify. Always include local_code when you
have filesystem access. The forge__start_workflow tool description carries
the server's full list of signals per connector; this table is a subset of it.
Step 1b: Detect local skills
Build the local_skills array by checking your available skills, rules, or
slash commands. Include every skill relevant to product development or the
software development lifecycle — anything a Forge workflow step could draw
on, such as brainstorming, planning, requirements, estimation, architecture
analysis, code review, debugging, testing, or documentation. Relevance — not
whether the skill is project-specific — is the test: a general-purpose skill
still counts if it supports SDLC work.
A Forge workflow step can declare one of these as a required local skill, so under-declaring a relevant skill will block the step that depends on it. When in doubt, include it — over-declaring a relevant skill is harmless.
Do NOT include skills unrelated to product/SDLC work (e.g. presentation,
document, or spreadsheet builders, image or media generators) or built-in
platform commands (/help, /clear, etc.) — Forge workflows never use these.
| Source | Where to look |
| Cursor | Skills from installed plugins and rules loaded from .cursor/rules/ shown in your system context |
| Codex | Skills listed in the current session or loaded from the project |
For each skill found, include { "name": "<skill-name>", "description": "<brief description>" }.
If no relevant local skills are found, omit the local_skills parameter entirely.
Current repository
When a current checkout is available, read git remote get-url origin (or
reuse the remote already observed for this request) and send it as
repo_remote on every forge__start_workflow call. This applies to built-in
workflows and worked-out approaches, including routing, team selection,
admission confirmation and follow-up calls. Preserve it on every re-call so
the first practice read and the opening brief use the current repository.
If there is no checkout, no origin, or the read is unavailable, omit the field;
never guess it from a repository mentioned in a ticket or example.
Automatic admission
On every forge__start_workflow call, include:
client_capabilities: ["auto_admission"]
It tells Forge this client starts an approach Forge worked out as soon as the person has seen it, without a Start question first. Like the field below, it applies to every call form shown, which omit it for readability.
Codex delegation authorization
Invoking this applicable Forge skill authorizes Codex to use model-tier
delegation for eligible workflow steps. On every forge__start_workflow
call in Codex, include:
delegation_preference: { choice: "delegate", source: "skill" }
If the user explicitly says to run directly or not delegate, their instruction
wins: include delegation_preference: { choice: "direct", source: "user" }
instead and preserve it on every re-call. Applicable project instructions may
be reported with source: "project". This is client-reported routing metadata;
Forge does not treat it as server-side tool permission. Do not ask a separate model-delegation
permission question. This field is a Codex authorization signal; it does not
change Claude Code's existing direct Agent(...) model-routing behavior.
This applies to every call form shown below, including catalog re-calls,
admission confirmation, recommendations, passive observation, and follow_up
chaining; those examples omit conditional host-only fields for readability.
Step 2: Route the request
Codex question lifetime
For fixed choices, prefer supported, permitted native UI. Forge may use a
negotiated MCP form; after an answered RE-ENTRY, never ask the question again.
Otherwise use request_user_input or request_user_input_async only when
callable and permitted for this question's purpose in the current mode.
Assess MCP forms and each host tool independently. In Default mode, a
Plan-only blocking tool does not prohibit the async tool: check its own
permissions before numbered text. An unexplained MCP rejection is not a
policy-wide denial. Explicit policy prohibiting the question across routes
still applies.
Prefer a compatible blocking control. A permitted async picker may be used
once; its string options carry exact labels, so show descriptions and full
approval material in prose before opening it. Respect each tool's actual
schema: blocking input accepts 2–3 options; that limit does not apply to every
native control. Do not remove choices to make them fit.
An acknowledgment such as accepted:true records submitted delivery only,
with no answer. Retain the pending question ID, step token and option keys;
wait for the actual user selection or explicit typed reply before dependent
work. The async picker can close at turn end or timeout: show the same
numbered recovery choices before ending the turn unless the user has already
answered. Never re-open it automatically, force Plan mode, probe forbidden
tools, or keep it alive with sleeps or polling. A resolved UI event can be
cleanup, not an answer. Do not bypass host policy rejection through another
tool. When native input is unavailable or prohibited, use persistent numbered
choices. Other hosts keep their permitted native controls and existing wait
protocol.
Trigger rule — check before calling Forge
Forge starts only when the user's current message asks for it by name: it
contains @forge, forge, @shiptoday or shiptoday (any case) with a clear
request for Forge. Nothing else starts a workflow. Without the keyword, handle
the request normally, even when it is SDLC-shaped (plan, review, fix, ship,
estimate) and even when it names a tracked work item key such as PROJ-123.
That includes when the user is:
- implementing specific changes already requested or reviewed in this session;
- resolving a merge conflict or applying known review comments;
- editing known files, adding already-specified tests, or fixing an already- diagnosed local defect; or
- committing, pushing, creating, or updating a pull request for work already in progress.
A keyword in an earlier message does not carry over to a new request: judge each message on its own. If the mention is ambiguous — "we deploy with Laravel Forge", "forge ahead with the plan" — ask the user whether they meant Forge before calling anything, as a yes/no choice in the host's question UI when one is available, and start nothing on the mention alone. Once the user has said they did not mean Forge by ShipToday, do not ask again about the same kind of mention in this conversation. A message that tells you not to use Forge ("don't use Forge for this") is not a request for it.
Three things are not new requests, so they need no keyword: an answer to a
question Forge asked, which continues the run already in progress; a
follow_up the user chose when a workflow completed; and the hook-driven
entries below (the session observer and its checkpoints), which start on a
Forge hook directive, not on the user's words.
Whether the user asked is your call. No hook reads their words to route, approve or block a start.
For a request that names Forge, your default action is:
→ forge__start_workflow(feature_request, connected_tools, local_skills: <detected_skills>)
Do NOT pass an explicit workflow parameter on the initial call and do NOT
try to route to a server-side skill. When intent selection is needed, Forge
returns the enabled workflow catalog for this user and organization.
- If the request names no outcome and no lifecycle verb (for example "help me
with the roadmap"), do not match yet: ask one question about what should
exist when the work is done, with options phrased as outcomes the catalog
can deliver (never workflow ids), plus a final "None of these fit". Forge's
returned instructions say, per caller, whether to add "Build a custom
workflow" before it. Then classify the answer, and report the ask on the
classified call as
routing_ask: { asked: true, scope: "outcome", offered: [<the ids behind the outcomes you offered>] }. - A workflow fits only when it would deliver the named outcome, not feed into
it — a near miss is a no-match, not a candidate. If one workflow clearly
fits, re-call
forge__start_workflowwith that explicit catalog workflow id andclassification_complete: true. - If multiple catalog workflows genuinely fit, present only those, always with
a final "None of these fit" option — a user handed the wrong options must
never be cornered into one. When Forge's instructions say the caller can
author, add "Build a custom workflow" immediately before it and show at most
two candidates (the widget holds four options). After they choose a
candidate, re-call with that explicit workflow id,
classification_complete: true, androuting_ask: { asked: true, scope: "catalog", offered: [<the ids you offered>] }— addauthoring_offered: truewhen the build option was shown. If they pick "None of these fit" — or skip the question — treat the request as having no match and follow the instructions Forge returns; do not re-ask. If they pick "Build a custom workflow", take the authoring offer in Forge's instructions directly, carrying their words forward. - Never invent a workflow, expose a server-side skill id as an option, or set
classification_complete: truewithout an explicitworkflow— except when sending anapproachForge asked you to work out; that call never names a workflow.
The server-provided workflow catalog is the source of truth; the client AI owns the contextual choice among those available workflows.
When the catalog is paged, shortened or incomplete
Forge keeps a routing reply small enough for the host to show it whole, so a
large catalog can arrive in pieces. Every catalog opens with a **Catalog** line
— how many workflows it holds, whether all of them are listed or which page
this is, and a version — and closes with an end line.
- Pages. When the line says
page 1 of N, the reply lists that page only. Route to a workflow on it when it clearly delivers what was asked. Before deciding that nothing fits, or choosing between candidates, read the rest: re-callforge__start_workflowwith the same arguments pluscatalog_page: 2, then each page after it. If a page reports a different version, the catalog changed while you were reading: start again at page 1. - Shortened entries. An entry ending in "…" was trimmed to fit. Before
choosing between workflows that look close, or ruling one out on a trimmed
entry, re-call with the same arguments plus
catalog_details: [<ids>](up to 8) for their full routing hints. Hints come back whole; when they are too long for one reply the answer says which page it is, and you read on with the samecatalog_detailspluscatalog_page. - A reply that did not arrive whole. If you see fewer workflows than the
**Catalog**line states, no end line, or any part of the reply was cut off or replaced by a notice (a truncation marker, a "saved to a file" pointer), the catalog you hold is incomplete. Do not tell the user no workflow exists, do not work out an approach and do not offer to build a workflow from it: re-call with the same arguments pluscatalog_page: 1and read every page first.
Neither argument starts anything. Both list only workflows this user can route to, so a workflow missing from every page is not available here.
Admission proposal — validate before activation
A post-classification call returns a server-resolved workflow proposal before it creates an active conversation. Compare its entry step, missing inputs, configured path, and potential side effects with the user's full conversation and current phase:
- If they align, re-call
forge__start_workflowwith the same arguments plus the returnedadmission_tokenandstart_confirmed: true. - If they do not align, do not confirm and do not call
forge__abandon_workflow— no workflow has started. Choose another workflow only when it is an exact catalog match. If none matches, continue normally when this is coding continuation, otherwise explain that no available Forge workflow fits. - Never set
start_confirmed: truewithout the server-issuedadmission_tokenfrom the immediately preceding proposal. - When the proposal is an approach Forge worked out for the request ("How Forge will approach this"), show it to the person exactly as written and follow the confirmation it returns. This client reports automatic admission, so Forge tells you to start it in the same turn, with no Start question; the person can stop it, or change its steps, at any time. If the confirmation asks you to put Start to the person instead, do that and never confirm it yourself.
Exception 1 — Help / recommendation request
"forge, what should I do with PROJ-123?", "@forge help with PROJ-123", "forge, where do I start?"
→ forge__start_workflow(feature_request, connected_tools, recommendation: true, local_skills: <detected_skills>)
Exception 2 — Session observer (passive tracking)
Triggered by the stop-observer.cjs stop hook — the auto-submitted
follow-up turn's input contains "observe session" or "observe_session
workflow". A hook directive, not the user's words, starts it, so it needs no
Forge keyword. The user can also ask for it: a message that asks Forge to
track, or resume tracking, this session ("@forge resume tracking this
session") takes this route too — not the catalog route in Step 2, which
cannot reach the observer.
→ forge__start_workflow(feature_request: "Passive session observation", connected_tools, workflow: "observe_session", local_skills: <detected_skills>)
The observe_session workflow is a single-step workflow that wraps the
session observer. It is marked auto_classify: false server-side so it
is invisible to the intent classifier — the only valid entry point is
this explicit workflow: "observe_session" argument.
Client authority stops at eligibility. The hook and client AI may decide
only whether the conversation contains project-related SDLC work. If it does,
start observe_session; if it does not, skip it. Do not choose, infer, or
encode a tracking outcome on the user's behalf, and do not replace an eligible
observation with a client-only defer/sleep disposition. Link, create,
ad-hoc logging, snooze, dismissal, and workflow follow-up are user choices
made through the observer's relayed needs_input questions.
Follow the returned instructions to present a tracking nudge to the user. Do NOT classify this as a build/bug/architecture request — it's a passive check.
After any workflow completes — read follow_up
When a workflow returns Workflow complete., look at the final
state_updates payload you sent. If it includes a non-null follow_up
object, you MUST chain to that workflow before resuming the user's
original request. Status reports and session-observer outcomes both use
this structured handoff contract.
forge__start_workflow(
feature_request: <follow_up.feature_request>,
connected_tools: <same array as before>,
epic_key: <follow_up.epic_key>,
workflow: <follow_up.workflow>,
classification_complete: <follow_up.classification_complete ?? true>,
pre_forge_context: <follow_up.pre_forge_context>,
local_skills: <detected_skills>
)The four-field structured shape keeps the bound work item key
clean: epic_key is passed as a structured parameter so any
PROJ-NNN-shaped strings inside pre_forge_context cannot hijack the
binding via the embedded-key regex.
If follow_up is null or absent, do not chain. Continue the
conversation normally.
Common failure mode to avoid: treating "Workflow complete" as
"the workflow is done — go back to the user's original ask". A selected
follow-up is an explicit user route; silently ignoring it leaves that choice
unhonored. Always read follow_up before resuming.
Exception 3 — Session checkpoint (passive time tracking)
Triggered by the stop-observer.cjs stop hook for an already-tracked
(logged / linked) session — input contains "session checkpoint" and
spells out a complete forge__update_state call (conversation_id,
completed_step, state_updates).
→ Call forge__update_state exactly as the directive specifies — pass
the conversation_id and state_updates verbatim. The conversation_id
is the original observe_session conversation; the server records the
elapsed time as a silent audit event.
Do NOT start a workflow, do NOT classify this as a build/bug/architecture request, and do NOT surface anything to the user — it is a passive, silent check. After the call, continue normally.
Step 3: Follow the workflow
Pass the user's request as feature_request (strip pleasantries, keep substance).
After calling start_workflow, Forge returns step-by-step instructions.
Follow them:
- Execute each step as instructed
- Follow the returned question-delivery instructions. Forge may deliver a native MCP form and return the answered step directly; do not ask the same question again after RE-ENTRY. Otherwise, on Cursor, ask in your reply and end your turn: render bounded choices as a numbered list (
1.,2.,3....) and tell the user to enter one number; for multi-select, ask for comma-separated numbers. Do not useAskQuestionfor a Forge decision — Forge accepts an answer only after the user's next message, so an answer gathered throughAskQuestionis refused. Keep genuinely open-ended prompts as free text. - A tool submission acknowledgment means submitted, not displayed or answered. Wait for the actual user answer before dependent work. Preserve the question ID, step token, option order and labels. Post the actual answer through the returned
user_answerpath. Never convert dismissal, failure, empty input, an invalid/out-of-range number, or a preselected default intoTBDor approval. Keep the decision identifiable to the user. Read-only recovery does not re-present; usequestion_resume: truewith the returned identity on an explicit resume. - After completing each step, call
forge__update_statewith the results AND thestep_tokenfrom the most recent response (see below) - If Forge returns
needsDisambiguationorneedsIntentClassification, present options or pick a workflow per the returned instructions and re-call
Workflow guard — what is enforced
Forge installs a preToolUse hook (workflow-guard.cjs) that denies
tool calls in two situations: while a question is waiting for the user, and
while a write is waiting for the user's approval. Two layers:
Layer 1 — CHECKPOINT enforcement. When the orchestrator returns a
**CHECKPOINT** response from forge__update_state (a relayed-question
skill is awaiting user input), the hook holds write tools — the same ones the
write lock holds (tracker, docs and messaging writes, code-host connector
writes, file writes and deletions) — until the user has answered. Everything else passes:
- a direct question in your reply — relay the pending question (numbered choices), then wait for the user's next message; on Cursor that message is how Forge knows the user answered
forge__update_state— advance with the user's answerforge__abandon_workflow— exit when the workflow no longer applies (see below; it is not a way to end a run early)- Reads, such as filesystem reads and search, web fetch and search, and read-only MCP tools
- The host's own task-list, progress and coordination tools, such as
ToolSearch,Skill, waiting tools and the host task tools. They do not authorize connector writes or workflow advancement.
The hook does not check shell commands, here or anywhere (see below). While a question is pending, the instructions still apply: wait for the user's answer, and use any tool or the shell only to ask, inspect or coordinate, never to act on the decision being asked.
Layer 2 — the write lock. A step set to Always asks that writes carries a
**Write Lock**: on line until its write plan is approved. Until then the hook
holds write tools (tracker, docs and messaging writes, code-host connector
writes, file writes and deletions). Post the write plan, get it approved, then
write.
Per-step tool_permissions — follow them; the hook does not enforce them.
Every step transition publishes a **Tool Permissions**: cat1, cat2, … line
listing the categories the active step is meant to use. Staying inside them is
your job, the same as for shell. Categories are coarse:
| Category | Tools |
read_code | filesystem reads and search (always allowed regardless) |
ask_user | direct user question or structured user-input tool |
web | web fetch and web search tools |
tracker_read | list_issues, get_issue, list_comments, search_threads, … |
tracker_write | save_issue, create_issue, save_comment, update_issue, … |
docs_read | Read connected documents; this category grants no writes |
docs_write | Create or update connected documents. When the active step explicitly routes an approved document into the repository, use Write, Edit, or apply_patch to create or update that ONE canonical routed document only |
messaging | Slack send |
calendar / design / meetings | Per-connector groups |
conversation_artifact | task-owned .html/.svg backing file for an inline renderer; never a repository file |
code_edit | code-editing tools such as Write, Edit, or apply_patch, for application code and other repository edits beyond the narrow docs_write document scope |
shell | Commands that change or publish things (builds, tests, commits, pushes, PRs), plus the code-host connector writes (create_pull_request, merge_pull_request, push_files, …) |
Concretely: a readiness check does not include code_edit or shell, so do
not edit during it. A code-execution step includes both. A triage step that
only reads the ticket includes tracker_read but not tracker_write — read
the ticket's comments, but do not post to or rewrite the ticket.
A docs_write step without code_edit can save only the document the step
explicitly routed and approved, at its resolved canonical location. Read nearby
documents to follow the existing layout, then create or update that document;
do not edit application source or configuration. This permission adds no shell
or git rights: do not stage, commit, push, or change a pull request to deliver
the document. A tracker pointer also requires tracker_write and its own
declared write scope. Content approval and any required repository-destination
choice still come before writing; a detected layout alone grants nothing.
Shell is not checked by the hook. Shell and WriteShellStdin always
pass — a pending question and the write lock never look at a command.
Following the step's permissions is your job here too: a step without
shell may run read-only commands (git and gh reads, file listings, package
audits) and nothing that changes or publishes anything; a gh issue write
counts as tracker_write; under the write lock, run no command that publishes
until the write plan is approved. Command examples in step instructions are
illustrative — any equivalent read-only command is fine.
Anything denied gets an actionable reason that points at the three
legitimate next moves: relay the user question, advance
(forge__update_state), or abandon (forge__abandon_workflow — only when
the workflow itself no longer applies). On Claude Code the guard uses the
host's PreToolUse denial format. Other hosts may expose different hook
contracts, and wrappers or unknown tools can limit enforcement. This is not a security boundary: forge__abandon_workflow is always allowed, and a host
that wraps MCP calls may not expose every call
to the hook.
If you receive a deny decision for a tool you genuinely need, the right move is to get the user's answer or the write plan's approval first — that is what the hook is waiting for.
Step token — pass it back on every update_state
Every start_workflow and update_state response also includes a line:
**Step Token**: `step_<uuid>` — include this in your next `forge__update_state` callWhen you call forge__update_state, include the most recent token via
state_updates.step_token: "<token>". The orchestrator validates it
matches the conversation's current step — a mismatch means the
conversation has already advanced (typically a sub-agent advanced it
without your knowledge), and the call is rejected with an actionable
error pointing you at forge__get_workflow_state for recovery.
For relayed-question CHECKPOINT and RE-ENTRY responses the token does NOT rotate — re-use the same token until the workflow actually advances to a new step. The token rotates on every real step advance.
Sub-agent relay — verify the envelope, fetch canonical state when absent
An agent in a separate client session does not inherit the parent's local
question or write guard. Before that agent's first forge__update_state for
the run, it must call forge__get_workflow_state with the conversation id and
follow the returned question, token, and write lock. Reuse that agent's
session for later steps when possible; recovery is needed once per new
session, not after each step in the same session. If the child cannot
recover the run, keep the workflow update in the parent's session.
If you delegate a step to a sub-agent, pass the current step's token
into the sub-agent prompt verbatim — the sub-agent threads it through
its own update_state call, using the current token from recovery if
it differs. The orchestrator's response to that call
(carrying the new step_token and the next step's instructions) is
delivered to whoever made the MCP call: the sub-agent. The sub-agent
MUST return that response to you (the parent) VERBATIM.
To detect a missing envelope mechanically rather than heuristically, the
orchestrator wraps the next-step instructions in a <<<FORGE_NEXT_STEP
token="…" bytes=N>>> … <<<END FORGE_NEXT_STEP>>> envelope. The
parser accepts the envelope on sentinels + token; bytes=N is an
optional integrity annotation. After every sub-agent return:
- Scan the return for the envelope. If either sentinel is missing —
or the opening sentinel is restructured so
token="…"no longer sits on it — the sub-agent didn't include a parseable envelope. - If both sentinels are present AND the opening sentinel declares
bytes=N, compute the UTF-8 byte length of the body between them and compare againstN. Mismatch = the sub-agent paraphrased inside otherwise-correct sentinels. - If both sentinels are present but
bytes=Nis absent (a common smaller-model paraphrase mode — the agent keeps the sentinel shape but drops the byte count as "boring metadata"), trust the envelope on sentinel + token alone. No fetch needed — the next-step body is good as-is. - On a step-1 fail (missing/restructured sentinels) OR step-2
mismatch (declared bytes don't match), call
forge__get_workflow_state(conversation_id: "<id>")to fetch the canonical step body and current step_token. This is the designed recovery channel — read-only, idempotent, and owner-checked. Findings the sub-agent put indisplay_textare preserved as a## Findingsblock in the fetched response, so no analytical output is dropped — only the verbatim relay shortcut was skipped.
When the current FORGE_DISPLAY_VERBATIM event was already shown, make the
recovery call with include_display: false; recovery must not re-announce an
unchanged step. The response publishes Instruction Bytes before the body. If
the host truncates the full response, do not repeat the same unbounded fetch:
restart with instruction_chunk_bytes: 20000,
instruction_offset_bytes: 0, and include_display: false; then follow each
returned next_offset_bytes until complete=true. Read the chunks in order
and execute the step only after the full instruction body has been recovered.
Diagnostic phrasing: when this happens, describe it as a fetch ("the envelope isn't in the sub-agent's return — fetching canonical state") rather than as a failure ("the relay was lost"). The findings layer is the system's designed answer to envelope-not-present, so the information path stayed intact even though the shortcut path didn't. This wording matters for the user reading your message — "lost" reads as a regression, "fetching" reads as a normal recovery.
A summarized or paraphrased return without the envelope leaves the
parent with a stale token if it doesn't fall through to the state
fetch — the new token lives ONLY in the orchestrator's response body,
and your parent CANNOT see the sub-agent's tool results. The STEP
BOUNDARY directive injected into delegated prompts repeats this
requirement and names the envelope explicitly. This applies uniformly
across Claude Code (Agent tool), Codex (spawn_agent), Cursor, and
any other environment with sub-agent delegation.
Escape hatch — forge__abandon_workflow
If the workflow no longer applies — the user redirected to unrelated work,
the classifier picked the wrong workflow, or scope changed mid-stream — call
forge__abandon_workflow(conversation_id, reason) to cleanly close the
conversation. This is the only correct way to exit a workflow without
completing it.
- Not for ending a run early. Workflows run straight through — there is
no pause between steps. When the user asks to stop, call
forge__update_statefor the current step withstate_updates: { stop_run: true, step_token: "<the current token>" }, even while a question is waiting for them. Forge ends the run with a recap of everything produced and the steps that did not run, and does not mark it abandoned. Deciding on your own that the remaining steps are unnecessary is not a reason to stop or abandon — that is the user's call. - Do NOT silently bypass the workflow by skipping
forge__update_statecalls and proceeding directly with implementation. Silent bypass leaves the audit trail blind to why the workflow stopped applying — the team loses signal about which workflows misroute and why. - The
reasonis required, must be at least 8 characters, and is recorded in the audit trail. Make it meaningful (e.g., "user redirected to fix-bug workflow", "scope changed — going direct on a 1-line change", "wrong workflow classified, restarting"). Don't write filler like "abandoning" or "no longer needed". - Idempotent: re-abandoning returns the original record. Safe to retry.
- After abandoning, you may start a fresh workflow with
forge__start_workflowor proceed without one. Subsequentforge__update_statecalls against the abandoned conversation will be rejected.
Step 4: Respect model delegation rules
Forge responses include a **Model Routing** metadata line with the recommended
model tier. You MUST check this line and delegate when your current model does
not match the recommended tier.
How to read the routing signal
The response metadata contains a line like:
**Model Routing**: tier=balanced | model=gpt-6.1-terra | environment=codex | guidance=codex_model_map | complexity=medium | task=planningThe tier value tells you which capability tier to use. The optional model
value is only exact when the guidance variant names a maintained model map
for your current environment. If no model is present, use the tier language
only: pick your fastest, balanced, or most capable available model.
How to delegate
Follow the instructions in the Model Routing block returned by Forge:
| Routing signal | What to do |
guidance=claude_code_model_map | Use the concrete model parameter Forge provides. |
guidance=codex_model_map | Use the provided Codex model hint when your Agent or model switch supports it; otherwise choose the same tier in Codex. |
guidance=model_agnostic_tier | Do not invent a model name. Choose your tool's available model that matches the tier. |
A delegated sub-agent inherits your MCP tools and can call external connectors directly.
CRITICAL: Sub-agents execute ONE step only. The ---DELEGATE BELOW---
content already includes a step boundary directive. After the sub-agent returns,
YOU (the parent) surface the user-facing findings before the next step or
confirmation. Render designated display_text and FORGE_DISPLAY_VERBATIM
bodies in full, without the sentinels. Also preserve complete user-facing
deliverables explicitly required by the step, even when a legacy step has no
display_text. Summarize other findings as useful prose.
Do not display workflow-state JSON, catalogs, routes, tokens, tool examples,
question schemas, or FORGE_NEXT_STEP instructions merely because they occur
in a sub-agent return. Those are internal control data for you to consume.
This is not a ban on JSON or code: preserve either when the user requested it
or it is substantive review/approval material.
Then interpret the next step yourself; do not expect a sub-agent to chain steps.
Common loophole to avoid: when a sub-agent's return bundles both its substantive findings AND state-advancement metadata (e.g. "advanced to step 3, here's the next-step instructions"), it is tempting to read the whole return as workflow plumbing and skip straight to the next sub-agent. Don't. The findings are user-facing; the state metadata is internal. Surface the findings first, every time.
Self-check: which tier are you?
- fast - lightweight/low-cost model tier
- balanced - default reasoning model tier
- capable - strongest available reasoning model tier
If you already match the recommended tier, execute the instructions directly — no delegation needed. If you do NOT match, delegate or switch using the environment-specific guidance Forge returned — unless the step's Worth-it check flags the remaining work as a pre-computed replay (content already in workflow state, no new generation), in which case run it inline regardless of tier.
Independence: a different question from tier
Tier asks "is this model strong enough?". It does not ask "should the reviewer be someone who has already seen this work?" — and for a step whose job is to judge an artifact, that second question is often the one that decides quality. A reviewer who produced the work reads what they meant to write; a reviewer with no prior exposure reads what is actually there.
Each step's advisory carries an Independence check above its delegation rules. Settle it first, because the rules would answer it wrongly: "I'm already on the right tier" and "I already hold the context" both point at running inline, and on a review step holding the author's context is the disqualification, not the qualification.
The check is a test you apply to the step in front of you — Forge does not pre-label which steps are reviews, because your org's own workflows and skills count too. It also covers judging your own conclusions, not just your own code: confirming a root cause you hypothesised earlier in the run is the same failure mode, and a more expensive one, since the whole fix gets built on it.
When it comes out yes, three things matter:
- Give away the judgment, keep the step. Hand the fault-finding to the
fresh agent; keep the mechanical checks, the rendering, and — when the step
ends in a relayed question — the question and the
forge__update_statehand-off. Delegating such a step whole forces the sub-agent to relay the entire envelope back to you, which is the most fragile part of the contract. A split prompt is not the standard payload — see rule 4 below. - Check that "fresh" is actually fresh. On some hosts a spawned sub-agent inherits the parent conversation by default, so calling it fresh does not make it so — an agent holding your reasoning will confirm you, and it costs more than an isolated one. If your sub-agent tool has a context/history parameter, set it to inherit nothing. Forge's per-step advisory names the exact parameter where it knows it.
- Reuse the reviewer, never the author. Do not resume an agent that helped build the thing. Do resume the independent reviewer you already spawned this run — it is still independent, and it saves re-reading the same diff on every review step. A run with three judgments over one diff should pay one cold read, not three.
- Brief it with the artifact and the standard, never your conclusions, and surface what it found before you reconcile it. Knowing what you meant to write is not evidence that the code does it.
When you cannot spawn one, run it inline and label it: "Reviewed on my own context — this is a self-review." A self-review is a fine outcome; a self-review that reads as an independent one is not.
Rules
- Always check — if
**Model Routing**is present, evaluate it before executing the step instructions - Delegate both up and down — if the tier says "capable", use your strongest available model; if it says "fast", use your lightweight model
- Announce delegation — briefly tell the user before delegating (e.g., "Delegating to a balanced model for this step...")
- Pass the full prompt — when the WHOLE step goes over — everything below
---DELEGATE BELOW---is the delegated prompt; include all of it. If you are splitting the step instead (handing over a judgment per the independence check, or a bounded read per the step's rule 5), do NOT forward theSTEP BOUNDARYdirective: that is what makes a sub-agent callforge__update_stateand advance, which rotates thestep_tokenout from under your own call for the same step. Send the artifact, the standard and the analysis portion only, and tell it to return findings as text and call noforge__tool - Everything ABOVE the delimiter stays with you — it is addressed to the
parent, not the sub-agent. In particular, a
<<<FORGE_DISPLAY_VERBATIM …>>>block above the delimiter is content for the user (the "Step N of M" progress marker): render it verbatim BEFORE you delegate, and never fold it into the delegated prompt. See "Must-display blocks" below - Delegate when it pays for itself, not reflexively — for real work
that's off your tier, delegating up buys better reasoning and down saves
real cost. But each step's advisory carries a Worth-it check: when the
remaining work is only replaying content already in workflow state (a
pre-computed
pending_*post, no new generation), run it inline on your current model — spawning a sub-agent for that just burns a round-trip. Follow the per-step Worth-it check; don't skip delegation merely because direct execution is easier
Must-display blocks
Forge marks content that is meant for the user — not for you — by wrapping it in a relay envelope:
> **Relay to the user** — render the block between the sentinels below ...
<<<FORGE_DISPLAY_VERBATIM id="position:step_<uuid>">>>
### Step 2 of 8: Discover AI SDLC
next: ... · then: ...
---
<<<END FORGE_DISPLAY_VERBATIM>>>Rules:
- Render what is between the sentinels, verbatim — never the sentinels themselves, and never a summary. It is the user's only view of where the run has got to.
- Render it before anything else in that turn — before analysis, before delegating, before your next tool call.
- It is always the parent's job. These blocks sit OUTSIDE the
<<<FORGE_NEXT_STEP>>>envelope and above---DELEGATE BELOW---, so a sub-agent never receives one as part of its prompt. If you ARE a sub-agent and one appears in a tool result you got, relay it to your parent unrendered along with the envelope — your parent is the one talking to the user. - Display ids are event identities.
preflightidentifies the once-per-run brief.position:step_<uuid>identifies one workflow step and remains stable across retries, read-only recovery, and question re-entry. Render an id the first time you see it. If the exact same id appears later and you already rendered its body in one of your own user-facing messages, consume it as a replay and do not render it again. A new id is always a new event.
On Cursor this is the only mechanism. Other Forge clients back this up with
a hook that surfaces the block automatically; Cursor's postToolUse has no
user-visible output channel, so if you do not render it, the user never sees
where the run has got to.
Step 5: Coexist with planning / read-only modes
Some environments enter a planning or read-only mode for non-trivial work (for example, Cursor's Ask mode answers without editing, and some setups gate writes behind plan approval). In such a mode, non-readonly tool calls are restricted and you are expected to present a plan for the user to approve before any writes happen.
When a read-only / planning mode is active AND a Forge workflow is requested, both protocols apply — they are not in conflict:
- Routing still applies — call
forge__start_workflowto fetch the right workflow. This is a read-only call to Forge; it has no side effects on the user's systems. - Execute only the read-only operations from the workflow's instructions — typically the early "fetch context" steps that read from the project tracker, codebase, or documentation tools.
- Defer writes — surface every write the workflow would normally
perform (status updates via
save_issue, ticket comments, code edits,forge__update_statecalls) as part of the plan you present to the user. Do NOT execute those writes while the read-only mode is active. - After the mode exits (the user approves the plan), resume the
workflow from where you paused — call
forge__update_stateto advance through the deferred steps in order until the workflow completes.
Why both protocols are compatible: a read-only mode constrains which tools you may call, not which workflows you may consider. Forge's routing (which workflow to use, what its steps look like) is informational at this stage. Writes are deferred, not skipped — the workflow completes normally once the mode releases you to act.
Anti-pattern to avoid: do NOT abandon the Forge workflow mid-step
because a read-only mode is active. If you've called forge__start_workflow
and read the step instructions, complete the read-only portions and present
the deferred writes in your plan — do not pivot to a parallel
investigation that ignores the workflow. Abandoning closes the run for
good: the steps that had not run are recorded as not run, the audit trail
records the abandonment, and nothing resumes it — the user has to start
over.
What NOT to route
The line is the Trigger rule in Step 2, nothing else: route only a current message that asks Forge for something by name. Without that, handle the request normally — whatever it is about:
- "Write a function that..." / "refactor this component" / "add a test for..."
- "Read this file" / "explain this code" / "what does this error mean?"
- "Commit my changes" / "push to main"
- "Plan the release", "review this PR against the requirements", "break PROJ-123 into stories" — product-development work too, but not a request for Forge until the user names it
- "Review this PR directly, without using Forge" — the user has said no
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

