@dna113p/machines 0.3.3 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -209,14 +209,27 @@ directory is created by the first write, as `0700`, and each file is `0600`:
209
209
  `status` is `running`, `waiting`, `completed`, or `failed`. `state` is the current
210
210
  state as a string (JSON for a nested state). `agent` is present while an Agent
211
211
  state has reported its identity, and `error` after a failure. `owner` is
212
- `MACHINES_RUN_OWNER` when set, otherwise `CLAUDE_CODE_SESSION_ID`, otherwise
213
- absent. `stateSince` is when the current state was entered. `label` is the first
212
+ `MACHINES_RUN_OWNER` when set, otherwise the session id the coding agent gives
213
+ the commands it starts (`CLAUDE_CODE_SESSION_ID`, then `CODEX_THREAD_ID`, then
214
+ `PI_SESSION_ID`), otherwise absent; see
215
+ [how a host names its session](#drawing-your-own-widgets). `stateSince` is when
216
+ the current state was entered. `label` is the first
214
217
  line of `MACHINES_RUN_LABEL` (at most 160 characters) when a launcher sets it to
215
218
  say what the run is for, such as a ticket id and title; it is the launcher's
216
219
  choice to publish that text. `machine run` exports its run id as
217
220
  `MACHINES_RUN_PARENT`, so a Machine started by one of its Operations or Agents
218
221
  records that id as `parent`; the status line shows only top-level runs.
219
222
 
223
+ `graph` is the Machine's states and transitions, published once when the run
224
+ starts: `initial`, `states` (each with an `id`, a `kind` of `agent`, `human`,
225
+ `operation`, `final`, `group`, or `state`, an Agent's `role`, and the `parent`
226
+ group of a nested state), and `edges` (`from`, `to`, and the `event` taken). Ids
227
+ are state paths joined by dots, so a nested state is `review.round`. A Machine
228
+ with more than 300 states or 1000 transitions publishes no graph. `trail` is the
229
+ states the run entered, oldest first, each with the time it was entered, as the
230
+ same dotted paths; it keeps the latest 200. Both are additions to version 2:
231
+ a reader that does not know them ignores them.
232
+
220
233
  A run hosted through `startMachineHost` from `@dna113p/machines/host` publishes
221
234
  the same record when its host asks with `publishRunStatus: true` and
222
235
  publication is on in the hosting process, as a daemon that runs tickets does. A
@@ -279,8 +292,17 @@ taken a response from its inbox, its record lists that delivery's id in
279
292
  id stays listed for at least a minute, however many responses the run takes
280
293
  after it, and never contains the response.
281
294
 
282
- Machine input, Machine output, and Human responses are never recorded. A Human
283
- prompt and its choices are written to the `0600` file while the request is
295
+ When a run finishes, `machine run`, the MCP server, and a host that asks for
296
+ publication all three publish the optional `note` field if the final output
297
+ is an object with a non-empty string `summary`. It is trimmed and limited to
298
+ 2000 characters, including a trailing `…` when shortened
299
+ at a word boundary (a single word longer than the limit is cut to fit).
300
+ Empty or whitespace-only summaries leave the field absent. The whole note,
301
+ including its line breaks, is available in JSON. This optional field keeps
302
+ `schemaVersion` at 2; readers should accept records with or without it.
303
+
304
+ Machine input, output fields other than that summary, and Human responses are
305
+ never recorded. A Human prompt and its choices are written to the `0600` file while the request is
284
306
  waiting. The standard directory is private to you; one you name instead should
285
307
  be a directory only you can read and write.
286
308
 
@@ -326,10 +348,13 @@ waiting run its prompt, request id, and choices (tab-separated when its
326
348
  output is not a terminal). A run's label follows as a fifth column when it has
327
349
  one, and a sixth says `watched` for a run this session
328
350
  [watches](#watching-other-runs). It lists the running and waiting runs of every
329
- session and project. `--all`, with or without `--json`, lists every published
351
+ session and project; `--for` narrows it to
352
+ [what one viewer shows](#drawing-your-own-widgets). `--all`, with or without `--json`, lists every published
330
353
  run: also the ones whose records remain after they finished, as `completed` or
331
354
  `failed`, or after their process disappeared, as `lost` (in JSON such a record
332
- keeps the status it last published). A run answered through its host shows
355
+ keeps the status it last published). For a completed or failed run, the first
356
+ non-empty line of its note is printed beneath the row; `--json` includes the
357
+ whole note. A run answered through its host shows
333
358
  `answered through its host` in place of a request id:
334
359
 
335
360
  ```text
@@ -440,13 +465,43 @@ for the same time, so the status line must run where it can see that process.
440
465
  Set `NO_COLOR` (to any value, even an empty one) for plain text and `COLUMNS`
441
466
  to change the 120-column truncation.
442
467
 
468
+ ### Watching runs in a browser
469
+
470
+ ```bash
471
+ machine ui [--port <port>] [--project <dir>]
472
+ ```
473
+
474
+ serves a live page on `127.0.0.1` (port 4777, or the next free one) of every
475
+ published run: what is waiting on you, what is running, and what finished, with
476
+ each run's Machine drawn as a graph, its current state marked, the states and
477
+ transitions it has taken counted, and the path it took so far. A waiting run
478
+ that takes outside responses is answered from the page, as `machine respond`
479
+ answers it; a run answered through its host, such as a daemon's, names where to
480
+ answer it instead. The page loads nothing from outside, accepts requests only
481
+ under its own host name, and answers a run only with a token it was served.
482
+
483
+ A project adds its own panels beside the runs, such as its tickets or pull
484
+ requests, in `.machines/ui.json` in the project (the nearest directory above the
485
+ current one that has a `.machines` directory, or `--project`):
486
+
487
+ ```json
488
+ { "panels": [{ "title": "Tickets", "command": ["node", "scripts/ui-panels.mjs", "tickets"], "refreshSeconds": 10 }] }
489
+ ```
490
+
491
+ Each command runs in the project directory every `refreshSeconds` (10 by
492
+ default) and prints one JSON object: `{ "summary"?, "items": [{ "text",
493
+ "detail"?, "status"?, "href"?, "at"? }] }`, where `status` is `ok`, `warn`,
494
+ `bad`, `active`, or `idle`, `href` is a web link, and `at` a time. Commands are
495
+ project code, trusted as `.machines/` Machines are; a command that fails or
496
+ prints something else shows its error in its panel.
497
+
443
498
  ### Watching other runs
444
499
 
445
500
  A session can pull in runs it did not start: a ticket that a daemon runs, or a
446
501
  run started by another session or under another project. Nothing is configured.
447
502
  The session is identified as a run's `owner` is, by `MACHINES_RUN_OWNER` and
448
- otherwise `CLAUDE_CODE_SESSION_ID`, so an agent inside Claude Code only runs the
449
- command.
503
+ otherwise by the session id its coding agent gives it, so an agent inside Claude
504
+ Code, Codex, or Pi only runs the command.
450
505
 
451
506
  ```bash
452
507
  machine runs # running and waiting runs, with their labels
@@ -455,6 +510,7 @@ machine watch org-9 # runs whose id or label starts with org-9
455
510
  machine watch --dir ../other # runs working in that directory or below it
456
511
  machine watch # what this session watches, and the published runs each entry matches now
457
512
  machine unwatch org-9 # also: unwatch --dir <path>, unwatch --all
513
+ machine watch --for <session> org-9 # the same commands for another session's list
458
514
  ```
459
515
 
460
516
  - `machine watch <text>` names every top-level run whose id starts with the
@@ -467,7 +523,12 @@ machine unwatch org-9 # also: unwatch --dir <path>, unwatch --all
467
523
  - An entry keeps matching runs that start later, so a ticket can be watched
468
524
  before its run exists. `machine watch` lists a matched run for as long as its
469
525
  record remains, with the status `machine runs --all` gives it, so it also
470
- says how a watched run ended.
526
+ says how a watched run ended, with the first non-empty line of its note
527
+ beneath the row when present.
528
+ - `--for <session>`, first, makes `machine watch` and `machine unwatch` read
529
+ and change the list of the session it names instead of this process's own.
530
+ A program that draws [its own widgets](#drawing-your-own-widgets) for a
531
+ viewer manages that viewer's list this way, without setting a variable.
471
532
 
472
533
  A watched run is shown even when another session owns it or it works outside
473
534
  the project. Everything else is as for any run: sub-runs stay hidden, a run is
@@ -486,7 +547,122 @@ session id that is not safe as a file name is refused. Removing the last entry
486
547
  removes the file, and writing a list or starting a run removes other sessions'
487
548
  lists that have not changed for 30 days. A missing, unreadable, or malformed list watches
488
549
  nothing; it is never an error on the status line. The commands fail with a
489
- message when publication is off or when no variable names the session.
550
+ message when publication is off or when nothing names the session.
551
+
552
+ ### Drawing your own widgets
553
+
554
+ Any application can show Machine runs in its own way from what is already
555
+ published: an extension for Codex or Pi, a web app, an editor panel. The Claude
556
+ Code status line above is one such consumer. Nothing is configured and nothing
557
+ new runs; there are three ways to read the same directory:
558
+
559
+ | Read with | You get |
560
+ | --- | --- |
561
+ | `machine runs --for <session> --json` | The runs one viewer shows, each ready to draw |
562
+ | `@dna113p/machines/run-status` | The same rule and the readers, in your own process |
563
+ | The files | The [records](#run-status-for-host-status-lines) and [watch lists](#watching-other-runs) themselves; the rule is then yours to apply |
564
+
565
+ A viewer is one surface that shows runs: a session of a coding agent, a browser
566
+ tab, a status line. It is named by a session id and may have a project
567
+ directory. The rule that decides what it shows is the status line's:
568
+
569
+ - only top-level runs: a run started from inside another Machine is never shown;
570
+ - the runs its session owns, unowned runs working in its project directory or
571
+ below it, and the runs its session [watches](#watching-other-runs), whoever
572
+ owns them;
573
+ - a run recorded as running or waiting whose process is gone is `lost`;
574
+ - a finished or lost run stays for 30 seconds and then drops out;
575
+ - each run once, the ones that can still change first, then the most recently
576
+ updated. Runs updated in the same millisecond keep the order they were read
577
+ in from the directory, for the command, the library, and the status line
578
+ alike.
579
+
580
+ Both ways below take a snapshot. Read again to refresh, as the status line does
581
+ every few seconds, and read where the runs' processes can be seen, or every run
582
+ looks `lost`.
583
+
584
+ ```bash
585
+ machine runs --for "$SESSION" --project "$PWD" --json # what that viewer shows
586
+ machine runs --for --json # for this process's own session
587
+ machine runs --for "$SESSION" --all # as text, and with older finished and lost runs
588
+ machine watch --for "$SESSION" org-9 # change what that viewer watches
589
+ ```
590
+
591
+ `--for` takes the viewer's session id; with no id after it, the viewer is this
592
+ process's own session, and the command fails when nothing names one.
593
+ `--project` is the viewer's project directory, compared as an absolute path;
594
+ without it only owned and watched runs are selected. `--all` also keeps the
595
+ finished and lost runs that the 30 seconds would have dropped, for as long as
596
+ their records remain. Without `--json` the runs are printed in the columns of
597
+ `machine runs`. Without `--for`, `machine runs` is unchanged: every session's
598
+ running and waiting runs, as plain records.
599
+
600
+ Each item is the published record with three additions:
601
+
602
+ ```json
603
+ {
604
+ "schemaVersion": 2, "id": "1a2b3c4d-…", "pid": 4242, "owner": "<session id>",
605
+ "machine": "release", "path": "/work/.machines/release.ts", "cwd": "/work",
606
+ "status": "waiting", "state": "approve",
607
+ "human": { "prompt": "Publish version 0.4.0?", "requestId": "9f8e7d6c-…", "choices": ["publish", "hold"] },
608
+ "startedAt": "2026-03-04T05:06:07.000Z", "updatedAt": "2026-03-04T05:08:12.000Z",
609
+ "shown": "owned",
610
+ "answer": { "via": "respond", "runId": "1a2b3c4d-…", "requestId": "9f8e7d6c-…" }
611
+ }
612
+ ```
613
+
614
+ - `status` is the effective one: what the record says, or `lost`.
615
+ - `shown` says why the viewer shows the run: `owned`, `project`, or `watched`,
616
+ the first of them that holds.
617
+ - `answer` is present while the run waits on a Human request that something
618
+ can answer. `"via": "respond"` carries the two ids for
619
+ `machine respond <runId> <requestId> <response>`; `"via": "host"` means only
620
+ the program hosting the run can answer it, so a widget shows the prompt and
621
+ offers no input. `human` still has the prompt, choices, and suggestions.
622
+
623
+ Nothing is added for presentation: no colour, layout, or shortening, and no
624
+ limit on how many runs are returned. Recorded text is the run's own, so escape
625
+ it for your surface as the status line strips control characters.
626
+
627
+ The library gives the same answer without starting a process:
628
+
629
+ ```ts
630
+ import {
631
+ readRunStatuses, readRunWatches, runStatusDirectory, runsForViewer,
632
+ } from "@dna113p/machines/run-status";
633
+
634
+ const directory = runStatusDirectory(); // undefined while publication is off
635
+ const runs = directory === undefined ? [] : runsForViewer(readRunStatuses(directory), {
636
+ sessionId,
637
+ projectDir: "/work/project",
638
+ watches: readRunWatches(directory, sessionId),
639
+ });
640
+ for (const run of runs) draw(run.label ?? run.id, run.status, run.shown, run.answer);
641
+ ```
642
+
643
+ `runsForViewer(records, viewer, options)` is pure apart from probing whether
644
+ each process is alive; `options` takes `now`, `all`, and an `isAlive` of your
645
+ own. The module also exports `runSessionId`, the types of a record, a watch, a
646
+ viewer, and an item, and `settledRunVisibleMilliseconds`. It only reads:
647
+ publishing stays with the runtime, and a viewer's watch list is changed with
648
+ `machine watch --for`.
649
+
650
+ **Naming the session.** A viewer shows the runs whose `owner` is its session
651
+ id, so the host and the runs it starts must agree on that id. Any host can set
652
+ `MACHINES_RUN_OWNER` in the environment of the commands it starts and pass the
653
+ same value to `--for`; the id may use letters, digits, `.`, `_`, and `-`. Where
654
+ it is not set, the id is the one the coding agent already gives its commands:
655
+
656
+ | Host | Variable | Given to |
657
+ | --- | --- | --- |
658
+ | Any | `MACHINES_RUN_OWNER` | Whatever the host starts with it set; takes precedence |
659
+ | Claude Code | `CLAUDE_CODE_SESSION_ID` | The commands a session runs; also the `session_id` its status line receives |
660
+ | Codex | `CODEX_THREAD_ID` | The commands a thread runs |
661
+ | Pi | `PI_SESSION_ID` | The commands its `bash` and `powershell` tools run, not `!` commands typed by the user |
662
+
663
+ The first one that is set names the session, in that order. A run started from
664
+ inside one of these agents is therefore owned by that session: it is shown
665
+ there, and in another session only when that session watches it.
490
666
 
491
667
  ## Troubleshooting
492
668
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dna113p/machines",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Reusable workflows coordinating agents, local operations, and human decisions.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -20,29 +20,96 @@
20
20
  },
21
21
  "types": "./dist/src/index.d.ts",
22
22
  "exports": {
23
- ".": { "types": "./dist/src/index.d.ts", "import": "./dist/src/index.js" },
24
- "./acp": { "types": "./dist/src/acp.d.ts", "import": "./dist/src/acp.js" },
25
- "./agy": { "types": "./dist/src/agy.d.ts", "import": "./dist/src/agy.js" },
26
- "./chatgpt-web": { "types": "./dist/src/chatgpt-web.d.ts", "import": "./dist/src/chatgpt-web.js" },
27
- "./claude": { "types": "./dist/src/claude.d.ts", "import": "./dist/src/claude.js" },
28
- "./conversation": { "types": "./dist/src/conversation.d.ts", "import": "./dist/src/conversation.js" },
29
- "./codex": { "types": "./dist/src/codex.d.ts", "import": "./dist/src/codex.js" },
30
- "./deepseek": { "types": "./dist/src/deepseek.d.ts", "import": "./dist/src/deepseek.js" },
31
- "./decision": { "types": "./dist/src/decision.d.ts", "import": "./dist/src/decision.js" },
32
- "./jev": { "types": "./dist/src/jev.d.ts", "import": "./dist/src/jev.js" },
33
- "./openrouter": { "types": "./dist/src/openrouter.d.ts", "import": "./dist/src/openrouter.js" },
34
- "./laya": { "types": "./dist/src/laya.d.ts", "import": "./dist/src/laya.js" },
35
- "./von": { "types": "./dist/src/von.d.ts", "import": "./dist/src/von.js" },
36
- "./host": { "types": "./dist/src/host.d.ts", "import": "./dist/src/host.js" },
37
- "./launcher": { "types": "./dist/src/launcher.d.ts", "import": "./dist/src/launcher.js" },
38
- "./mcp": { "types": "./dist/mcp/server.d.ts", "import": "./dist/mcp/server.js" },
39
- "./pi-extension": { "types": "./dist/pi-extension/index.d.ts", "import": "./dist/pi-extension/index.js" }
23
+ ".": {
24
+ "types": "./dist/src/index.d.ts",
25
+ "import": "./dist/src/index.js"
26
+ },
27
+ "./acp": {
28
+ "types": "./dist/src/acp.d.ts",
29
+ "import": "./dist/src/acp.js"
30
+ },
31
+ "./agy": {
32
+ "types": "./dist/src/agy.d.ts",
33
+ "import": "./dist/src/agy.js"
34
+ },
35
+ "./chatgpt-web": {
36
+ "types": "./dist/src/chatgpt-web.d.ts",
37
+ "import": "./dist/src/chatgpt-web.js"
38
+ },
39
+ "./claude": {
40
+ "types": "./dist/src/claude.d.ts",
41
+ "import": "./dist/src/claude.js"
42
+ },
43
+ "./conversation": {
44
+ "types": "./dist/src/conversation.d.ts",
45
+ "import": "./dist/src/conversation.js"
46
+ },
47
+ "./codex": {
48
+ "types": "./dist/src/codex.d.ts",
49
+ "import": "./dist/src/codex.js"
50
+ },
51
+ "./deepseek": {
52
+ "types": "./dist/src/deepseek.d.ts",
53
+ "import": "./dist/src/deepseek.js"
54
+ },
55
+ "./decision": {
56
+ "types": "./dist/src/decision.d.ts",
57
+ "import": "./dist/src/decision.js"
58
+ },
59
+ "./jev": {
60
+ "types": "./dist/src/jev.d.ts",
61
+ "import": "./dist/src/jev.js"
62
+ },
63
+ "./openrouter": {
64
+ "types": "./dist/src/openrouter.d.ts",
65
+ "import": "./dist/src/openrouter.js"
66
+ },
67
+ "./laya": {
68
+ "types": "./dist/src/laya.d.ts",
69
+ "import": "./dist/src/laya.js"
70
+ },
71
+ "./von": {
72
+ "types": "./dist/src/von.d.ts",
73
+ "import": "./dist/src/von.js"
74
+ },
75
+ "./host": {
76
+ "types": "./dist/src/host.d.ts",
77
+ "import": "./dist/src/host.js"
78
+ },
79
+ "./launcher": {
80
+ "types": "./dist/src/launcher.d.ts",
81
+ "import": "./dist/src/launcher.js"
82
+ },
83
+ "./mcp": {
84
+ "types": "./dist/mcp/server.d.ts",
85
+ "import": "./dist/mcp/server.js"
86
+ },
87
+ "./pi-extension": {
88
+ "types": "./dist/pi-extension/index.d.ts",
89
+ "import": "./dist/pi-extension/index.js"
90
+ },
91
+ "./run-status": {
92
+ "types": "./dist/src/run-viewer.d.ts",
93
+ "import": "./dist/src/run-viewer.js"
94
+ }
40
95
  },
41
- "files": ["dist", "claude/statusline.mjs", "docs/architecture.md", "docs/authoring.md", "docs/integrations.md", "docs/local-decisions.md", "services/laya/server.py", "skills", "LICENSE", "README.md"],
96
+ "files": [
97
+ "dist",
98
+ "claude/statusline.mjs",
99
+ "docs/architecture.md",
100
+ "docs/authoring.md",
101
+ "docs/integrations.md",
102
+ "docs/local-decisions.md",
103
+ "services/laya/server.py",
104
+ "skills",
105
+ "LICENSE",
106
+ "README.md"
107
+ ],
42
108
  "scripts": {
43
109
  "build": "node scripts/build.mjs",
44
110
  "build:plugin": "npm run build && node scripts/build-plugin.mjs",
45
- "check": "npm run typecheck && npm test",
111
+ "check": "npm run lint && npm run typecheck && npm test",
112
+ "lint": "oxlint --deny-warnings",
46
113
  "example": "node examples/operation.ts",
47
114
  "example:agents": "node examples/named-agents.ts",
48
115
  "example:agy": "node examples/agy.ts",
@@ -72,6 +139,7 @@
72
139
  },
73
140
  "devDependencies": {
74
141
  "@types/node": "^24.0.0",
142
+ "oxlint": "^1.87.0",
75
143
  "typescript": "^7.0.2"
76
144
  }
77
145
  }
@@ -31,6 +31,11 @@ needs. Existing Machines and examples are starting points, not a fixed menu.
31
31
  Make consequential checks and decisions explicit in states when they need to
32
32
  control progress. Choose the complexity needed to complete the task reliably.
33
33
 
34
+ Machine Shop is an optional starter library of common software-task Machines,
35
+ kept in its own repository at `https://github.com/dna113p/machine-shop`. Nothing
36
+ here requires it; clone it to try a default or adapt one, as
37
+ [machine-project-setup](../machine-project-setup/SKILL.md) describes.
38
+
34
39
  Look for common task sequences and recurring manual workflows while doing the
35
40
  work. Convert them into Machines when explicit steps would improve repeatability
36
41
  or code quality. Parameterize assignment details so the workflow can serve the
@@ -82,3 +87,29 @@ Agent runners or injected Human input where possible. A successful run reaches
82
87
  the intended final state and produces the requested artifact or effect. Run real
83
88
  external work only within the user's authorized scope. Report what was verified
84
89
  and any branch that still requires a configured harness or human interaction.
90
+
91
+ ## Checks the Machine owns
92
+
93
+ Design the workflow so these checks control progress and leave evidence for the
94
+ next repair round or person taking over:
95
+
96
+ - Run every gate, not only up to the first failure, so one repair round sees
97
+ all failures.
98
+ - Keep the project's gate list in one place that every route reads.
99
+ - For new behaviour, write tests first; have an Operation see them fail before
100
+ implementation, then pass after implementation.
101
+ - After each editing Agent, run an Operation that compares the changed paths
102
+ against the paths the assignment allows.
103
+ - Ask for a structured review: each acceptance criterion met or not, findings
104
+ tagged by severity. Let an Operation, not the reviewer's verdict word, decide
105
+ what happens next.
106
+ - Match a reviewer's findings across rounds by their place in the conversation,
107
+ never by their wording.
108
+ - End stuck unattended runs with work kept and a written note of what a person
109
+ must decide, instead of waiting in a Human state. Every stop keeps both so a later
110
+ run or a person can continue from them.
111
+ - Before implementation, have a read-only Agent try to show the assignment is
112
+ wrong, already done, or underspecified; require options and a recommended answer
113
+ for each question it raises.
114
+ - Tell editing Agents that checks they cannot run in their environment are the
115
+ workflow's to run, so a sandboxed Agent returns its work instead of `blocked`.
@@ -62,6 +62,16 @@ Workers do not inherit this conversation. Supply the context needed to act:
62
62
  - Applicable constraints, permitted effects, and what should produce a blocked outcome.
63
63
  - A result location readable by the coordinator, and what evidence it must contain.
64
64
 
65
+ State these five fields for each assignment:
66
+
67
+ 1. Its goal.
68
+ 2. The paths it may change.
69
+ 3. Its acceptance criteria.
70
+ 4. How it will be verified.
71
+ 5. What is forbidden.
72
+
73
+ A field that cannot be filled means the unit is not scoped yet.
74
+
65
75
  Follow the selected Machine's input contract. MCP/Pi accept JSON-serializable
66
76
  input; CLI positional input is text, while `--input-file <file|->` accepts JSON.
67
77
  Keep task details in input rather than hardcoding each assignment into a new
@@ -103,6 +113,15 @@ polling continuously. Interpret the current snapshot:
103
113
  Preserve existing user authorization when answering Human requests, and surface a
104
114
  new decision when the request requires one. A desire to finish is not an answer.
105
115
 
116
+ A `blocked` or parked run is a completed run to read, not a failure to retry blindly.
117
+ Design stuck unattended runs to end with work kept and a written note of what a
118
+ person must decide, instead of waiting in a Human state; every stop keeps both.
119
+ Read the note, answer it or take it to the user, then start a fresh run from the
120
+ kept work and note when resolved, or let a person continue from them.
121
+
122
+ Tell an editing Agent that checks it cannot run in its environment are the
123
+ workflow's to run, so a sandboxed Agent returns its work instead of `blocked`.
124
+
106
125
  To follow a run this session did not start, such as a ticket a daemon runs, use
107
126
  `machine watch <ticket-or-run-id>` where run status is published (`machine runs`
108
127
  lists what can be watched; `--all` adds runs that already ended). Watching only
@@ -126,3 +145,7 @@ verified. Read the report and inspect relevant changes or checks, then integrate
126
145
  the result into the user's task. If evidence is missing, treat that as incomplete
127
146
  work even if the run says `completed`. Report the useful result, any blocker, and
128
147
  artifact paths; a run ID alone is not the delegated deliverable.
148
+
149
+ Read the reviewer's structured report: each acceptance criterion met or not,
150
+ findings tagged by severity. Let an Operation, not the reviewer's verdict word,
151
+ decide what happens next.
@@ -29,6 +29,19 @@ types, quality gates, recurring failure modes, and decisions needing a Human.
29
29
  Include domain-specific work, not just generic coding stages. Use only relevant,
30
30
  available project context; distinguish observed needs from speculative ones.
31
31
 
32
+ Inventory the project's deterministic checks before designing any workflow, by
33
+ class: lint, types, tests, build, and a live check that drives the real artifact
34
+ (a packaging smoke, an install test, a CLI or service run against a scratch
35
+ directory). Take them from the project's own scripts and CI, not from a fixed
36
+ list, and run each once to learn its duration and that it leaves the checkout
37
+ clean. Record the inventory in the map, naming the classes the project has no
38
+ command for; a missing class is a finding for the owner, not something to
39
+ invent. Put the resulting gate list in one project-owned place that every
40
+ route reads (a configuration file the launchers load, not a copy in each
41
+ launcher), so an Agent is held to the same gates a person runs. Where the
42
+ project's checks stop at the first failure, prefer running every gate and
43
+ reporting all failures at once: one repair round then sees them all.
44
+
32
45
  Inspect the existing orchestrator instructions and any `.machines/README.md`.
33
46
  Run `machine list` and `machine agents` in the actual work directories, or use
34
47
  `machine_list` / `machine_agents` with the correct workspace. Read candidate
@@ -95,11 +108,16 @@ instead of presenting same-model review as independent.
95
108
 
96
109
  ## Build the smallest useful set
97
110
 
98
- When the Machine Shop starter library is available, inspect its `docs/catalog.md`
99
- and candidate `defaults/` sources before rebuilding a common software process.
100
- Select or adapt relevant entries with the project-local installer; do not install
101
- the entire catalog by default. Its check commands, Agent bindings, and task inputs
102
- remain project-specific. Do not assume that library is globally installed.
111
+ Machine Shop is an optional starter library of common software-task Machines:
112
+ implementation, bug fixing, review, investigation, and model-free verification
113
+ among them. Nothing here requires it, but it is a good set of defaults to try
114
+ before rebuilding a common software process. It is a separate repository,
115
+ `https://github.com/dna113p/machine-shop`, and is not installed with the runtime;
116
+ when no checkout is present, tell the user about it or clone it to try it out.
117
+ Read its `docs/catalog.md` and the candidate `defaults/` sources, then run one
118
+ from source or copy selected entries with its project-local installer; do not
119
+ install the entire catalog by default. Its check commands, Agent bindings, and
120
+ task inputs remain project-specific.
103
121
 
104
122
  Map the major work to existing or missing workflows. Favor frequent delegation,
105
123
  important quality gates, and costly recurring mistakes. Reuse good definitions;
@@ -19,6 +19,10 @@ For each major task type, record:
19
19
  - Expected final state, checks, result retrieval, and verification command.
20
20
  - Allowed effects, Human decisions, retry limits, and untested prerequisites.
21
21
 
22
+ Record the gate inventory once, by class (lint, types, tests, build, live
23
+ check): the command for each, where the one list that every route reads lives,
24
+ and the classes the project has no command for.
25
+
22
26
  Mark deliberately direct, deferred, or blocked work with its reason. An existing
23
27
  setup may need no new Machine. Do not generate placeholders to fill categories.
24
28
  Include copyable invocations only for routes that exist. CLI positional input is