# Oneshot

> Lightweight workflow for straightforward changes — plan → implement → optional PR. Direct-commit by default; synthesize is opt-in via synthesisPolicy or a runtime request_synthesize event. Use for trivial fixes, config tweaks, single-file changes, or exploratory work that doesn't warrant subagent dispatch or two-stage review. Triggers: 'oneshot', 'quick fix', 'small change', or oneshot.

- Skill: `lvlup-sw/oneshot` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add lvlup-sw/oneshot`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lvlup-sw/oneshot/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: lvlup-sw (https://skillmd.com/u/lvlup-sw)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/lvlup-sw/oneshot

---


# Oneshot Workflow Skill

## VCS Provider

This skill uses VCS operations through Exarchos MCP actions (`create_pr`, `merge_pr`, etc.) when the synthesize path is taken.
These actions automatically detect and route to the correct VCS provider (GitHub, GitLab, Azure DevOps).
No `gh`/`glab`/`az` commands needed — the MCP server handles provider dispatch.

A lean, four-phase workflow type for changes that are too small to justify the
full `feature` flow (`plan → plan-review → delegate → review → synthesize → completed`)
but still deserve event-sourced auditability and a planning step. The workflow is
**direct-commit by default** with an opt-in PR path; the choice between the
two is resolved at the end of `implementing` via a pure event-sourced guard,
not a heuristic.

> **Read this first if you have never run a oneshot:** the workflow has a
> *choice state* at the end of `implementing`. Whether you land on
> `completed` (direct commit) or transition through `synthesize` (PR) is
> decided by two inputs: the `synthesisPolicy` set at init, and whether the
> user emitted a `synthesize.requested` event during implementing. Both
> inputs are persisted; the decision is replay-safe.

## When to use oneshot

Reach for oneshot when **all** of the following are true:

- The change is bounded — typically a single file, or a tightly-coupled
  cluster of 2-3 files
- No subagent dispatch is needed — the work fits comfortably in a single
  in-session implement-and-verify loop
- No design document is required — the goal is obvious from the task
  description, and a one-page plan is enough scaffolding
- No two-stage review is required — either the change is trivial enough
  that direct-commit is acceptable, or a single PR review will suffice

Concrete examples that fit oneshot:
- Fixing a typo in a README
- Bumping a dependency version
- Adding a missing null-check in one function
- Tweaking a CI workflow YAML
- Renaming a config key everywhere it's referenced
- Adding a one-off helper script
- Exploratory spikes that may or may not be kept

## When NOT to use oneshot

Do not use oneshot for any of the following — use the full `feature`
workflow instead (`ideate`):

- Cross-cutting refactors that touch many files or modules
- Multi-file features that benefit from subagent decomposition
- Anything that needs design exploration or competing approaches weighed
- Anything that needs spec-review + quality-review (two-stage)
- Anything that needs to coordinate with another agent team
- Changes that should land in stages (stacked PRs)
- Anything where you'd want a written design doc to look back at

If you start a oneshot and discover the change is bigger than expected, the
right move is `cancel` and restart with `ideate`.
Don't try to grow a oneshot into a feature workflow mid-stream; the
playbooks have different shapes and you'll fight the state machine.

## Synthesis policy — three options

The `synthesisPolicy` field on a oneshot workflow declares the user's
intent up front about whether the change should be turned into a PR. It
takes one of three values, persisted on `state.oneshot.synthesisPolicy`:

| Policy | Behavior | When to use |
|---|---|---|
| `always` | Always transition `implementing → synthesize` at finalize, regardless of events. A PR is always created. | The user wants a paper trail / review for every change in this workflow, even small ones. |
| `never` | Always transition `implementing → completed` at finalize, regardless of events. No PR is created — commits go directly to the current branch. | The user is iterating on personal/scratch work and explicitly opts out of PRs. |
| `on-request` *(default)* | Direct-commit by default. The user can opt in to a PR mid-implementing by calling `request_synthesize`; if any `synthesize.requested` event is on the stream at finalize, the workflow transitions to `synthesize` instead of `completed`. | The common case: start with the assumption of direct-commit, but leave the door open for the user to change their mind once they see the diff. |

The default is `on-request` because it's the least surprising: the user
gets the lightweight path until they explicitly ask for the heavy one.

**Policy wins over event.** If `synthesisPolicy: 'never'` is set and a
`synthesize.requested` event is somehow on the stream (e.g. the user
called the action on a workflow they thought was `on-request`), the
guard still routes to `completed`. Policy is the user's declared intent
and overrides runtime signal.

## Lifecycle

```text
     plan ──────► implementing ──┬── [synthesisOptedOut] ──► completed
                                 │
                                 └── [synthesisOptedIn]  ──► synthesize ──► completed
```

Four phases. The fork after `implementing` is a UML *choice state*,
implemented via two mutually-exclusive HSM transitions whose guards are
pure functions of `state.oneshot.synthesisPolicy` and the
`synthesize.requested` event count.

| Phase | What happens | Exit criteria |
|---|---|---|
| `plan` | Lightweight one-page plan: goal, approach, files to touch, tests to add. No design doc. No subagent dispatch. | `artifacts.plan` set → transition to `implementing` |
| `implementing` | In-session loop, verification scaled to risk. Defaults to the low tier (static analysis suffices); higher-blast changes add tests covering the changed behavior (test-after) under the `check_test_adequacy` kill-probe. Commit as you go. | Tests pass + typecheck clean + finalize_oneshot called |
| `synthesize` | Reached **only** when `synthesisOptedIn` is true. Hands off to the existing synthesis flow — see `@skills/synthesize/SKILL.md`. PR created via `exarchos_orchestrate({ action: "create_pr" })`, auto-merge enabled, CI gates apply. | PR merged → `completed` |
| `completed` | Terminal. For direct-commit path, commits are already on the branch — there's nothing more to do. For synthesize path, the PR merge event terminates the workflow. | — |

`cancelled` is also reachable from any phase via the universal cancel
transition, same as every other workflow type.

## Step-by-step

### Step 1 — Init

Call `exarchos_workflow` with `action: 'init'`, `workflowType: 'oneshot'`,
and an optional `synthesisPolicy`:

```typescript
exarchos:exarchos_workflow({
  action: "init",
  featureId: "fix-readme-typo",
  workflowType: "oneshot",
  synthesisPolicy: "on-request" // optional — defaults to 'on-request'
})
```

If the user has been clear up front ("I want a PR for this"), pass
`synthesisPolicy: "always"`. If they've been clear ("don't open a PR,
just commit it"), pass `synthesisPolicy: "never"`. Otherwise, omit the
field and rely on the `on-request` default — you can always escalate
later in the implementing phase.

The init returns the new workflow state; the workflow lands in `plan`.

### Step 2 — Plan phase

Produce a one-page plan. This is intentionally lightweight — no design
doc, no parallelization analysis, no decomposition into N tasks. The
plan should answer four questions in 5-10 lines each:

1. **Goal** — what is the user trying to accomplish?
2. **Approach** — what's the one-line implementation strategy?
3. **Files** — which files will be touched? (1-5 typically)
4. **Tests** — which test cases will be added? (named, not described)

Persist the plan, then transition to `implementing`. Field mutation and phase
mutation are **separate actions**: `update` writes the plan artifacts (`phase`
is a reserved field `update` cannot touch), then `transition` advances the
phase — guards read the most recent state, so the plan lands before the
`oneshot-plan-set` guard evaluates:

```typescript
exarchos:exarchos_workflow({
  action: "update",
  featureId: "fix-readme-typo",
  updates: {
    "artifacts.plan": "<plan text>",
    "oneshot.planSummary": "<one-line summary>"
  }
})

exarchos:exarchos_workflow({
  action: "transition",
  featureId: "fix-readme-typo",
  target: "implementing"
})
```

The plan goes on `artifacts.plan` for parity with the `feature` workflow;
the human-readable one-liner goes on `oneshot.planSummary` for the
pipeline view. Only `artifacts.plan` is enforced by the
`oneshot-plan-set` guard — `planSummary` is an optional pipeline-view
label and is not a substitute for a real plan artifact.

### Step 3 — Implementing phase

Run an in-session loop, with verification scaled to the change's risk (the
ladder in `@skills/_shared/references/verification.md`). A oneshot defaults to
the **low tier** — static analysis suffices for a trivial fix. When the change
touches a higher-blast surface (schema/type/API/shared contract, or it crosses
an I/O boundary), it is a higher-tier task: cover the new/changed behavior with
tests (test-after is fine) and expect the `check_test_adequacy` kill-probe to
reject tests that cannot actually fail.

For each higher-tier behavior in the plan:

1. Implement the behavior.
2. Add the tests its tier requires (scoped tests for the change; an integration
   test across the seam at high tier), named `Method_Scenario_Outcome`.
3. Refactor while the tests stay green.

Granular per-behavior red-green is available as an explicit opt-in, never a
requirement.

Commit each logical change as a single atomic commit. Do not batch
multiple unrelated changes into one commit — keeping commits atomic
matters even more in oneshot, where there's no separate review phase
to catch bundled changes.

There is **no subagent dispatch** in oneshot. The main agent does the
work directly. There is **no separate review phase**. Quality is
maintained by the tier-scaled verification (static analysis plus, above the
low tier, the `check_test_adequacy` kill-probe) and (if the user opts in)
the synthesize PR review.

#### Mid-workflow: opting in to a PR

If at any point during `plan` or `implementing` the user decides they
want a PR after all (policy is `on-request`, default), they can opt in
by calling the `request_synthesize` orchestrate action:

```typescript
exarchos:exarchos_orchestrate({
  action: "request_synthesize",
  featureId: "fix-readme-typo",
  reason: "user requested review of the parser changes"
})
```

**The trigger for this is conversational, not a magic keyword.** Listen
for phrases like:
- "actually, let's open a PR for this"
- "I want a review on this before it lands"
- "make this a PR"
- "let's get eyes on this"
- "synthesize this"

When you hear any of those, call `request_synthesize` immediately. The
handler appends a `synthesize.requested` event to the workflow's event
stream; the `synthesisOptedIn` guard reads the stream at finalize and
routes accordingly.

`request_synthesize` is accepted from both `plan` and `implementing`
phases — call it whenever you know you want the PR path, even before
`implementing` starts. Terminal phases (`synthesize`, `completed`,
`cancelled`) are rejected.

Duplicate calls are **routing-idempotent** but not event-idempotent:
each call appends a new `synthesize.requested` event, but the guard
treats any count >= 1 as "opted in", so the routing decision is the
same whether you call once or five times. The event stream will
contain each duplicate for audit purposes.

Calling `request_synthesize` does **not** transition the phase. The
workflow stays in its current phase. The decision is only acted on
when you call `finalize_oneshot` in step 4.

### Step 4 — Finalize (the choice point)

When the implementing loop is done — tests pass, typecheck clean, all
commits made — call `finalize_oneshot` to resolve the choice state:

```typescript
exarchos:exarchos_orchestrate({
  action: "finalize_oneshot",
  featureId: "fix-readme-typo"
})
```

The handler:

1. Reads the current state and verifies `workflowType === 'oneshot'` and
   `phase === 'implementing'`.
2. Hydrates `_events` from the event store so the guard sees the same
   view the HSM will see during the actual transition.
3. Evaluates `guards.synthesisOptedIn` against the state. The guard
   inspects `state.oneshot.synthesisPolicy` and the `_events` array.
4. Calls `handleSet` with the resolved target phase (`synthesize` or
   `completed`). The HSM re-evaluates the guard at the transition
   boundary, so any race between the read and the transition is caught
   safely.

Possible outcomes:

| `synthesisPolicy` | `synthesize.requested` event present? | Resolved target | Path |
|---|---|---|---|
| `always` | (any) | `synthesize` | PR path |
| `never` | (any) | `completed` | direct-commit path |
| `on-request` (default) | yes | `synthesize` | PR path |
| `on-request` (default) | no | `completed` | direct-commit path |

### Step 5a — Direct-commit path (terminal)

If finalize resolved to `completed`, you're done. The commits made
during implementing are already on the current branch. Push them if
they aren't already pushed:

```bash
git push
```

The workflow is now in `completed` and will not appear in the default
pipeline view. Report completion:

```markdown
## Oneshot Complete (direct-commit)

Workflow: <featureId>
Plan: <one-line summary>
Tests added: N
Commits: <hash list>
Path: direct-commit
```

### Step 5b — Synthesize path

If finalize resolved to `synthesize`, hand off to the standard synthesis
flow — see `@skills/synthesize/SKILL.md`. The same `prepare_synthesis` /
`validate_pr_body` / `create_pr` machinery used by the `feature`
workflow applies, including `prepare_synthesis`'s required `repoRoot`:
pass the absolute path of the repo you have been committing into, since
every readiness leg runs there rather than in the server's own working
directory. After the PR merges, the workflow transitions
`synthesize → completed` via the existing `mergeVerified` guard, same as
every other workflow type.

You do **not** need to run `delegate` or
`review` for an opt-in oneshot synthesize. Those phases
do not exist in the oneshot playbook. The PR review is the only review.
Report completion:

```markdown
## Oneshot Complete (synthesize)

Workflow: <featureId>
PR: <url>
Tests: X pass | Build: 0 errors
Path: synthesize → PR review → merge
```

## Example invocations

### Example A — Direct-commit (default `on-request` policy, no opt-in)

```text
User: "Quick fix — there's a typo in the README, 'recieve' should be 'receive'.
       Use oneshot."

Agent:
  1. exarchos_workflow init { featureId: "fix-readme-typo", workflowType: "oneshot" }
     → workflow created in 'plan' phase, synthesisPolicy defaults to 'on-request'
  2. Produces a 4-line plan: goal=fix typo, approach=sed, files=[README.md],
     tests=[readme has no occurrence of 'recieve']
  3. exarchos_workflow update {
       featureId: "fix-readme-typo",
       phase: "implementing",                   // top-level
       updates: {
         "artifacts.plan": "...",
         "oneshot.planSummary": "..."
       }
     }
  4. edits README, fixes typo (low tier — a docs one-liner; static analysis
        suffices, no test-first ceremony)
  5. verifies: greps README for 'recieve' → 0 matches
  6. git commit -m "docs: fix 'recieve' typo in README"
  7. exarchos_orchestrate finalize_oneshot { featureId: "fix-readme-typo" }
     → guard sees policy='on-request' + no synthesize.requested event
     → resolves to 'completed'
  8. git push
     "Done. Workflow completed via direct-commit path."
```

### Example B — Mid-implementing opt-in (`on-request` → user changes mind)

```text
User: "Add input validation to the parseConfig helper. Oneshot."

Agent:
  1. exarchos_workflow init { workflowType: "oneshot" }
     → 'plan' phase, default 'on-request'
  2. plans (goal: validate input, files: [parser.ts, parser.test.ts])
  3. transitions to 'implementing'
  4. adds the null-input validation to parseConfig
  5. adds a test for the null-input case (test-after)
  6. refactors: extracts validateInput helper (tests stay green)
  7. commits

User: "Actually, this is touching the parser — I want a review on it before
       it lands."

Agent:
  8. exarchos_orchestrate request_synthesize {
       featureId, reason: "user requested review of parser changes"
     }
     → appends synthesize.requested event
  9. exarchos_orchestrate finalize_oneshot { featureId }
     → guard sees policy='on-request' + 1 synthesize.requested event
     → resolves to 'synthesize'
 10. hands off to @skills/synthesize/SKILL.md → prepare_synthesis →
     validate_pr_body → create_pr → merge
```

### Example C — `synthesisPolicy: 'always'` (PR mandatory)

```text
User: "I want a PR for any change to the auth module, even small ones.
       Use oneshot but always make a PR."

Agent:
  1. exarchos_workflow init {
       workflowType: "oneshot",
       synthesisPolicy: "always"
     }
  2-7. plan + implement + verify + commits, identical to Example A
  8. exarchos_orchestrate finalize_oneshot { featureId }
     → guard sees policy='always' (short-circuits — no event check needed)
     → resolves to 'synthesize'
  9. synthesis flow → PR
```

## State management

Track oneshot-specific state under the `oneshot` key on the workflow state:

```typescript
exarchos:exarchos_workflow({
  action: "update",
  featureId: "<id>",
  updates: {
    "oneshot": {
      "synthesisPolicy": "on-request",
      "planSummary": "Fix off-by-one in pagination helper"
    }
  }
})
```

The `synthesisPolicy` field is optional and defaults to `'on-request'` per
the schema in `servers/exarchos-mcp/src/workflow/schemas.ts`. Setting it
explicitly is recommended when the user has stated a preference.

## Phase Transitions and Guards

For the full transition table for oneshot, consult
`@skills/checkpoint/references/phase-transitions.md`.

> **Note:** The engine's `exarchos_workflow describe playbook="oneshot"`
> output and the HSM definitions in `hsm-definitions.ts` are the
> canonical sources for transition behavior. `phase-transitions.md` is
> a prose reference that can drift — when discrepancies arise, prefer
> engine `describe` output.

**Quick reference for oneshot:**

| From | To | Guard |
|---|---|---|
| `plan` | `implementing` | `oneshotPlanSet` (requires non-empty `artifacts.plan`) |
| `implementing` | `synthesize` | `synthesisOptedIn` |
| `implementing` | `completed` | `synthesisOptedOut` |
| `synthesize` | `completed` | `mergeVerified` |
| (any) | `cancelled` | universal — always allowed |

`synthesisOptedIn` and `synthesisOptedOut` are pure functions of
`state.oneshot.synthesisPolicy` and `state._events`. They are mutually
exclusive across all 4 meaningful combinations — `always` and `never`
each map to one outcome (ignoring the event flag), and `on-request`
branches on whether a `synthesize.requested` event is present or
absent. Exactly one guard returns true at any given time.

### Schema discovery

Use `exarchos_workflow({ action: "describe", actions: ["init", "update"] })`
for parameter schemas (including the `synthesisPolicy` enum) and
`exarchos_workflow({ action: "describe", playbook: "oneshot" })` for the
phase transitions, guard names, and playbook prose. Use
`exarchos_orchestrate({ action: "describe", actions: ["request_synthesize", "finalize_oneshot"] })`
for the orchestrate action schemas.

## Verification scales with risk

The verification ladder from `@skills/_shared/references/verification.md` applies to
oneshot. A oneshot **defaults to the cheap (low) tier** — a one-line fix or config
tweak leans on static analysis (typecheck + lint), no test-first ceremony — *unless
risk signals say otherwise*. Specifically:

- A low-risk change (docs/config/rename-only, near-zero blast radius) needs only
  static analysis to pass; add a focused test only if behavior is non-obvious.
- If the change touches a higher-blast surface (schema/type/API/shared contract,
  or it crosses an I/O boundary), it is **not** a low-tier task: cover the
  new/changed behavior with tests (test-after is fine), and expect the
  `check_test_adequacy` kill-probe to flag tests that cannot actually fail.
- Commits stay atomic — one logical change per commit, regardless of tier.

The temptation in a oneshot is to assume *everything* is "just one line" and skip
verification entirely. Resist that for anything past the low tier. The point of
oneshot is auditability at the *right* cost — not zero verification, and not the
full feature ceremony on a trivial edit.

## Anti-patterns

| Don't | Do Instead |
|-------|------------|
| Skip the plan phase ("it's obvious") | Write the four-line plan anyway — it's the artifact future-you reads |
| Apply full red-green-refactor ceremony to a trivial one-liner | Match verification to risk: low-tier leans on static analysis; higher-blast changes add tests covering the changed behavior |
| Skip verification entirely because "it's a oneshot" | Even low-tier must pass static analysis; higher-blast changes still need tests covering the changed behavior |
| Use oneshot for multi-file refactors | Use `ideate` and the full feature workflow |
| Try to grow a oneshot into a feature workflow mid-stream | Cancel and restart with `ideate` |
| Call `request_synthesize` without listening for the user's intent | Wait for the user to ask for a PR, then call it |
| Bundle unrelated changes into one commit "since it's a oneshot" | Keep commits atomic — there's no review phase to catch bundling |
| Forget to call `finalize_oneshot` at the end | The workflow stays in `implementing` forever otherwise — call it explicitly |

## Completion criteria

- [ ] `exarchos_workflow init` called with `workflowType: "oneshot"`
- [ ] One-page plan persisted to `artifacts.plan`
- [ ] Phase transitioned to `implementing`
- [ ] All planned behaviors implemented and verified to their risk tier, with atomic commits
- [ ] `finalize_oneshot` called and resolved to either `completed` or `synthesize`
- [ ] If direct-commit path: commits pushed
- [ ] If synthesize path: PR created via `@skills/synthesize/SKILL.md` and merged

