workmachine skill
scripts서브에이전트와 사전 정의된 전이 테이블을 사용하여 명시적인 이벤트 기반 상태 머신으로 복잡한 작업을 실행합니다.
- GitHub 스타
- 28
- 파일
- 13
- 마지막 커밋
- 2026년 11월 11일
- 라이선스
- MIT
- 허용된 도구
- —
workmachine skill은 무엇을 하나요?
workmachine은(는) agent skill입니다. 서브에이전트와 사전 정의된 전이 테이블을 사용하여 명시적인 이벤트 기반 상태 머신으로 복잡한 작업을 실행합니다.
skill의 SKILL.md에서 생성된 요약입니다.
workmachine skill 정보
workmachine은 개발자가 명시적인 이벤트 기반 상태 머신을 통해 복잡하고 다단계인 목표를 실행할 수 있도록 지원합니다. 에이전트의 유동적이고 예측 불가능한 의사결정에 의존하는 대신, 이 SKILL은 사전 정의된 전이 테이블을 사용하여 작업을 라우팅합니다. 오케스트레이터는 머신 초기화, 큐에서 이벤트 추출, 전이 조회, 이펙트 실행이라는 엄격한 루프를 따릅니다. 이 프로세스를 통해 상태 간의 모든 이동이 직관이 아닌 기계적이고 감사 가능한 방식으로 이루어집니다.
사용자가 이 SKILL을 트리거하면, 플래너 서브에이전트가 상태, 이벤트 알파벳, 예산이 포함된 머신 파일을 작성합니다. 그런 다음 런타임은 특정 역할 프롬프트 경로에 따라 서브에이전트를 배정하여 작업을 병렬로 수행합니다. 이러한 서브에이전트들은 아주 작은 구조화된 상태를 생성하며, 이는 정의된 규칙에 따라 단일 이벤트 토큰으로 통합됩니다. 이 방식은 빌드, 측정, 수정과 같이 단계별 명칭이 명확한 작업에 이상적이며, 실시간 판단이 아닌 공식적인 조회를 통해 한 단계의 결과가 다음 단계를 결정합니다.
agent가 수행할 수 있는 작업
- 1자동화된 소프트웨어 릴리스 파이프라인개발자가 AI에게 목표를 전달하면 코드 빌드부터 성공 측정, 최종 배포까지의 전환 과정을 관리할 수 있습니다.
- 2감사 가능한 작업 오케스트레이션모든 라우팅 결정이 이벤트 로그에 기록되어 나중에 검토할 수 있는 복잡한 워크플로우를 팀에서 실행할 수 있습니다.
- 3제어된 오류 복구 루프에이전트가 문제를 에스컬레이션하기 전에 실패한 측정을 특정 횟수만큼 재시도하여 수정할 수 있습니다.
skill의 SKILL.md를 기반으로 작성되었습니다. 모든 사용 사례는 해당 문서의 인용구와 대조 확인되었습니다.
workmachine skill 설치 방법
각 명령은 skill 폴더를 클라이언트의 skills 디렉터리에 복사합니다. 클라이언트를 재시작하거나 새 세션을 시작하여 로드하세요.Claude Code에 workmachine skill 설치
~/.claude/skills/workmachine/SKILL.md
$ npx degit av/skills/workmachine ~/.claude/skills/workmachineClaude Code (project)에 workmachine skill 설치
.claude/skills/workmachine/SKILL.md
$ npx degit av/skills/workmachine .claude/skills/workmachineCodex에 workmachine skill 설치
~/.agents/skills/workmachine/SKILL.md
$ npx degit av/skills/workmachine ~/.agents/skills/workmachineGemini CLI에 workmachine skill 설치
~/.gemini/skills/workmachine/SKILL.md
$ npx degit av/skills/workmachine ~/.gemini/skills/workmachineClaude.ai에 workmachine skill 설치
npx degit av/skills/workmachine workmachine && zip -r workmachine.zip workmachine # claude.ai → Customize → Skills → + → Create skill → Upload a skill → workmachine.zip
자동 검사
2026년 11월 14일에 실행됨전체 보고서 →아직 검증되지 않았습니다. 스킬은 점수 80, 심각한 발견 없음, 검사에 충분한 데이터가 필요합니다. 검사 방식 · 점수를 높이기 위해 수정할 항목
- 소스 저장소 연결 및 접근 가능av/skills/workmachine is public and reachablegithub.com/av/skills/tree/HEAD/workmachine ↗통과
- 오픈 소스 라이선스 (OSI)MITspdx.org/licenses/MIT.html ↗통과
- 최근 1년 내 커밋 또는 릴리스Last commit 2026-11-11github.com/av/skills/commits ↗통과
- 스크립트에 다운로드 후 실행이나 자격 증명 탈취가 없음1 script read; no download-and-run, remote eval, base64 execution or credential upload통과
- SKILL.md에 숨겨진 지침 없음No override phrases, safeguard switches, credential requests or hidden charactersgithub.com/av/skills/blob/HEAD/workmachine/SKILL.md ↗통과
- 사전 허용 도구가 제한됨No pre-approved tools; the agent asks before using onegithub.com/av/skills/blob/HEAD/workmachine/SKILL.md ↗통과
- SKILL.md가 Agent Skills 형식을 따름description is 1247 characters; the limit is 1,024github.com/av/skills/blob/HEAD/workmachine/SKILL.md ↗결함 발견
SKILL.md
GitHub에서 보기Workmachine — An Explicit State Machine, Executed by Subagents
state, effects = init(machine)
execute(effects)
while state is active:
event = nextEvent()
state, effects = transition(machine, state, event)
execute(effects)
return state.output
That is the whole orchestrator. Everything else in this skill exists to make each of those five names concrete:
| Runtime name | What it is in a workmachine run |
|---|---|
machine |
machine.md — states, event alphabet, transition table, budgets, final states. Authored once in Phase 1. Never edited at runtime except through the ESCALATE protocol. |
state |
one line in state.md — the current state name plus its visit count; and the output slot once a final state is reached. |
init |
run the scaffold, author the machine, enter the initial state, and evaluate that state's entry effects. |
effects |
a list of side-effects a state emits on entry: subagent dispatches (by role-prompt path), state.md/events.md writes, git spot-checks. Effects never decide anything. |
execute |
dispatch the effect's subagents in parallel, read their TINY statuses, spot-check commits. |
nextEvent() |
pop ONE event token from the queue in state.md. Tokens come only from the alphabet; they are produced by collapsing tiny statuses with the machine's event rules, or by the clock / the user. |
transition |
a pure table lookup: (state, event) → (next state, effects). Table miss → ESCALATE. Budget exhausted → the row's on_budget target. |
state.output |
the artifact pointer a final state records (commit / path / verdict). The run's return value. |
The one design commitment: judgment happens at plan time, in the machine file; the runtime is mechanical. If you find yourself deciding where to go next mid-run, the machine file is incomplete — stop, ESCALATE, fix the table once, resume. Never route by feel.
Your Role
You are the runtime. You do exactly five things — the five lines above:
init— scaffold the workspace, get the machine authored (by a subagent), lint it, enter the initial state, execute its entry effects.nextEvent— pop one token from the event queue; if the queue is empty, collapse the last batch's tiny statuses into a token using the machine's event rules; check the clock/user first (they preempt).transition— look the(state, event)pair up in the table. Record the visit and the transition inevents.md. No lookup, no move.execute— fire the target state's entry effects: dispatch subagents by role-prompt PATH + a short unit delta, in one parallel step; collect tiny statuses;git logspot-check any claimed commit.return— when the state is final, copystate.outputto the user as the final summary. Nothing else is ever written to the user mid-run.
You do no authorship and no analysis (the Orchestrator-Only Line, inherited from timeboxed-iterating): no code edits, no queries, no reading the codebase, no "quick fixes", no composing the machine file yourself. Even under context pressure, even after a blowout, every productive act is a subagent.
Pure dispatch — never ingest large content. Subagents persist results to their unit file, append findings to the cheatsheet, and append one line to the digest; they return a ONE-LINE status. You read the digest tail and those one-liners only. A return about to dump an artifact into your context is a Red Flag — it belongs in the unit file.
The Iron Laws
1. THE TABLE ROUTES. NOT YOU. Every move is a (state, event) lookup in machine.md. A miss is ESCALATE, never an improvisation.
2. EVENTS COME FROM THE ALPHABET ONLY. A token not in the alphabet cannot be queued. Statuses are collapsed by the written event rules.
3. EFFECTS DO NOT DECIDE. An effect dispatches, writes, or checks. It never chooses the next state.
4. EVERY CYCLE HAS A BUDGET. A backward transition without a budget + on_budget target fails lint.
5. THE ORCHESTRATOR NEVER AUTHORS. Not the machine, not the units, not the output. Subagents produce; you look up and dispatch.
6. INDEPENDENT EFFECTS RUN IN PARALLEL. A state whose entry effects are independent dispatches fires them in ONE batch (≤ concurrency cap).
Inputs
| Input | Required | Example |
|---|---|---|
| Goal | yes | "port the importer to the new API", "produce the release notes and verify them" |
| Repo / scope | yes | ~/code/pace; mutable vs frozen paths |
| Duration | optional | "2h", "overnight" = 8h — becomes a deadline event source |
| Success bar | recommended | numeric threshold OR textual rubric; drives the pass/fail event rule |
| Machine sketch | optional | the user may name the states/phases; otherwise the planner derives them |
Ask once for anything missing. After init, never ask again — a question
mid-run is a table miss: ESCALATE it.
The Process
digraph workmachine {
rankdir=TB; node [shape=box];
init [label="init\nscaffold → author machine.md (subagent) → lint →\nstate = initial; execute(entry effects)" shape=doublecircle];
next [label="nextEvent()\nclock/user preempt → else pop queue →\nelse collapse tiny statuses via event rules" shape=diamond];
trans [label="transition\n(state, event) table lookup;\nbudget check; append events.md; write state.md"];
exec [label="execute(effects)\ndispatch by PATH in ONE parallel batch;\ncollect tiny statuses; git spot-check"];
done [label="return state.output\nfinal summary to user" shape=doublecircle];
init -> next;
next -> trans;
trans -> exec;
exec -> next [label="state is active"];
exec -> done [label="state is final"];
}
done is reachable only by entering a state marked final in
machine.md. There is no "feels complete" exit. A final state is entered only
via a table row like every other state.
Phase 0: init — Scaffold
bash <skill-dir>/scaffold/init.sh \
--slug <slug> --goal "<goal>" [--duration <e.g. 2h|90m>] [--force]
Creates ~/.harness/workmachine/<slug>/:
machine.md — THE MACHINE: states, alphabet, transition table, budgets,
final states, event rules. Seeded as a skeleton; a PLANNER
subagent fills it in (Phase 1). Ground truth for routing.
state.md — THE STATE: current state + visit count, the event queue,
the output slot. One screen. Single source of volatile values.
events.md — append-only log: every event popped and every transition
taken (`<ts> <from> --<event>--> <to> effects: …`). Audit trail.
progress.md — the digest: per-unit status lines appended by subagents.
Orchestrator reads only its tail.
cheatsheet.md — self-populating environment knowledge (read first, append back).
run-card.md — static orientation; NO volatile values.
prompts/ — initialiser.md, planner.md, builder.md, sub-subagent.md,
measurement.md — rendered ONCE; dispatched BY PATH.
units/<id>.md — one file per unit; parallel subagents never share a write target.
Fill the placeholder the scaffold left in state.md: the concurrency cap.
Ensure the target repo is under git. Record date +%s + deadline epoch in
state.md if a duration was given.
Phase 1: init — Author the Machine (planner subagent)
Dispatch ONE planner subagent by path: prompts/planner.md + the goal + any
user-supplied machine sketch. It writes machine.md. You do not write it.
machine.md format
# Machine: <goal>
## Alphabet
built | pass | fail | blocked | deadline | user_stop | <custom tokens…>
## Event rules <!-- how tiny statuses collapse into ONE token -->
- all dispatched units returned `done` with commits in git log → built
- any unit returned `blocked` → blocked
- measurement returned PASS for every unit under test → pass
- any measurement returned FAIL → fail
- `date +%s` ≥ deadline → deadline (preempts everything)
- user says stop → user_stop (preempts everything)
## States
### PLAN [initial]
- Entry effects: dispatch builder over units: <unit list or "derive from goal">
- Notes: <what "done" means for this state's units>
### BUILD
- Entry effects: dispatch builder(s) in parallel over: <units>
### MEASURE
- Entry effects: dispatch measurement over: <units>; bar: <numeric|textual bar>
### FIX
- Entry effects: dispatch builder over: units whose measurement FAILed
- Notes: builder reads the prior Measurement section first and writes a
root-cause hypothesis into the unit file before editing; a repeat of the
previous attempt returns `blocked`
### SHIP [final]
- Output: <what to record — commit hash, path, verdict>
### ESCALATE [final]
- Output: the (state, event) that missed / the cycle that exhausted its budget
## Transitions <!-- (state, event) → next [budget N → on_budget] -->
| State | Event | Next | Budget | On budget |
|---|---|---|---|---|
| PLAN | built | BUILD | — | — |
| BUILD | built | MEASURE | — | — |
| BUILD | blocked | ESCALATE | — | — |
| MEASURE | pass | SHIP | — | — |
| MEASURE | fail | FIX | 3 | ESCALATE |
| FIX | built | MEASURE | — | — |
| * | deadline | SHIP-PARTIAL or ESCALATE | — | — |
| * | user_stop | ESCALATE | — | — |
Rules the planner must obey (and you lint for):
- Exactly one
[initial]state; at least one[final]state;ESCALATEalways exists and is final. - Every non-final state has a row for every event its effects can produce, plus
deadlineanduser_stop(a*wildcard row is fine). - Every backward transition (one that can revisit a state) has a Budget and
an On budget target. The budget counts how many times that table row
fires (
MEASURE/fail: 2/3), not how many times the state is entered — a state reachable by two rows has two independent counters. - Every entry effect is a dispatch, a write, or a check — never "decide".
- Every state that produces artifacts is followed by a measurement state
whose effects dispatch
prompts/measurement.md, never the producer. - A
pass/failevent rule must reference the recorded success bar (numeric OR textual). If the goal has no statable bar, the planner writesbar: noneand MEASURE's rule collapses on ordinary scoped verification.
Lint (you do this — it is a check, not authorship)
Read machine.md ONCE and verify the rules above mechanically. Any failure →
re-dispatch the planner with the lint line as its delta. Do not patch the
machine yourself. Then write state = <initial> and visits = 0 into
state.md, append init → <initial> to events.md, and execute the initial
state's entry effects.
Phase 2: The Loop
nextEvent()
In this order — the first that yields a token wins:
- Preempts.
date +%s≥ deadline →deadline. User said stop →user_stop. Queue these at the head. - Queue. If
state.md's event queue is non-empty, pop the head. - Collapse. Otherwise, take the tiny statuses from the last
executeand apply the machine's event rules top to bottom; the first matching rule yields the token. Push it, then pop it (so it is logged as queued).
You never produce a token the alphabet does not contain. If no rule matches,
the token is blocked — and if blocked has no row either, the lookup misses
and the machine goes to ESCALATE. That is the system working, not failing.
Subagents are never given the deadline or any time awareness. Time is yours.
transition(machine, state, event)
- Find the row
(state, event); if none, try(*, event); if none →ESCALATEwith output = the missed pair. - If the row has a budget, increment its visit count in
state.md; if it now exceeds the budget → next = the row's On budget target instead. - Append to
events.md:<ts> <state> --<event>--> <next> visits=<n>. - Write the new state into
state.md. - The new state's entry effects are the
effectsto execute.
No step reads an artifact. No step calls for judgment. If a step feels like it needs judgment, it is a lint failure that slipped through: ESCALATE, fix the table once via the planner, and resume.
execute(effects)
Write each unit's scope into units/<unit-id>.md (a subagent does this if
non-trivial — you may write the one-line scope header). Then dispatch all
independent effects in ONE message (multiple subagent calls), each a SHORT
message:
- the role-prompt PATH —
prompts/builder.mdorprompts/measurement.md(which tells the subagent to readprompts/initialiser.mdfirst); - the unit id + unit-file path + one line naming the gap/target.
Never paste a template, the digest, the cheatsheet, or machine.md. Respect
the concurrency cap: 20 units with a cap of 6 → three sequential sub-batches
inside the same state; the state does not transition until all have returned.
Size a model per unit — small for mechanical, mid for standard, your own tier for hard — never above your own tier.
When statuses return: git log spot-check each claimed commit. A claimed commit
not in git is treated as blocked for that unit, not done. Same for
measurement: a PASS whose unit file holds no runnable evidence (the command
run + its output, or the rubric applied line by line) is blocked, not pass.
Subagents lie; a report is a claim, not evidence.
Unit file before dispatch. The one-line gap in the dispatch is not the
context — the unit file is. Before dispatching, units/<id>.md must hold the
scope (files/topics), the constraints (user's technology choices, frozen paths,
things already rejected), and, for a FIX visit, a pointer to the prior
Measurement section. A builder dispatched against a bare unit file rediscovers
all of that and drifts.
Final state → return state.output
Enter a [final] state, record output in state.md per the state's Output
line (commit hash / path / verdict / for ESCALATE the missed pair or exhausted
cycle), and send the user ONE final summary: the goal, the path through the
machine (from events.md, compressed), the output pointer, and — if ESCALATE —
exactly what the table needs so the run can be resumed after one edit.
Escalation Protocol
ESCALATE is the only place judgment re-enters, and it re-enters as a table
edit, not as a runtime choice:
- The run stops in ESCALATE with the missed
(state, event)or the exhausted cycle instate.output. - Dispatch the planner with that delta. It adds/changes the minimal rows and nothing else. Lint again.
- Resume: set
stateto the state that was active when the miss occurred (recorded inevents.md), re-queue the event, continue the loop.
Never hand-edit machine.md to "just add the row". The planner does it; you
lint it. The audit trail in events.md must show the escalation and the
resume.
Anti-Drift
| Thought you're having | What you must do instead |
|---|---|
| "The obvious next step is X, I'll just go there" | Look it up. If the table has no row, ESCALATE. Judgment lives in machine.md. (Law 1) |
"This status is basically pass" |
Apply the written event rules. If none matches, it is blocked. (Law 2) |
"I'll add a retry row real quick" |
Dispatch the planner with the delta; lint; resume. You author nothing. (Law 5) |
| "This fix→measure loop can run until it's green" | Every cycle has a budget and an on-budget target, or it fails lint. (Law 4) |
| "Measurement said PASS, move on" | Only if its unit file shows the command + output. A bare PASS is blocked. |
| "FIX again, maybe it works this time" | A FIX builder must state a root-cause hypothesis from the prior Measurement before editing. Same attempt twice = blocked. |
| "I'll dispatch these builders one by one" | Independent entry effects fire in ONE parallel batch. (Law 6) |
| "I'll paste machine.md / the digest into the dispatch" | Pass PATHS. The initialiser makes the subagent read shared state. |
| "I'll read the unit's artifact to see if it passed" | The MEASURE state dispatches measurement.md; you read PASS|FAIL — one line. |
| "The builder said it passed, skip MEASURE" | Producer self-report never yields pass. Only the measurement subagent does. |
| "Deadline is close, one more BUILD" | deadline preempts; its row decides (SHIP-PARTIAL or ESCALATE). |
| "I'll sleep until the deadline event" | Never. Idle sleeping is a firing offense. |
| "Context is tight, I'll do this unit inline" | Especially then — dispatch. |
| "I'll grab a stronger model for FIX" | Never above your own tier. |
Red Flags — STOP and Reread This Skill
- You moved to a state without a matching row in
machine.md. - You queued a token that is not in the alphabet.
- A transition is missing from
events.md(every move is logged, no exceptions). - A backward row has no budget, or you exceeded it without going to
on_budget. - You edited
machine.mdyourself. - You dispatched independent effects serially, or over the concurrency cap.
- A dispatch carried a template, the digest, the cheatsheet, or
machine.mdinline. - You read a full artifact / transcript / large return into your context.
- A unit is marked
doneon the producer's claim, with no measurement dispatch. - A
passwas queued from a measurement whose unit file has no runnable evidence. - A builder was dispatched against a unit file with no scope/constraints in it.
- A claimed commit is not in
git logand you did not treat it asblocked. - You haven't run
date +%ssince the lastexecutereturned. - You wrote the user a mid-run status message.
events.md+state.mdare the status page. - You are composing the machine "in your head" instead of having the planner write it.
Resumption
If ~/.harness/workmachine/<slug>/ exists, trust disk over memory:
- Read
state.md(current state, queue, visits) and the tail ofevents.md. - Clock. If the deadline has passed, the resume request is the new mandate: take a fresh duration if given, else ask once.
- Reconcile git.
workmachine(<state>/<unit>): …commits not recorded inprogress.mdmean the run died between commit and digest write — record them. - Reconcile units. A unit marked
doingwith a matching commit isdone; without one it is unclaimed — it will be re-dispatched by the state's effects. - Reconcile
prompts/. All five role files present, else re-run the scaffold with--force. - If the current state's effects were mid-flight, re-
executethem (unit files show what returned). Then continue atnextEvent().
Quick Reference
| Item | Value |
|---|---|
| Harness dir | ~/.harness/workmachine/<slug>/ |
| Scaffold | scaffold/init.sh --slug --goal [--duration] [--force] — one command; prints every path |
| Machine | machine.md — states / alphabet / event rules / transition table / budgets / finals; authored by the planner, linted by you, edited only via ESCALATE |
| State | state.md — current state + visits + event queue + output; single source of volatile values |
| Events | events.md — append-only; every pop and every transition |
| Digest | progress.md — subagents append one line per unit; you read the tail |
| Prompts | prompts/ — initialiser + planner + builder + sub-subagent + measurement; written ONCE, dispatched BY PATH |
nextEvent |
preempts (deadline, user_stop) → queue head → collapse statuses via event rules; alphabet-only |
transition |
pure (state, event) lookup; * wildcard; miss → ESCALATE; budget exceeded → on_budget |
execute |
independent effects in ONE parallel batch ≤ cap; role-prompt PATH + unit delta; tiny returns; git log spot-check |
| Return | only from a [final] state; state.output is the summary |
| Commits | workmachine(<state>/<unit-id>): <summary> |
| Never | route by feel, invent a token, unbudgeted cycle, edit the machine yourself, inline authorship, ingest artifacts, over-cap batch, up-tier model, idle sleep, mid-run user updates |
이 스킬의 파일
13workmachine skill 질문
Claude Code에 workmachine skill을 어떻게 설치하나요?
npx degit av/skills/workmachine ~/.claude/skills/workmachine를 실행합니다. Claude Code는 ~/.claude/skills(모든 프로젝트) 또는 .claude/skills(한 프로젝트)에서 skills를 로드하며, 설명이 작업과 일치할 때 skill을 사용합니다.
workmachine skill을 사용할 수 있는 클라이언트는 무엇인가요?
Agent Skills 형식을 읽는 모든 클라이언트(Claude Code, Codex, Gemini CLI, claude.ai(ZIP으로 업로드))가 사용할 수 있습니다. skill 내 스크립트는 클라이언트가 코드 실행을 허용하는 경우에만 실행됩니다.
오케스트레이터가 실행 중에 코드를 수정하거나 의사결정을 내릴 수 있나요?
아니요, 오케스트레이터는 작성이나 분석 작업이 엄격히 금지되어 있습니다. 모든 생산적인 작업은 반드시 서브에이전트가 수행해야 합니다.