Repository navigation
[feat] Add the session skill driver and invocable workflows #1086
Description
Parent: #818
Goal
Let an agent in an existing coding session run team workflows: it orchestrates, and a deterministic CLI state machine drives the public lease loop. No other runtime is deployed, and nothing outside the engine reads the graph.
Scope
The decisions of 2026-10-24 (comment below) replace the earlier SessionStart and invocable items.
-
invoke: allow | ask | deny, defaultdeny, plus optionalalias.invokelimits agents only, and replaces the earlierinvocable.denygenerates no skill.invokeAgent session (any driver; --driver skillalways counts)A person's terminal, local driver CI without an agent session, local driver Sub-workflow allowStarts Starts Starts Starts askStarts once the person agrees (describe, then --confirmed). Unattended:denied, also with--confirmedStarts Starts Starts deny, the defaultRefused Starts Starts Starts A process with an agent session variable is an agent session, in CI too.
resumeandnext --takeoverfollow the same rule, except for a run the same session started and drives. -
Discovery through generated skills.
pullgenerates oneworkflow-<name>skill per workflow an agent may start (alloworask). It carries the workflow's description and alias and routes toteamai skill get workflow. Generated skills follow the team-skill pipeline, are marked as generated, never enterpush, andpullprunes them. No SessionStart workflow list. -
The skill driver (
--driver skill). A file-backed state machine run throughteamai workflow. It reaches the engine only through the public CLI (next,report,respond), and returns literal commands for the agent to run. Prompt steps go to child agents, interactive steps stay with the person in the session, and command steps run in detached runners with only the step'srequires.envkeys. Judging goes throughteamai score --next. -
Workspaces. TeamAI owns the workspace contract and the host owns the place.
next --preparecreates a worktree, or adopts one the host made (--adopt), andreportcommits, pushes and cleans up, through one workspace module shared by every driver. -
A capability map, not tool names. Each dispatch states what it needs (isolation, parallelism, a person). The agent maps it to the best primitive its host offers.
driver.candeclares only what the host provides. -
No nested starts.
workflow startfails withnested_workflowinside an attempt (marker in the environment) or a workflow worktree. Both drivers export the marker to every process they launch. -
Run modes.
resolveRunMode:TEAMAI_MODE(attended|unattended) >CI>attended. The skill driver resolves the mode once and keeps it in its state. Inunattended,--describeanswers the recommended start with--auto-approve, ordeniedfor required approvals, interactive steps,invoke: askand choices without a default. An unattendedresumeor takeover with the skill driver decides the same way from the steps the run has left, before it claims anything. Every process TeamAI starts for a step getsTEAMAI_MODE. Judging in the session keeps running; a judge elsewhere does not block the run and is left toscore --next. -
Published runs. A run published from CI or another machine is continued from a session with
teamai workflow resume <run> --driver skill(--claimwhen another install owns it). Claiming is the person's decision. -
Guides.
skill-data/workflow(the orchestrator guide, with references loaded on demand) andskill-data/workflow-local(unattended runs in CI or cloud agents, with a CI reference).
Out of scope
- Offered runs and
resume --next([feat] Hand runs between machines, and run teamai in CI without a user token #1390). - Default workflows and
session-close([feat] Default workflows a team can override, starting with session-close #1388). teamai score --drainand its line in the skill's unattended paragraph ([feat] Publish and score sessions by run mode #1385).- A second deployed skill, graph traversal, provider approval and event waits.
Dependencies
- Blocked by: [feat] Run prompt and agent steps with the local driver #1077, [feat] Add manual run controls and human gates #1081, [feat] Add workflow workspaces, branches, and session continuation #1080, [feat] Enforce workflow requirements and execution limits #1082, [feat] Close the #1080 gaps that #1175 leaves open #1246
- Unblocks: [feat] Add a Paseo driver adapter #1087, [feat] Run mode: tell attended runs from unattended ones #1384, [feat] Publish and score sessions by run mode #1385, [feat] Default workflows a team can override, starting with session-close #1388, [feat] Hand runs between machines, and run teamai in CI without a user token #1390
Acceptance criteria
- Only
allowandaskworkflows get a generated skill.denyworkflows refuse an agent's start with any driver. - The skill reads CLI help and the generated reference instead of duplicating flags.
- The skill driver uses public workflow commands only and never reads the graph.
- A lease whose workspace mode or requirement the host does not declare is never handed to the session, and stays available to another driver.
- No agent sees a secret value: command steps get only their
requires.envkeys. - Required approval is never agent-mediated.
workflow startinside an attempt or a workflow worktree fails withnested_workflow.- An unattended run asks nobody.
next,reportandrespondaddress a run by id from a workflow worktree and from outside the product repo.
Verification
Unit tests for the transition table, discovery, invoke, places, run modes and claim; a model-based test of the state machine; build, typecheck and lint; real-CLI E2E for a session run, a published-run claim across two checkouts, and an unattended run.
Activity
SaulMoro commentedon Oct 14, 2026
Looking up a run by id regardless of the current directory is in #1087, but the skill driver needs it first. A session started outside the product checkout gets
run_not_found, becauseworkflow-commands.tsresolves the run throughautoDetectInit. That criterion could move here, or this issue could be blocked by #1087.SaulMoro commentedon Oct 14, 2026
Notes from #1078 (#1129) for the skill driver:
nextsettles them before handing out a lease. The session only sees leases.stepis a path (ship.check.classify) for steps inside sub-workflows. Treat it as an opaque id.env, a map of already-rendered strings. Set them for the command; never paste them into the command text. Inputs reach a command only this way.workflow start --inputs '<json>'passes a workflow's declared inputs; an invocable workflow with required inputs needs the session to collect them first.env, sub-workflow paths and recovery after a failure; a driver that passes it handles graph runs.The engine rules that span files are in
docs/dev/workflow-engine.md, added by #1129.hsuchifeng commentedon Oct 15, 2026
Move run lookup by id into this issue. The session skill driver must work outside the product checkout, so #1086 should not wait for #1087.
SaulMoro commentedon Oct 16, 2026
A lease for a prompt step will carry two model fields (#1177):
model, the step's own, anddefaultModel, the workflow'sdefaults.modelin effect for the step. Each is an alias or a model name, resolved per tool with the member's aliases.The driver picks the model, first match wins: the step's
model, the model set by the definition deployed for its tool,defaultModel, the tool's default. A default fills a gap and never replaces a model a definition sets for its role. Only the driver can read the deployed definition, so this order cannot be applied before the lease.When the first match is the step's
modelordefaultModeland that model cannot run, the driver fails the step withmodel_unavailable. That covers an alias with no model for the tool, and a driver that cannot switch models, such as a session that runs every step on its own model. It does not run the tool's default, which would record a result for a model the step did not ask for. It does not release the lease either, sincenexthands a released step straight back to the same driver. No capability says which models a driver can run, so this lease does not stay available to another driver the way a declined requirement does. How a driver applies a model that only the definition sets does not change.SaulMoro commentedon Oct 20, 2026
Now blocked by #1080 and #1082. The session driver takes its leases from both, so its scope changed with them.
nextholds aneworcontinuelease back from a driver whosecan.workspacelacks that mode. A driver that releases a lease gets it back on its nextnext, so declining it in the driver cannot work. A host that can create worktrees and run agents in parallel, such as Claude Code, can take these leases. The driver then declares the modes and meets the lease's git contract.git worktree add(worktree.tsin feat(workflow): add workspace branches #1175). It pointscore.hooksPathat an empty directory, replacesAGENTS.md,CLAUDE.md,.claudeand the other agent paths with the run's base commit, gives the worktree the inlinepullwithprepareCheckout, then commits, pushes and reportschanges. A session that creates the worktree with its host's own tool skips the base overlay. A later step then loads hooks and agents that an earlier step committed, which is what [feat] Add workflow workspaces, branches, and session continuation #1080 rules out for a step that holds a token. Either the session reuses the local driver's preparation through a public command, or the skill spells out each step and the conformance suite checks it. The first keeps one implementation. Following the Occam rule, it would extend an existing workflow command, not add a new one.SaulMoro commentedon Oct 20, 2026
Decision on the open question above: the session reuses the local driver's workspace preparation, extending the existing
workflow nextandworkflow report, with no new command.nextprepares the worktree for aneworcontinuelease the waylocal-driver.tsdoes in feat(workflow): add workspace branches #1175. It creates the worktree, merges the dependency branches, pointscore.hooksPathat the empty directory, applies the run's base agent configuration, and runs the inlinepull. The lease then names the directory.reportcommits what the step left, pushes, fillschanges, and removes the worktree whencleanupis set.Why not let the host create the worktree: the worktree starts from a branch an earlier agent wrote. Hooks committed on it (
.githooks,.husky) would run on checkout, merge, commit or push with the member's credentials. Without the base overlay, a later step would also load agents and hooks an earlier step committed. Reusingworktree.tskeeps that protection in one implementation, and the conformance suite checks one code path.How the session asks
nextto prepare the workspace (a field in the--jsondriver payload or an option) is left to the implementation. A driver that prepares its own workspaces, such as a sandbox driver, must not get it by default.SaulMoro commentedon Oct 20, 2026
workflow start.can.judge(proposed there), this driver declares it only once its host has a profile [feat] Add workflow scorers and recorded quality labels #1085 verifies, prepares the evidence where the judged pass ran, and runs the judge apart from the session.SaulMoro commentedon Oct 22, 2026
For the skill driver: scorer work from #1085 does not come through
workflow next. A driver that can judge asks for it separately.Two rules for drivers: never list a judge ref in
workflow next'srunning, and settle a judge lease only with--submit. Conformance covers both.SaulMoro commentedon Oct 24, 2026
Decisions for the session skill driver, settled before #1304 is finished. They replace the SessionStart and
invocableparts of the scope above.next,report,respond) and imports nothing from the local driver. Workspace preparation and cleanup move out oflocal-driver.tsinto a shared workspace module that every driver reaches throughnext --prepareandreport.next --preparecan also adopt a directory the host created, such as a host worktree or sandbox: it checks the branch and base and applies the base overlay. TeamAI owns the workspace contract and the host owns the place. A host whose sandbox cannot run the CLI does not declareneworcontinue.teamai workflow, drives the lease loop (lease, dispatch, await, report, then wait, HITL or done). It keeps its state in a file, so it is idempotent and survives a context compaction, and it returns actions for the agent to carry out. It never reads the graph: the engine keeps dependencies, loops, fan-out, deadlines and budget. Prompt steps go to child agents. Interactive steps stay with the person in the session. Command steps run inside the state machine with only the step'srequires.envkeys ([feat] Enforce workflow requirements and execution limits #1082), so no agent sees a secret value. A host that cannot scope env leavesenvout ofcan.driver.cancomes from what the host declares.pullgenerates oneworkflow-<name>skill per advertised workflow. Its description is the workflow's, plus its alias and a lead that asks the agent to prefer the workflow over ad-hoc work. Its body routes toteamai skill get workflow. These skills follow the team-skill pipeline (scope, git exclude, git hooks, base overlay). They are marked as generated, never enterpush, andpullprunes them. The SessionStart workflow list goes away.invocablebecomesinvoke: allow | ask | deny, defaultdeny. Withallow, the agent starts the workflow on its own, and the generated skill pre-approvesteamai workflow start <name>. Withask, the agent sees the workflow, proposes it and waits for the person's yes. Nothing is pre-approved, and the body says so for hosts without permission prompts. Withdeny, no skill is generated, and the workflow runs only from CI or as a sub-workflow. Theteamaistub pre-approves only the loop and read subcommands (--describe,next,report,respond).workflow startfails withnested_workflowwhen an attempt marker is in the environment, or when the working directory is a workflow worktree. The local driver and the state machine export that marker to every process they launch. Each child's brief names its attempt. Sub-workflows run asworkflowsteps. One gap remains: a session child on a read-only step in the member's checkout, which only its brief covers.--driver skill.startwithout--driverstill only creates the run, for external drivers.skill-data/workflowholds the orchestrator guide, with references loaded only when an action needs them (for example, a VM or sandbox needs the CLI installed plusinitandpull).skill-data/workflow-localcovers unattended runs in CI or cloud agents, with a reference for writing CI pipelines that use it.SaulMoro commentedon Oct 25, 2026
FYI, nothing to change here: #1388 (
session-close) depends on the skill driver, and #1385 adds an unattended rule for drivers: drainteamai score --nextbefore exiting. Once this lands, the skill driver's skill-data needs that line; #1385 carries it.SaulMoro commentedon Oct 25, 2026
Update to my earlier comment: the skill driver's part of run modes now ships with #1304 itself.
resolveRunModeinsrc/launch-context.ts:TEAMAI_MODE>CI>attended.unattended,--describestarts with the recommended option and--auto-approve, or answersdeniedfor required approvals,interactivesteps andinvoke: ask.attemptEnvexportsTEAMAI_MODEto every process of an attempt, in both drivers.skill-data/workflow-local/references/ci.mdsays howCIandTEAMAI_MODEset the mode.#1384 extends that resolver and now depends on this issue. No drain line is added to the skill: #1385 adds its session work to the existing
done.judgingloop.SaulMoro commentedon Oct 25, 2026
Correction to my update above: a drain line is added after all, by #1385.
#1385 adds
teamai score --drainand one line to the unattended paragraph ofskill-data/workflow/SKILL.md: runteamai score --drainbefore the session ends.Session judging and publishing do not go into
done.judging. That loop keeps its shape and stays the run's own pass judging. Nothing else in PR #1304 changes.feat(workflow): add session discovery and managed public leases (Tenc…