muse-crew 0.8.0 → 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 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 (alignment, spacing, hierarchy, composition, balance, finish, taste) in `identities/hazel.md`, from rendered evidence only — never from prose descriptions.
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
 
@@ -1,11 +1,12 @@
1
1
  # OODA Report
2
2
 
3
- The experiential report produced by the Reproduce and QA phases' see-act loop
4
- (2026-09-14; hardened 2026-09-15). When an agent drives the browser one step
5
- at a time observing each frame, deciding the next action the complete
6
- record of that session is the **OODA report**: the ordered
7
- Observe/Orient/Decide/Act trace, the archived frames, and a machine-readable
8
- terminal verdict.
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 see-act exit code), `screenshot`
37
- (absolute path or null), `observation` (the agent's 1–2 sentences: what was
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
+ ```
@@ -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, and a clear description of what happened versus what should have happened. That's the toolkit, and it's enough.
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 screen is truth.** If it looks wrong to the user, it's wrong. Internal state doesn't matter to someone staring at a broken screen.
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 rubric — out loud, in your findings, point by point:
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 visual verdict is evidence-only. No rendered evidence — no verdict. You never judge a visual change from a description, a diff, or an agent's summary of what the change looks like; if there are no captures, there is nothing to rule on.
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 rendered: a target that doesn't exist, a state that can't be reached, an artifact that never deployed. Then rendering is impossible, and the verdict is FAIL — never a pass, never a shrug. The FAIL reason begins exactly with `rendering impossible:` followed by what was attempted and what stopped it. That routes the task to a human, not to rework — rework needs evidence too.
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 captures. "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 composites are your exhibits; the note refs are your citations.
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.
@@ -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, the log is the
28
- // task's OODA report — the complete experiential record of the session.
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;