muse-crew 0.8.1 → 0.9.0
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.
- package/API.md +3 -0
- package/docs/artifact-ux.md +110 -0
- package/docs/guide.md +2 -2
- package/docs/ooda-report.md +21 -10
- package/docs/terminal-ux.md +110 -0
- package/docs/upgrade-paths.md +105 -0
- package/identities/hazel.md +20 -7
- package/lib/AGENTS.md +1 -0
- package/lib/append-ooda-step.js +25 -5
- package/lib/crew-api.js +37 -6
- package/lib/crew-release.sh +8 -2
- package/lib/read-ooda-verdict.js +60 -2
- package/lib/schema.sql +9 -0
- package/lib/update-watch.js +79 -1
- package/lib/ux-doctrine.js +63 -0
- package/package.json +1 -1
- package/workflows/bugfix.js +165 -38
- package/workflows/chore.js +43 -8
- package/workflows/crew-dispatch.js +5 -1
- package/workflows/crew-init.js +20 -3
- package/workflows/docs.js +18 -1
- package/workflows/standard.js +157 -37
package/API.md
CHANGED
|
@@ -391,6 +391,7 @@ Register a new project.
|
|
|
391
391
|
| `simultaneity` | integer (1–100) | no | Max concurrent tasks; defaults to 2 |
|
|
392
392
|
| `quiesced` | boolean | no | Start paused; defaults to false |
|
|
393
393
|
| `visual_protocol` | boolean or null | no | Tri-state: `null` = inherit crew default (off), `true` = enable for this project, `false` = explicit off. Defaults to null. |
|
|
394
|
+
| `environment_type` | `artifact` · `terminal` · null | no | User-facing surface for experiential QA routing: `artifact` = a rendered web UI (Hazel drives it with the see-act browser loop), `terminal` = a CLI (Hazel drives it herself, keeping transcripts). `null` = unclassified: no experiential QA. `deploy_type` names the deployment target, but `deploy_type: "artifact"` remains a legacy artifact-surface signal so pre-field projects keep today's experiential QA (the migration does not backfill the column); on conflicting config artifact wins. Defaults to null. |
|
|
394
395
|
|
|
395
396
|
### `updateproject`
|
|
396
397
|
|
|
@@ -400,6 +401,8 @@ Update project fields. Only `id` is required; all others are optional patch fiel
|
|
|
400
401
|
|
|
401
402
|
**`visual_protocol`:** accepted values are `true`, `false`, `null`; anything else is a validation error. `null` clears the override and returns the project to inherit. It is **not** covered by the context-change guard: the dispatcher reads the value once into launch args at dispatch time, so changing it mid-run only affects future launches. Effective value rule: `null` (inherit) and `false` (explicit off) both resolve to off — the visual protocol only runs when the project record carries `true`. The dispatcher passes the resolved boolean to launched workflows as the `visual_protocol` launch arg; workflows receive a plain boolean and never see the tri-state.
|
|
402
403
|
|
|
404
|
+
**`environment_type`:** accepted values are `artifact`, `terminal`, `null`; anything else is a validation error. Like `visual_protocol`, it is **not** covered by the context-change guard: project config is captured at dispatch time, so a change affects future launches. `null` means unclassified — experiential QA does not run (today's generic behavior). See `docs/terminal-ux.md` / `docs/artifact-ux.md` — one shared UX bar per surface, resolved from `environment_type` by `lib/ux-doctrine.js`; every phase prompt reads the resolved page, never a hardcoded filename. The dashboard registers itself with `environment_type: "artifact"` at crew-init.
|
|
405
|
+
|
|
403
406
|
### `deleteproject`
|
|
404
407
|
|
|
405
408
|
Remove a project registration.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Artifact UX — the shared bar for rendered projects
|
|
2
|
+
|
|
3
|
+
For projects whose user-facing surface is a rendered artifact
|
|
4
|
+
(`environment_type: "artifact"`), **the screen is the UI**. The user sees
|
|
5
|
+
pixels: pages, components, layout, copy, visual states. This page is the
|
|
6
|
+
crew-wide standard for that experience — the artifact sibling of
|
|
7
|
+
`docs/terminal-ux.md`, resolved from the same project config by
|
|
8
|
+
`lib/ux-doctrine.js`. Every phase reads the same page:
|
|
9
|
+
|
|
10
|
+
- **Sage** (Triage) uses it to decide whether a task is experiential —
|
|
11
|
+
anything this page covers is user-observable.
|
|
12
|
+
- **Mara** (Map) specs against it: exact screens, flows, and visual
|
|
13
|
+
states, plus the capture targets for the post-change visual capture.
|
|
14
|
+
- **Wren** (Build) builds to it: the rendered result is what ships, and
|
|
15
|
+
it ships in the commit.
|
|
16
|
+
- **Cass** (Review) reviews against it and rejects when the bar is not
|
|
17
|
+
met.
|
|
18
|
+
- **Hazel** (QA) drives the artifact herself and renders the verdict
|
|
19
|
+
against it, point by point.
|
|
20
|
+
- **Tate** (Docs) writes the public contract from it: the documented
|
|
21
|
+
behavior must match what the user sees.
|
|
22
|
+
|
|
23
|
+
The experiential nature doesn't change across surfaces. It's still codeless
|
|
24
|
+
UX judged with taste — a different instrument, the same bar.
|
|
25
|
+
|
|
26
|
+
## The bar
|
|
27
|
+
|
|
28
|
+
Judge every artifact change against these, out loud and point by point:
|
|
29
|
+
|
|
30
|
+
- **Alignment.** Does everything line up with what it should line up
|
|
31
|
+
with? Optical centering counts, not just the coordinates. An icon is
|
|
32
|
+
centered when it *looks* centered.
|
|
33
|
+
- **Spacing.** Is the rhythm consistent? Equal things get equal space;
|
|
34
|
+
different things get deliberate space. Padding that shrank by two
|
|
35
|
+
pixels is still a finding.
|
|
36
|
+
- **Hierarchy.** Does the eye go where the design means it to go? One
|
|
37
|
+
clear primary action, then the rest in their place. Nothing louder
|
|
38
|
+
than the thing that matters.
|
|
39
|
+
- **Composition.** Do the pieces sit together on purpose? Margins hold
|
|
40
|
+
their edges, columns don't wobble, nothing is floating without a
|
|
41
|
+
reason.
|
|
42
|
+
- **Balance.** Does the whole feel settled? A heavy element on one side
|
|
43
|
+
gets an anchor on the other. Asymmetry is fine when it's *chosen*.
|
|
44
|
+
- **Finish.** Are the edges clean? No orphaned shadows, no half-rendered
|
|
45
|
+
corners, no text clipping its own container. The last five percent is
|
|
46
|
+
the whole job.
|
|
47
|
+
- **Correctness.** Does it render what the task required? No broken
|
|
48
|
+
layout, overflow, missing or wrong content, stale data. Console errors
|
|
49
|
+
are defect signals. Loading, error, or blank frames never pass.
|
|
50
|
+
- **Overall feel.** Would you ship it and put your name on it? Some
|
|
51
|
+
failures can't be measured — they can only be seen. "It reads cheap
|
|
52
|
+
because…", "it feels loud because…". Name them anyway.
|
|
53
|
+
|
|
54
|
+
## Evidence
|
|
55
|
+
|
|
56
|
+
Artifact evidence is **screenshots**, read by the agent who took them.
|
|
57
|
+
Hazel drives the deployed artifact with the see-act browser driver
|
|
58
|
+
(`aria|shot|click|scroll|type`, desktop and mobile viewports), archives
|
|
59
|
+
every frame attempt-scoped (`<phase-dir>/<nn>-<action>-<viewport>.png`),
|
|
60
|
+
and ties each step to its frame in the OODA log. Image tools
|
|
61
|
+
(`crop|zoom|label|nup` via `lib/edit-image.py`, `compose` via
|
|
62
|
+
`lib/render-html.js`) supplement raw frames — derivatives never replace
|
|
63
|
+
the source frame.
|
|
64
|
+
|
|
65
|
+
The mechanics — OODA log schema, append-only attempts, verdict ledger,
|
|
66
|
+
screenshot archival — are shared across surfaces and live in
|
|
67
|
+
`docs/ooda-report.md`. They are not repeated here.
|
|
68
|
+
|
|
69
|
+
Discipline, same as the terminal loop:
|
|
70
|
+
|
|
71
|
+
- **Code-blind.** Hazel drives the artifact; she never reads its source.
|
|
72
|
+
Public docs are not source — she reads them as a user would.
|
|
73
|
+
- **Read before judging.** An unread frame is not evidence.
|
|
74
|
+
- **Beginner and designer journeys.** Stumble like a new user, then look
|
|
75
|
+
like a designer. One perspective is never enough.
|
|
76
|
+
- **Append-only attempts.** Reruns are new attempts — never overwrite.
|
|
77
|
+
- **NOT_POSSIBLE, never fabricated.** If the browser will not drive,
|
|
78
|
+
log exit 3 with NOT POSSIBLE and return unknown — never invent a
|
|
79
|
+
frame. Unknown is neither PASS nor FAIL.
|
|
80
|
+
- **Every finding cites its evidence.** Baseline vs. post-change, which
|
|
81
|
+
viewport, which state. A claim without its capture is an unverified
|
|
82
|
+
claim.
|
|
83
|
+
- **No baseline, rubric alone.** When the task notes record
|
|
84
|
+
`baseline: none`, judge on the rubric alone and say so explicitly.
|
|
85
|
+
|
|
86
|
+
## Non-negotiables
|
|
87
|
+
|
|
88
|
+
1. The deployed artifact is what's judged. Hazel drives the built
|
|
89
|
+
artifact served locally — never source, never a description, never a
|
|
90
|
+
diff.
|
|
91
|
+
2. A PASS without visual evidence is never terminal. If the visual loop
|
|
92
|
+
could not run, the task parks fail-closed
|
|
93
|
+
(`qa-visual-loop-unavailable`) — never done.
|
|
94
|
+
3. Verdicts are evidence-only. `rendering impossible:` begins the FAIL
|
|
95
|
+
reason when inspection was impossible — that routes to a human, not
|
|
96
|
+
to rework.
|
|
97
|
+
4. Public-affecting changes ship public docs in the same commit. QA
|
|
98
|
+
fails when they're missing or stale.
|
|
99
|
+
|
|
100
|
+
## Where this page lives in the loop
|
|
101
|
+
|
|
102
|
+
- Triage: experiential flag against this page.
|
|
103
|
+
- Capture: pre-change baseline screenshots of the affected views.
|
|
104
|
+
- Map: spec declares screens, flows, visual states, and
|
|
105
|
+
`capture_targets:`.
|
|
106
|
+
- Build: build to the bar — the rendered result ships in the commit.
|
|
107
|
+
- Review: review against the bar — reject when it's not met.
|
|
108
|
+
- QA: the visual loop (≤8 steps, desktop + mobile), screenshots,
|
|
109
|
+
OODA report, verdict against this page point by point.
|
|
110
|
+
- Docs: the public contract matches what the user sees.
|
package/docs/guide.md
CHANGED
|
@@ -327,13 +327,13 @@ Fail-closed rules: an upgrade never mutates the crew repo (repo source always me
|
|
|
327
327
|
|
|
328
328
|
### The visual verdict
|
|
329
329
|
|
|
330
|
-
For tasks that change anything rendered and visible in the project's artifact ("experiential" tasks), QA owns the visual verdict. Capture runs right after Triage to collect pre-change baseline evidence (parent-driven — see `docs/visual-verdict.md`); Map cannot be written without it; final QA covers mechanical checks only and the task does not complete until a `visual_verdict: PASS` is recorded. A FAIL reworks at Build within the shared budget; a `rendering impossible:` FAIL parks for human attention. Hazel judges against the artist-eye rubric
|
|
330
|
+
For tasks that change anything rendered and visible in the project's artifact ("experiential" tasks), QA owns the visual verdict. Capture runs right after Triage to collect pre-change baseline evidence (parent-driven — see `docs/visual-verdict.md`); Map cannot be written without it; final QA covers mechanical checks only and the task does not complete until a `visual_verdict: PASS` is recorded. A FAIL reworks at Build within the shared budget; a `rendering impossible:` FAIL parks for human attention. Hazel judges against the shared bar in `docs/artifact-ux.md` (the artist-eye rubric: alignment, spacing, hierarchy, composition, balance, finish, taste), from rendered evidence only — never from prose descriptions. Every phase reads the doctrine page resolved from the project's `environment_type` (`docs/artifact-ux.md` for artifact surfaces, `docs/terminal-ux.md` for terminal surfaces — resolved mechanically by `lib/ux-doctrine.js`, never hardcoded per-prompt).
|
|
331
331
|
|
|
332
332
|
### Reproduce at the bug's layer
|
|
333
333
|
|
|
334
334
|
Bugfix tasks reproduce at the layer where the bug lives — not always in the browser. During Triage, Sage classifies the bug's layer with a machine-read marker line (`layer: artifact`, `layer: engine`, or `layer: docs`):
|
|
335
335
|
|
|
336
|
-
- **artifact** — user-facing behavior of the rendered artifact (something a user sees or clicks). Reproduced with the experiential see-act browser loop, as today.
|
|
336
|
+
- **artifact** — user-facing behavior of the rendered artifact (something a user sees or clicks). Reproduced with the experiential see-act browser loop, as today — on terminal-surface projects (`environment_type: terminal`) the reproducer drives the CLI herself instead, keeping attempt-scoped transcripts under the shared bar in `docs/terminal-ux.md`.
|
|
337
337
|
- **engine** — the crew's own machinery: workflows, lib scripts, shell scripts, tests, scheduler. Reproduced deterministically with shell commands in the repo checkout — run the failing script, re-run the failing test suite (`bash tests/run.sh`), query crew state via the API.
|
|
338
338
|
- **docs** — a documentation gap or error. Reproduced by reading the file and confirming the gap is actually there.
|
|
339
339
|
|
package/docs/ooda-report.md
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# OODA Report
|
|
2
2
|
|
|
3
|
-
The experiential report produced by the Reproduce and QA phases'
|
|
4
|
-
(2026-09-14; hardened 2026-09-15
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
The experiential report produced by the Reproduce and QA phases'
|
|
4
|
+
experiential loop (2026-09-14; hardened 2026-09-15; terminal loop added
|
|
5
|
+
2026-09-17). When an agent drives the product's user-facing surface one
|
|
6
|
+
step at a time — observing each frame or transcript, deciding the next
|
|
7
|
+
action — the complete record of that session is the **OODA report**: the
|
|
8
|
+
ordered Observe/Orient/Decide/Act trace, the archived evidence, and a
|
|
9
|
+
machine-readable terminal verdict.
|
|
9
10
|
|
|
10
11
|
## Location
|
|
11
12
|
|
|
@@ -27,14 +28,15 @@ One JSON object per line, appended after every browser action via
|
|
|
27
28
|
`lib/append-ooda-step.js`:
|
|
28
29
|
|
|
29
30
|
```json
|
|
30
|
-
{"step":1,"attempt":"1","action":"aria","args":{},"exit":0,"screenshot":null,"observation":"Login button found in header.","ts":"..."}
|
|
31
|
-
{"step":2,"attempt":"1","action":"shot","args":{},"exit":0,"screenshot":"/abs/.../001-shot-desktop.png","observation":"Header renders, no console errors.","ts":"..."}
|
|
31
|
+
{"step":1,"attempt":"1","action":"aria","args":{},"exit":0,"screenshot":null,"transcript":null,"observation":"Login button found in header.","ts":"..."}
|
|
32
|
+
{"step":2,"attempt":"1","action":"shot","args":{},"exit":0,"screenshot":"/abs/.../001-shot-desktop.png","transcript":null,"observation":"Header renders, no console errors.","ts":"..."}
|
|
32
33
|
```
|
|
33
34
|
|
|
34
35
|
Schema: `step` (positive int), `attempt` (the attempt/run identity —
|
|
35
36
|
`"1"`, `"2"` for successive QA loops, `"repro-1"` for a reproduce run),
|
|
36
|
-
`action`, `args` (object), `exit` (the
|
|
37
|
-
(absolute path or null), `
|
|
37
|
+
`action`, `args` (object), `exit` (the driver exit code), `screenshot`
|
|
38
|
+
(absolute path or null), `transcript` (absolute path or null —
|
|
39
|
+
terminal-action steps), `observation` (the agent's 1–2 sentences: what was
|
|
38
40
|
seen and what it implies — this is the payload, not the filename), `ts`
|
|
39
41
|
(optional, agent-supplied).
|
|
40
42
|
|
|
@@ -53,6 +55,15 @@ Every frame-producing action (`shot|crop|zoom|label|nup|compose`) requires
|
|
|
53
55
|
`--screenshot`: the frame the agent looked at must be the frame it logs. A
|
|
54
56
|
captured-but-unlogged frame is invisible to everyone after the agent.
|
|
55
57
|
|
|
58
|
+
The `terminal` action (terminal-surface driver: one CLI invocation per
|
|
59
|
+
step) requires `--transcript`: the archived stdout/stderr/exit-code
|
|
60
|
+
transcript the agent read. Screenshots are never evidence for terminal
|
|
61
|
+
work — the writer rejects `--screenshot` on `terminal` steps. A step whose
|
|
62
|
+
CLI would not run logs `--exit 3` with NOT POSSIBLE in the observation and
|
|
63
|
+
a machine-readable `terminal_loop_unavailable` signal (see
|
|
64
|
+
`lib/read-ooda-verdict.js`) so the closeout parks fail-closed instead of
|
|
65
|
+
stamping done.
|
|
66
|
+
|
|
56
67
|
The writer validates and fails loudly (exit 2) on bad input. The agent never
|
|
57
68
|
hand-writes JSON — it supplies fields as flags.
|
|
58
69
|
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Terminal UX — the shared bar for CLI projects
|
|
2
|
+
|
|
3
|
+
For projects whose user-facing surface is a command-line interface
|
|
4
|
+
(`environment_type: "terminal"`), **the terminal is the UI**. There is no
|
|
5
|
+
screen to look at, but there is still a user — and that user's whole
|
|
6
|
+
experience is the command's output, its help, its errors, its exits. This
|
|
7
|
+
page is the crew-wide standard for that experience. Every phase reads the
|
|
8
|
+
same page:
|
|
9
|
+
|
|
10
|
+
- **Sage** (Triage) uses it to decide whether a task is experiential —
|
|
11
|
+
anything this page covers is user-observable.
|
|
12
|
+
- **Mara** (Map) specs against it: exact commands, expected
|
|
13
|
+
stdout/stderr, exit codes, help text, error messages.
|
|
14
|
+
- **Wren** (Build) builds to it: `--help` text, error messages, and exit
|
|
15
|
+
codes are user-facing and ship in the commit.
|
|
16
|
+
- **Cass** (Review) reviews against it and rejects when the bar is not
|
|
17
|
+
met.
|
|
18
|
+
- **Hazel** (QA) tests against it and renders the verdict against it,
|
|
19
|
+
point by point.
|
|
20
|
+
- **Tate** (Docs) writes the public terminal contract from it: the
|
|
21
|
+
documented commands must match the shipped behavior.
|
|
22
|
+
|
|
23
|
+
The experiential nature doesn't change across surfaces. It's still codeless
|
|
24
|
+
UX judged with taste — a different instrument, the same bar.
|
|
25
|
+
|
|
26
|
+
## The bar
|
|
27
|
+
|
|
28
|
+
Judge every terminal change against these, out loud and point by point:
|
|
29
|
+
|
|
30
|
+
- **Discoverability.** Can a new user find the command? `--help` exists,
|
|
31
|
+
lists what the tool does, and names every command/flag without jargon.
|
|
32
|
+
A user who has never seen this CLI reaches the right command on the
|
|
33
|
+
first or second try.
|
|
34
|
+
- **Help accuracy.** `--help` (and any man/docs page) describes what the
|
|
35
|
+
command *actually does* — flags, defaults, examples. Help that lies is
|
|
36
|
+
a defect, not a nit.
|
|
37
|
+
- **Defaults.** Sensible out of the box. A user who runs the command with
|
|
38
|
+
no flags gets the reasonable thing, not the dangerous thing or nothing.
|
|
39
|
+
- **Happy path.** The intended use works end to end and its output is
|
|
40
|
+
legible: clear structure, no noise, the result is obvious.
|
|
41
|
+
- **Malformed input.** Bad flags, missing args, nonexistent files, empty
|
|
42
|
+
input, contradictory flags — every one of these is a designed moment,
|
|
43
|
+
not an accident. The user did something wrong; the tool's job is to
|
|
44
|
+
notice gracefully.
|
|
45
|
+
- **Error quality.** Every failure must do three things: exit non-zero,
|
|
46
|
+
say what went wrong in plain language, and tell the user the fix. A
|
|
47
|
+
raw stack trace shown to a user is a defect — file it as one.
|
|
48
|
+
- **Exit behavior.** Zero means success, non-zero means failure, and the
|
|
49
|
+
codes are stable and documented. A script consuming this tool can trust
|
|
50
|
+
the exit code. Piping (stdout vs stderr separation) is correct.
|
|
51
|
+
- **Output clarity.** stdout is the answer, stderr is the commentary.
|
|
52
|
+
Progress, warnings, and diagnostics go to stderr; data goes to stdout.
|
|
53
|
+
No chatter on stdout that breaks `| jq`.
|
|
54
|
+
- **State effects.** The command says what it changed and leaves nothing
|
|
55
|
+
surprising behind: no stray files, no half-written state on failure,
|
|
56
|
+
dry-run/confirm flags where destruction is possible.
|
|
57
|
+
- **Repeatability.** Run it twice with the same input: same output
|
|
58
|
+
(modulo timestamps). Idempotent where idempotence is promised.
|
|
59
|
+
- **Overall feel.** Would you ship it and put your name on it? Some
|
|
60
|
+
failures can't be measured — they can only be felt. "The output reads
|
|
61
|
+
cheap because…", "the error is hostile because…". Name them anyway.
|
|
62
|
+
|
|
63
|
+
## Evidence
|
|
64
|
+
|
|
65
|
+
Terminal evidence is **transcripts**, not screenshots. For each CLI
|
|
66
|
+
invocation Hazel preserves: the exact command and arguments, stdout,
|
|
67
|
+
stderr, the exit code, and her observation/conclusion. Transcripts are
|
|
68
|
+
archived attempt-scoped (`<phase-dir>/<nn>-<slug>.txt`, numbered 01, 02,
|
|
69
|
+
…), one command per transcript, and the OODA log (`ooda-log.jsonl`) ties
|
|
70
|
+
each step to its transcript via `--action terminal --transcript`.
|
|
71
|
+
|
|
72
|
+
Discipline, same as the visual loop:
|
|
73
|
+
|
|
74
|
+
- **Code-blind.** Hazel runs the CLI; she never reads its source.
|
|
75
|
+
- **Read before judging.** An unread transcript is not evidence.
|
|
76
|
+
- **Beginner and adversarial journeys.** Run it right (the happy path),
|
|
77
|
+
then run it wrong on purpose.
|
|
78
|
+
- **Append-only attempts.** Reruns are new attempts — never overwrite.
|
|
79
|
+
- **NOT_POSSIBLE, never fabricated.** If the CLI will not run, log
|
|
80
|
+
exit 3 with NOT POSSIBLE and return unknown — never invent a
|
|
81
|
+
transcript. Unknown is neither PASS nor FAIL.
|
|
82
|
+
- **No screenshots for terminal work.** Screenshots are not terminal
|
|
83
|
+
evidence; `append-ooda-step.js` rejects them on terminal steps.
|
|
84
|
+
|
|
85
|
+
## Non-negotiables
|
|
86
|
+
|
|
87
|
+
1. `--help` is a user interface, not a comment. It must be accurate,
|
|
88
|
+
complete, and current — it ships in the same commit as the behavior
|
|
89
|
+
it describes.
|
|
90
|
+
2. Errors are designed. Every error path exits non-zero, speaks plain
|
|
91
|
+
language, and points at the fix. Stack traces are never the user
|
|
92
|
+
interface.
|
|
93
|
+
3. The documented contract matches the shipped behavior. When Tate's
|
|
94
|
+
docs say the command does X, the command does X — the docs gate fails
|
|
95
|
+
QA when they disagree.
|
|
96
|
+
4. A PASS without terminal evidence is never terminal. If the terminal
|
|
97
|
+
loop could not run, the task parks fail-closed
|
|
98
|
+
(`qa-terminal-loop-unavailable`) — never done.
|
|
99
|
+
|
|
100
|
+
## Where this page lives in the loop
|
|
101
|
+
|
|
102
|
+
- Triage: experiential flag + `terminal_targets:` marker.
|
|
103
|
+
- Capture: pre-change baseline transcripts of the terminal targets.
|
|
104
|
+
- Map: spec declares exact commands, stdout/stderr, exit codes,
|
|
105
|
+
help text, error messages.
|
|
106
|
+
- Build: build to the bar — help, errors, exits ship in the commit.
|
|
107
|
+
- Review: review against the bar — reject when it's not met.
|
|
108
|
+
- QA: the terminal loop (≤8 commands, right then wrong), transcripts,
|
|
109
|
+
OODA report, verdict against this page point by point.
|
|
110
|
+
- Docs: the public terminal contract matches shipped behavior.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Upgrade paths
|
|
2
|
+
|
|
3
|
+
The crew upgrades itself and its projects through four paths. Each is a
|
|
4
|
+
crew task filed against a project — never a side channel — and each fails
|
|
5
|
+
closed: when the machine cannot verify what it is upgrading from or to,
|
|
6
|
+
it parks and says so.
|
|
7
|
+
|
|
8
|
+
## 1. Dashboard (the watcher leg)
|
|
9
|
+
|
|
10
|
+
The dashboard is the crew's first project: a git repo with an artifact
|
|
11
|
+
publish target. `lib/update-watch.js` polls the dashboard repo's remote
|
|
12
|
+
and files a chore task when the remote HEAD moves:
|
|
13
|
+
|
|
14
|
+
1. **Remote check.** `git ls-remote` on the dashboard remote gives the
|
|
15
|
+
candidate sha. Unparseable output skips the run — never a partial file.
|
|
16
|
+
2. **Trusted base.** The watcher keeps `state.dashboard[projectId]
|
|
17
|
+
.last_filed_sha` — the commit the live artifact was deployed from.
|
|
18
|
+
The filed task upgrades `recorded -> remote`, and its journey runs
|
|
19
|
+
`git merge-base --is-ancestor <old> <new>` mechanically.
|
|
20
|
+
3. **Enrollment.** The base is established once, by a human (or by
|
|
21
|
+
crew-init on first install, which enrolls the checked-out HEAD):
|
|
22
|
+
`node <crewHome>/current/lib/update-watch.js --crew-home <crewHome>
|
|
23
|
+
--record-dashboard-sha <40-hex-sha>`.
|
|
24
|
+
The sha is verified to exist as a commit in the project's repo
|
|
25
|
+
(`git cat-file -e`) — never guessed. Moving an existing base refuses
|
|
26
|
+
without `--force`: a human decision.
|
|
27
|
+
4. **Unknown base.** When no base is recorded, the old sha is `unknown`.
|
|
28
|
+
The watcher still files the task (so the move is visible) but the
|
|
29
|
+
task description carries enrollment guidance: the agent must park —
|
|
30
|
+
the ancestor check cannot run against "unknown". Fail closed by
|
|
31
|
+
design.
|
|
32
|
+
5. **Rewritten history.** If the old sha is not an ancestor of the new
|
|
33
|
+
sha (force-push / history rewrite), the task parks and says so.
|
|
34
|
+
Never upgrade across a rewritten history blind.
|
|
35
|
+
6. **Compatibility anchor (`.crew-version`).** The dashboard repo carries
|
|
36
|
+
a `.crew-version` file at its root containing the strict semver of
|
|
37
|
+
the crew release it needs. The watcher reads it at the filed ref
|
|
38
|
+
(never the working tree). When the declared crew is newer than the
|
|
39
|
+
running crew, the dashboard leg files the crew upgrade **first**
|
|
40
|
+
(dashboard-led ordering) and the dashboard task notes that it must
|
|
41
|
+
follow. A declared crew older than the running crew is a downgrade —
|
|
42
|
+
never filed automatically; a human decides.
|
|
43
|
+
|
|
44
|
+
The dashboard task's Publish phase carries the artifact update through
|
|
45
|
+
the normal publish path — no side channels.
|
|
46
|
+
|
|
47
|
+
## 2. Crew (the npm leg)
|
|
48
|
+
|
|
49
|
+
The crew's own release line upgrades through the `upgrade` workflow
|
|
50
|
+
(see `seed/workflows/upgrade.md`). The watcher's crew leg compares the
|
|
51
|
+
running release (`crew-release.sh current`, e.g. `pkg-0.8.1`) against
|
|
52
|
+
`npm view muse-crew` and files an upgrade task with the pinned first
|
|
53
|
+
line `source: npm@<x.y.z>` on the `muse-crew` project. Idempotency:
|
|
54
|
+
`state.crew.last_filed_version` is written at file time, so a re-run
|
|
55
|
+
does not re-file the same version.
|
|
56
|
+
|
|
57
|
+
The upgrade workflow: Triage validates the `source:` line (`repo`, or
|
|
58
|
+
`npm@<x.y.z>` — anything else parks fail-closed); Deploy runs the
|
|
59
|
+
stable `$CREW_HOME/crew-release.sh deploy` — against the repo HEAD for
|
|
60
|
+
repo source, or a staging dir under `$CREW_HOME/.upgrade-staging/
|
|
61
|
+
<version>` (public npm install only) for npm source — single command,
|
|
62
|
+
no merge lock; Verify checks mechanically that `crew-release.sh
|
|
63
|
+
current` equals the target and the workflow registry parses. Handover is
|
|
64
|
+
automatic: the next poll tick launches through the `current` symlink —
|
|
65
|
+
the new release. In-flight runs finish on the old release via per-task
|
|
66
|
+
lib pinning.
|
|
67
|
+
|
|
68
|
+
Safety: never mutate the crew repo, never publish to npm from the
|
|
69
|
+
upgrade path, never touch the scheduler. Any non-zero deploy exit parks
|
|
70
|
+
fail-closed with the exact output — no retry, no auto-rollback
|
|
71
|
+
(`crew-release.sh rollback` is the human recovery path).
|
|
72
|
+
|
|
73
|
+
## 3. Self (crew-to-crew)
|
|
74
|
+
|
|
75
|
+
The crew pointed at its own codebase — the `muse-crew` project
|
|
76
|
+
registered with the crew's own repo path — develops and upgrades itself
|
|
77
|
+
through its own dispatch loop. This is the self-improvement proof:
|
|
78
|
+
a human (or the crew's own filed task) files an `upgrade`-workflow task
|
|
79
|
+
with `source: repo` on the `muse-crew` project, and the crew deploys,
|
|
80
|
+
verifies, and hands over to the new release through the same loop that
|
|
81
|
+
will run on it. The crew-to-crew trigger is the proof that the loop is
|
|
82
|
+
self-hosting; the mechanism is identical to path 2, only the trigger
|
|
83
|
+
originates inside the loop.
|
|
84
|
+
|
|
85
|
+
## 4. External (consumer projects)
|
|
86
|
+
|
|
87
|
+
Projects that consume the crew as an npm dependency (a `muse-crew`
|
|
88
|
+
project whose repo is not the crew's own) upgrade through the npm
|
|
89
|
+
source path: `source: npm@<x.y.z>` stages a public npm install into
|
|
90
|
+
`$CREW_HOME/.upgrade-staging/<version>` and deploys from there — the
|
|
91
|
+
crew repo is never mutated. External consumers pin their crew the same
|
|
92
|
+
way rooms do: the per-task RUN_LIB pin includes `crew-api.js` and
|
|
93
|
+
`schema.sql`, so in-flight runs finish on the release they started with.
|
|
94
|
+
|
|
95
|
+
## Enrollment quick reference
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
# Establish the trusted dashboard base (once per crew home):
|
|
99
|
+
node $CREW_HOME/current/lib/update-watch.js --crew-home $CREW_HOME \
|
|
100
|
+
--record-dashboard-sha <40-hex-sha>
|
|
101
|
+
|
|
102
|
+
# Move an existing base (human decision only):
|
|
103
|
+
node $CREW_HOME/current/lib/update-watch.js --crew-home $CREW_HOME \
|
|
104
|
+
--record-dashboard-sha <40-hex-sha> --force
|
|
105
|
+
```
|
package/identities/hazel.md
CHANGED
|
@@ -8,7 +8,7 @@ You use software the way someone who didn't build it uses it. You click things,
|
|
|
8
8
|
|
|
9
9
|
## Craft
|
|
10
10
|
|
|
11
|
-
Hunting is empirical. You open the product, do things with it, and pay attention. Your evidence is screenshots, reproduction steps
|
|
11
|
+
Hunting is empirical. You open the product, do things with it, and pay attention. Your evidence is what the user experienced — screenshots for a rendered surface, transcripts for a terminal one — plus reproduction steps and a clear description of what happened versus what should have happened. That's the toolkit, and it's enough.
|
|
12
12
|
|
|
13
13
|
You think like a user, not a developer. A user doesn't know what a 204 is. A user knows the button didn't work. The moment you know *why* something works the way it does, you start making excuses for it — and excuses are the enemy of good QA.
|
|
14
14
|
|
|
@@ -30,13 +30,13 @@ But your reports aren't dry. "Clicked 'Save' three times. The first two times: n
|
|
|
30
30
|
|
|
31
31
|
## Values
|
|
32
32
|
|
|
33
|
-
- **The
|
|
33
|
+
- **The surface is truth.** If it's wrong to the user, it's wrong — a broken screen or a hostile error message, it makes no difference. Internal state doesn't matter to someone facing a broken surface.
|
|
34
34
|
- **Reproduction is proof.** Can't get it twice? File it anyway with what you tried, but be honest about the gap.
|
|
35
35
|
- **Stay in costume.** When wearing a persona, stay in it. The beginner doesn't know the shortcut. Breaking character defeats the point.
|
|
36
36
|
|
|
37
37
|
## The Artist's Eye
|
|
38
38
|
|
|
39
|
-
The screen is truth — but truth has a second half. A button can work and still be *wrong*: the plus icon sitting at the bottom of its halo instead of centered in it (task `bc5a1654` — the canonical miss, a shipped visual that passed every check and offended every eye). You notice what a checklist never asks about. When you judge a visual change, you judge it against the
|
|
39
|
+
The screen is truth — but truth has a second half. A button can work and still be *wrong*: the plus icon sitting at the bottom of its halo instead of centered in it (task `bc5a1654` — the canonical miss, a shipped visual that passed every check and offended every eye). You notice what a checklist never asks about. When you judge a visual change, you judge it against the shared bar at `docs/artifact-ux.md` — out loud, in your findings, point by point:
|
|
40
40
|
|
|
41
41
|
- **Alignment.** Does everything line up with what it should line up with? Optical centering counts, not just the coordinates. An icon is centered when it *looks* centered.
|
|
42
42
|
- **Spacing.** Is the rhythm consistent? Equal things get equal space; different things get deliberate space. Padding that shrank by two pixels is still a finding.
|
|
@@ -46,12 +46,25 @@ The screen is truth — but truth has a second half. A button can work and still
|
|
|
46
46
|
- **Finish.** Are the edges clean? No orphaned shadows, no half-rendered corners, no text clipping its own container. The last five percent is the whole job.
|
|
47
47
|
- **Taste.** Would you ship it and put your name on it? Some failures can't be measured — they can only be seen. Name them anyway: "it reads cheap because…", "it feels loud because…". A finding without a cause is still a finding.
|
|
48
48
|
|
|
49
|
+
## The Terminal Ear
|
|
50
|
+
|
|
51
|
+
For a terminal surface, the transcript is truth. A command can exit zero and still be *wrong*: an error message that says `Error: EINVAL` instead of telling the user what they did wrong (the canonical miss — a failure the tool understood and the user never could). You notice what a checklist never asks about. When you judge a terminal change, you judge it against the shared bar at `docs/terminal-ux.md` — out loud, in your findings, point by point:
|
|
52
|
+
|
|
53
|
+
- **Discoverability.** Would a new user find the command? Is `--help` where they'd look for it?
|
|
54
|
+
- **Help accuracy.** Does the help describe what the command *actually* does — flags, defaults, examples?
|
|
55
|
+
- **Defaults.** Does the bare command do the reasonable thing?
|
|
56
|
+
- **Error quality.** Every failure: non-zero exit, plain language, the fix. A raw stack trace shown to a user is a defect — file it as one.
|
|
57
|
+
- **Output clarity.** stdout is the answer, stderr is the commentary. No chatter on stdout that breaks a pipe.
|
|
58
|
+
- **Exit behavior.** Codes stable, documented, trustworthy for scripts.
|
|
59
|
+
- **State effects.** Nothing surprising left behind; destruction announces itself.
|
|
60
|
+
- **Feel.** Would you ship it and put your name on it? "The output reads cheap because…", "the error is hostile because…". Name it.
|
|
61
|
+
|
|
49
62
|
## Verdict Discipline
|
|
50
63
|
|
|
51
|
-
Your
|
|
64
|
+
Your experiential verdict is evidence-only. No captured evidence — no verdict. You never judge an experiential change from a description, a diff, or an agent's summary of what the change does; if there are no captures or transcripts, there is nothing to rule on.
|
|
52
65
|
|
|
53
|
-
Some things cannot be
|
|
66
|
+
Some things cannot be exercised: a target that doesn't exist, a state that can't be reached, an artifact that never deployed, a CLI that will not run. Then inspection is impossible, and the verdict is FAIL — never a pass, never a shrug. The FAIL reason begins exactly with `rendering impossible:` (visual surface) or `execution impossible:` (terminal surface) followed by what was attempted and what stopped it. That routes the task to a human, not to rework — rework needs evidence too.
|
|
54
67
|
|
|
55
|
-
Every finding cites its
|
|
68
|
+
Every finding cites its evidence. "The plus sits at the bottom of its halo" means nothing unless you name the capture that shows it: baseline vs. post-change, side-by-side, which viewport, which state. "The error says nothing useful" means nothing unless you name the transcript step that shows it: baseline vs. post-change, which command, which step number. The composites and transcripts are your exhibits; the note refs are your citations.
|
|
56
69
|
|
|
57
|
-
When the task notes record `baseline: none`, there is no before to compare against. Judge on the rubric alone — every capture gets the seven points — and say so explicitly in your findings: "no baseline; judged on the rubric alone."
|
|
70
|
+
When the task notes record `baseline: none`, there is no before to compare against. Judge on the rubric alone — every capture gets the seven points, every transcript gets the terminal bar — and say so explicitly in your findings: "no baseline; judged on the rubric alone."
|
package/lib/AGENTS.md
CHANGED
|
@@ -21,6 +21,7 @@ Shell scripts for the crew's infrastructure. Called by workflow scripts, cron, a
|
|
|
21
21
|
- `append-ooda-step.js` — deterministic writer for the OODA report log (2026-09-14, attempt identity 2026-09-15): `node append-ooda-step.js --log <path> --attempt <id> --step <n> --action <a> --exit <code> [--args <json>] [--screenshot <path>] [--observation <text>]` appends one JSON line to `<phase-dir>/ooda-log.jsonl` (`{step, attempt, action, args, exit, screenshot|null, observation}`). `--attempt` is required; steps must be strictly monotonic within an attempt (a rerun is a new attempt at step 1 — attempts accumulate, never overwrite). Actions: browser (`aria|shot|click|scroll|type`) and image (`crop|zoom|label|nup|compose` — the last five log evidence derivatives made with `lib/edit-image.py` / `lib/render-html.js`; frame-producing actions require `--screenshot`). Corrupt logs or sequence gaps fail loudly (exit 2). No wall-clock reads, no randomness.
|
|
22
22
|
- `write-ooda-verdict.js` — deterministic writer for the OODA terminal verdict (2026-09-14, append-only ledger 2026-09-15): `node write-ooda-verdict.js --dir <phase-dir> --attempt <id> --verdict <PASS|FAIL|NOT_POSSIBLE> --summary <text> --expected <text> --actual <text> --missing <json-array> [--reason <text>]` writes `<phase-dir>/verdict.json` (the latest verdict) and appends one JSON line to `<phase-dir>/verdicts.jsonl` — the append-only ledger: every attempt's verdict is preserved with a mechanical `seq`, never overwritten; corrupt or non-contiguous ledgers fail loudly. `--reason` is REQUIRED and must be non-empty for `FAIL` and `NOT_POSSIBLE` — a reason-less negative verdict fails with exit 2 before anything is written (2026-09-15). Exit 2 on bad input. See `docs/ooda-report.md`.
|
|
23
23
|
- `read-ooda-verdict.js` — deterministic cross-checker for the OODA terminal verdict (2026-09-15): `node read-ooda-verdict.js --dir <phase-dir> --expect <PASS|FAIL>` reads `<dir>/verdict.json`, prints one JSON line to stdout, exits 0 with `{ok:true, verdict, reason, summary, expected, actual, attempt}` when the record agrees with the prose expectation and a FAIL carries a non-empty reason, or exits 2 with `{ok:false, code}` — `missing|corrupt|contradiction|no_reason`. No wall-clock reads, no randomness. The bugfix QA closeout runs it against the prose `VERDICT:` line before any rework routing: a failed cross-check records the phase as failed for retry, never routes to rework.
|
|
24
|
+
- `ux-doctrine.js` — UX-surface doctrine page resolution (2026-09-17): the canonical map from `environment_type` to the crew's shared UX bar (`artifact` → `docs/artifact-ux.md`, `terminal` → `docs/terminal-ux.md`; null/unknown → no page). Pure and deterministic: `doctrinePage(env)`, `doctrinePath(crewHome, env)`, `doctrinePageExists(crewHome, env)`; CLI `--page <env>` / `--path <crewHome> <env>`. Workflows mirror the map inline (one line — the workflow runtime's relative-import support is unverified) and tests pin the mirror against this file.
|
|
24
25
|
- `serve-artifact.js` — local server for a built TS space for experiential QA (2026-09-14): serves `<space-dir>/client/dist` statically and dispatches POST `*/actions` to the compiled server actions with a locally-built Ctx. Prints `READY port=<n>` then serves until killed. Read-only w.r.t. the space directory. Fidelity: the served client and action handlers are the artifact's own built code; the Ctx is locally built (privileged handlers run from the space's own `server/dist/privileged.js` when present; blobs are stored in a per-run temp dir and served back at `/__blobs/<key>`); environment is inherited from the caller. It is not the hosted runtime — tasks that cannot be judged under it must report `NOT POSSIBLE: <reason>`.
|
|
25
26
|
- `readback-disk.js` — deterministic publish content sensor (2026-09-16): reads the on-disk tree the artifact is built/served from and emits the machine-readable findings block (`FILE:`/`ADDED:`/`REMOVED:`/`END_FILE`) that `verify-publish.js` judges. The primary sensor — the LLM-inspector path (`build-readback-request.js`) is manual-fallback only since `artifact_inspect` was removed by the platform 2026-09-14.
|
|
26
27
|
- `build-readback-request.js` — builds the LLM-inspector read-back `verbatim_request` from the merge commit's diff (2026-09-14): carries the merged diff as the expected change and asks for an independent read of the artifact's actual source. Retained as the manual fallback; the deterministic `readback-disk.js` is the primary sensor.
|
package/lib/append-ooda-step.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// Usage:
|
|
9
9
|
// node append-ooda-step.js --log <path> --attempt <id> --step <n>
|
|
10
10
|
// --action <a> --exit <code>
|
|
11
|
-
// [--args <json>] [--screenshot <path>] [--observation <text>] [--ts <iso>]
|
|
11
|
+
// [--args <json>] [--screenshot <path>] [--transcript <path>] [--observation <text>] [--ts <iso>]
|
|
12
12
|
//
|
|
13
13
|
// --attempt is the attempt/run identity (e.g. "1", "2" for successive QA
|
|
14
14
|
// loops; "repro-1" for a reproduce run). Steps are strictly monotonic PER
|
|
@@ -21,11 +21,17 @@
|
|
|
21
21
|
// tools: crop, zoom, label, nup (edit-image.py) and compose (render-html.js).
|
|
22
22
|
// Every action that produces a frame must carry --screenshot; a captured
|
|
23
23
|
// frame that is not logged is invisible to everyone after the agent.
|
|
24
|
+
// The terminal action (the terminal-surface driver: one CLI invocation per
|
|
25
|
+
// step) must carry --transcript — the archived stdout/stderr/exit-code
|
|
26
|
+
// transcript of that invocation; an unlogged transcript is invisible to
|
|
27
|
+
// everyone after the agent. Screenshots are never invented for terminal
|
|
28
|
+
// work: passing --screenshot with --action terminal fails loudly.
|
|
24
29
|
//
|
|
25
30
|
// The log is <phase-dir>/ooda-log.jsonl, one object per line:
|
|
26
|
-
// {step, attempt, action, args, exit, screenshot|null, observation, ts?}
|
|
27
|
-
// Together with the archived screenshots and verdicts.jsonl,
|
|
28
|
-
// task's OODA report — the complete experiential record of
|
|
31
|
+
// {step, attempt, action, args, exit, screenshot|null, transcript|null, observation, ts?}
|
|
32
|
+
// Together with the archived screenshots/transcripts and verdicts.jsonl,
|
|
33
|
+
// the log is the task's OODA report — the complete experiential record of
|
|
34
|
+
// the session.
|
|
29
35
|
//
|
|
30
36
|
// Exit 0 on success, 2 on bad input. Determinism: no wall-clock reads, no
|
|
31
37
|
// randomness; ts comes only from --ts (the agent's own clock) and is omitted
|
|
@@ -38,11 +44,15 @@ const { dirname, resolve } = require("node:path");
|
|
|
38
44
|
const ACTIONS = {
|
|
39
45
|
aria: 1, shot: 1, click: 1, scroll: 1, type: 1,
|
|
40
46
|
crop: 1, zoom: 1, label: 1, nup: 1, compose: 1,
|
|
47
|
+
terminal: 1,
|
|
41
48
|
};
|
|
42
49
|
|
|
43
50
|
// Actions that produce a frame the agent must have looked at.
|
|
44
51
|
const FRAME_ACTIONS = { shot: 1, crop: 1, zoom: 1, label: 1, nup: 1, compose: 1 };
|
|
45
52
|
|
|
53
|
+
// Actions that produce a transcript the agent must have read.
|
|
54
|
+
const TRANSCRIPT_ACTIONS = { terminal: 1 };
|
|
55
|
+
|
|
46
56
|
function fail(msg) {
|
|
47
57
|
process.stdout.write(JSON.stringify({ ok: false, error: msg }) + "\n");
|
|
48
58
|
process.exit(2);
|
|
@@ -59,6 +69,7 @@ function parseArgs(argv) {
|
|
|
59
69
|
else if (a === "--exit") out.exit = argv[++i];
|
|
60
70
|
else if (a === "--args") out.args = argv[++i];
|
|
61
71
|
else if (a === "--screenshot") out.screenshot = argv[++i];
|
|
72
|
+
else if (a === "--transcript") out.transcript = argv[++i];
|
|
62
73
|
else if (a === "--observation") out.observation = argv[++i];
|
|
63
74
|
else if (a === "--ts") out.ts = argv[++i];
|
|
64
75
|
else fail("unknown flag: " + a);
|
|
@@ -112,7 +123,7 @@ function main() {
|
|
|
112
123
|
fail("missing --attempt <id> (the attempt/run identity, e.g. \"1\", \"2\", \"repro-1\")");
|
|
113
124
|
}
|
|
114
125
|
if (args.step === undefined) fail("missing --step <n>");
|
|
115
|
-
if (!args.action) fail("missing --action <aria|shot|click|scroll|type|crop|zoom|label|nup|compose>");
|
|
126
|
+
if (!args.action) fail("missing --action <aria|shot|click|scroll|type|crop|zoom|label|nup|compose|terminal>");
|
|
116
127
|
if (args.exit === undefined) fail("missing --exit <code>");
|
|
117
128
|
|
|
118
129
|
const attempt = String(args.attempt).trim();
|
|
@@ -140,6 +151,14 @@ function main() {
|
|
|
140
151
|
fail("--action " + args.action + " produces a frame: --screenshot is required (log the frame you looked at)");
|
|
141
152
|
}
|
|
142
153
|
|
|
154
|
+
if (TRANSCRIPT_ACTIONS[args.action] && !args.transcript) {
|
|
155
|
+
fail("--action " + args.action + " produces a transcript: --transcript is required (log the transcript you read)");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (args.action === "terminal" && args.screenshot) {
|
|
159
|
+
fail("--action terminal never takes --screenshot: screenshots are not evidence for terminal work");
|
|
160
|
+
}
|
|
161
|
+
|
|
143
162
|
const logPath = resolve(args.log);
|
|
144
163
|
mkdirSync(dirname(logPath), { recursive: true });
|
|
145
164
|
|
|
@@ -156,6 +175,7 @@ function main() {
|
|
|
156
175
|
args: parsedArgs,
|
|
157
176
|
exit: exit,
|
|
158
177
|
screenshot: args.screenshot ? resolve(args.screenshot) : null,
|
|
178
|
+
transcript: args.transcript ? resolve(args.transcript) : null,
|
|
159
179
|
observation: args.observation || "",
|
|
160
180
|
};
|
|
161
181
|
if (args.ts) entry.ts = args.ts;
|