muse-crew 0.7.20 → 0.8.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/docs/guide.md +179 -7
- package/lib/AGENTS.md +2 -1
- package/lib/build-registry.js +3 -2
- package/lib/publish-npm.sh +33 -0
- package/lib/test-publish-preflight.sh +210 -0
- package/lib/test-worktree-backend.sh +51 -1
- package/lib/update-watch.js +633 -0
- package/lib/worktree-lifecycle.sh +68 -10
- package/package.json +1 -1
- package/seed/AGENTS.md +1 -0
- package/seed/cron-body-update-watch.md +13 -0
- package/seed/crons.json +14 -1
- package/seed/workflows/upgrade.md +21 -0
- package/workflows/AGENTS.md +1 -1
- package/workflows/bugfix.js +239 -14
- package/workflows/chore.js +44 -12
- package/workflows/crew-dispatch.js +43 -6
- package/workflows/crew-init.js +173 -10
- package/workflows/standard.js +44 -12
- package/workflows/upgrade.js +794 -0
|
@@ -6,13 +6,18 @@
|
|
|
6
6
|
# worktree strategy lives — workflows and agents never run raw git worktree
|
|
7
7
|
# commands; they call these lifecycle commands instead.
|
|
8
8
|
#
|
|
9
|
-
# The crew's registry ($REPO/.worktrees/.registry/<task_id>) is the
|
|
10
|
-
# of truth for task→branch/path.
|
|
11
|
-
#
|
|
9
|
+
# The crew's registry ($REPO/.worktrees/.registry/<task_id>) is the first
|
|
10
|
+
# source of truth for task→branch/path. When the registry is empty (a
|
|
11
|
+
# Build agent created the branch with raw git from an abbreviated id,
|
|
12
|
+
# bypassing prepare — task 4e1a1bba, 2026-09-17), resolve_branch() falls
|
|
13
|
+
# back to enumerating refs: the canonical task/<full-id> ref wins, then a
|
|
14
|
+
# task/<id-prefix> ref is tolerated (matched by strict prefix, never
|
|
15
|
+
# created). Canonical stays task/<full-id>; ambiguous prefixes fail closed.
|
|
12
16
|
#
|
|
13
17
|
# Commands:
|
|
14
18
|
# validate <task_id> — check task ID is safe for branches/paths
|
|
15
19
|
# prepare <task_id> — create worktree + branch, register ownership
|
|
20
|
+
# resolve-branch <task_id> — print the resolved task branch name
|
|
16
21
|
# inspect <task_id> — diff against main for review
|
|
17
22
|
# integrate <task_id> <commit_msg> — merge lock → merge into main → report commit
|
|
18
23
|
# verify-merge <task_id> — confirm the task branch tip is an ancestor of main (post-step merge check)
|
|
@@ -63,15 +68,47 @@ MERGE_RECORDS_DIR="$CREW_HOME/.merge-records"
|
|
|
63
68
|
|
|
64
69
|
# --- helpers ---
|
|
65
70
|
|
|
66
|
-
# Task branch name
|
|
67
|
-
#
|
|
71
|
+
# Task branch name. Resolution order (task 4e1a1bba, 2026-09-17):
|
|
72
|
+
# 1. the registry — prepare writes the branch it created;
|
|
73
|
+
# 2. the canonical task/<full-id> ref, when it exists;
|
|
74
|
+
# 3. a task/<id-prefix> ref, matched by strict literal prefix over
|
|
75
|
+
# refs/heads/task/ — tolerated, never created. Build and Integrate
|
|
76
|
+
# therefore agree on the branch name even when Build bypassed
|
|
77
|
+
# prepare with an abbreviated id.
|
|
78
|
+
# When nothing matches, the canonical name is returned anyway so callers
|
|
79
|
+
# keep their "branch X does not exist" failure (and verify-merge's merge-
|
|
80
|
+
# record fallback) instead of failing on an empty name. Ambiguous prefixes
|
|
81
|
+
# fail closed (exit 1).
|
|
68
82
|
resolve_branch() {
|
|
69
83
|
local task_id="$1" b
|
|
70
84
|
if [ -f "$REGISTRY_DIR/$task_id" ]; then
|
|
71
85
|
b=$(grep '^branch=' "$REGISTRY_DIR/$task_id" | cut -d= -f2-)
|
|
72
86
|
if [ -n "$b" ]; then printf '%s' "$b"; return 0; fi
|
|
73
87
|
fi
|
|
74
|
-
|
|
88
|
+
local canonical="task/$task_id"
|
|
89
|
+
if git -C "$REPO" rev-parse --verify "$canonical" >/dev/null 2>&1; then
|
|
90
|
+
printf '%s' "$canonical"; return 0
|
|
91
|
+
fi
|
|
92
|
+
local matches=() ref stripped
|
|
93
|
+
while IFS= read -r ref; do
|
|
94
|
+
[ -n "$ref" ] || continue
|
|
95
|
+
[ "$ref" = "$canonical" ] && continue
|
|
96
|
+
# Strict literal prefix: ref must be a proper prefix of the canonical
|
|
97
|
+
# name. The quoted expansion keeps the pattern literal (task ids are
|
|
98
|
+
# [a-zA-Z0-9_-], but literal is literal).
|
|
99
|
+
stripped="${canonical#"$ref"}"
|
|
100
|
+
if [ "$stripped" != "$canonical" ]; then
|
|
101
|
+
matches+=("$ref")
|
|
102
|
+
fi
|
|
103
|
+
done < <(git -C "$REPO" for-each-ref --format='%(refname:short)' 'refs/heads/task/')
|
|
104
|
+
if [ "${#matches[@]}" -eq 1 ]; then
|
|
105
|
+
printf '%s' "${matches[0]}"; return 0
|
|
106
|
+
fi
|
|
107
|
+
if [ "${#matches[@]}" -gt 1 ]; then
|
|
108
|
+
echo "ERROR: task id '$task_id' matches multiple task branches: ${matches[*]} — ambiguous, refusing to guess" >&2
|
|
109
|
+
return 1
|
|
110
|
+
fi
|
|
111
|
+
printf '%s' "$canonical"
|
|
75
112
|
}
|
|
76
113
|
|
|
77
114
|
# Worktree path: $REPO/.worktrees/<id>. The registry wins when the task
|
|
@@ -168,6 +205,17 @@ cmd_prepare() {
|
|
|
168
205
|
echo "BASE: $base_commit"
|
|
169
206
|
}
|
|
170
207
|
|
|
208
|
+
# Agent-side one-liner: $(worktree-lifecycle.sh resolve-branch <id>) prints
|
|
209
|
+
# the branch Build and Integrate must agree on. Mechanical — no agent call
|
|
210
|
+
# per phase needed (task 4e1a1bba). Exits 1 on an ambiguous prefix.
|
|
211
|
+
cmd_resolve_branch() {
|
|
212
|
+
local task_id="$1"
|
|
213
|
+
validate_task_id "$task_id"
|
|
214
|
+
local branch
|
|
215
|
+
branch=$(resolve_branch "$task_id")
|
|
216
|
+
echo "$branch"
|
|
217
|
+
}
|
|
218
|
+
|
|
171
219
|
cmd_inspect() {
|
|
172
220
|
local task_id="$1"
|
|
173
221
|
validate_task_id "$task_id"
|
|
@@ -373,7 +421,12 @@ cmd_post_deploy() {
|
|
|
373
421
|
final_commit=$(git rev-parse HEAD)
|
|
374
422
|
|
|
375
423
|
# Cleanup worktree + branch — report honestly: a failed removal must
|
|
376
|
-
# never print success (a lying "removed" hid real leftovers).
|
|
424
|
+
# never print success (a lying "removed" hid real leftovers). The branch
|
|
425
|
+
# is resolved, never reconstructed: a Build agent may have created it
|
|
426
|
+
# from an abbreviated id (task 4e1a1bba) and deleting "task/<full-id>"
|
|
427
|
+
# would leave the real branch behind.
|
|
428
|
+
local branch
|
|
429
|
+
branch=$(resolve_branch "$task_id")
|
|
377
430
|
if [ -d "$WORKTREE_DIR/$task_id" ]; then
|
|
378
431
|
if git worktree remove "$WORKTREE_DIR/$task_id" 2>/dev/null; then
|
|
379
432
|
echo "WORKTREE: removed"
|
|
@@ -382,7 +435,7 @@ cmd_post_deploy() {
|
|
|
382
435
|
fi
|
|
383
436
|
fi
|
|
384
437
|
git worktree prune 2>/dev/null
|
|
385
|
-
git branch -d "
|
|
438
|
+
git branch -d "$branch" 2>/dev/null && echo "BRANCH: deleted" || true
|
|
386
439
|
unregister "$task_id"
|
|
387
440
|
|
|
388
441
|
# Release merge lock
|
|
@@ -480,7 +533,11 @@ cmd_cleanup() {
|
|
|
480
533
|
echo "WORKTREE: not found (already clean)"
|
|
481
534
|
fi
|
|
482
535
|
git worktree prune 2>/dev/null
|
|
483
|
-
|
|
536
|
+
# Resolve, never reconstruct: the branch may live under an abbreviated
|
|
537
|
+
# id (task 4e1a1bba).
|
|
538
|
+
local branch
|
|
539
|
+
branch=$(resolve_branch "$task_id")
|
|
540
|
+
git branch -D "$branch" 2>/dev/null && echo "BRANCH: deleted" || true
|
|
484
541
|
unregister "$task_id"
|
|
485
542
|
echo "CLEANUP: done"
|
|
486
543
|
}
|
|
@@ -569,6 +626,7 @@ shift || true
|
|
|
569
626
|
case "$cmd" in
|
|
570
627
|
validate) cmd_validate "${1:?task_id required}" ;;
|
|
571
628
|
prepare) cmd_prepare "${1:?task_id required}" ;;
|
|
629
|
+
resolve-branch) cmd_resolve_branch "${1:?task_id required}" ;;
|
|
572
630
|
inspect) cmd_inspect "${1:?task_id required}" ;;
|
|
573
631
|
integrate) cmd_integrate "${1:?task_id required}" "${2:-}" ;;
|
|
574
632
|
verify-merge) cmd_verify_merge "${1:?task_id required}" ;;
|
|
@@ -581,7 +639,7 @@ case "$cmd" in
|
|
|
581
639
|
merge-record) cmd_merge_record "${1:?task_id required}" ;;
|
|
582
640
|
*)
|
|
583
641
|
echo "Usage: worktree-lifecycle.sh <command> [args]"
|
|
584
|
-
echo "Commands: validate, prepare, inspect, integrate, verify-merge, post-deploy, terminal-cleanup, cleanup, status, refresh-lock, lock-status, merge-record"
|
|
642
|
+
echo "Commands: validate, prepare, resolve-branch, inspect, integrate, verify-merge, post-deploy, terminal-cleanup, cleanup, status, refresh-lock, lock-status, merge-record"
|
|
585
643
|
exit 1
|
|
586
644
|
;;
|
|
587
645
|
esac
|
package/package.json
CHANGED
package/seed/AGENTS.md
CHANGED
|
@@ -4,6 +4,7 @@ Init source data. Everything `crew-init.js` reads when setting up a new crew ins
|
|
|
4
4
|
|
|
5
5
|
- `crons.json` — the declarative cron manifest; crew-init creates/updates each entry idempotently; `enabled` is a creation-time default only
|
|
6
6
|
- `cron-body-template.md` — template for creating the dispatch cron job
|
|
7
|
+
- `cron-body-update-watch.md` — template for the daily `crew-update-watch` cron job: runs `lib/update-watch.js --crew-home {crewHome}` verbatim and reports stdout
|
|
7
8
|
- `posture.md` — PM posture that shapes the user's assistant for crew interaction
|
|
8
9
|
- `feedback/` — feedback feature placeholder
|
|
9
10
|
- `workflows/` — human-readable workflow definitions, copied to `$CREW_HOME/.orchestration/workflows/` during init
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
## Muse Crew update watch
|
|
2
|
+
|
|
3
|
+
You are the update watcher. Run the deterministic check and report its output.
|
|
4
|
+
|
|
5
|
+
### Steps
|
|
6
|
+
|
|
7
|
+
1. Run verbatim:
|
|
8
|
+
node {crewHome}/current/lib/update-watch.js --crew-home {crewHome}
|
|
9
|
+
The script exits 0 on every path. Check failures are logged to
|
|
10
|
+
{crewHome}/update-watch.log and reported on stdout — never thrown.
|
|
11
|
+
2. Report the script's stdout verbatim. Do not summarize, do not judge, do
|
|
12
|
+
not file tasks yourself — the watcher files through the Crew API when
|
|
13
|
+
policy and idempotency allow.
|
package/seed/crons.json
CHANGED
|
@@ -5,13 +5,26 @@
|
|
|
5
5
|
"enabled": true,
|
|
6
6
|
"id": "crew-poll",
|
|
7
7
|
"mode": "task",
|
|
8
|
-
"owner": "
|
|
8
|
+
"owner": "cli:{instanceId}",
|
|
9
9
|
"schedule": {
|
|
10
10
|
"every": "15m",
|
|
11
11
|
"kind": "interval"
|
|
12
12
|
},
|
|
13
13
|
"timeout_secs": 5400,
|
|
14
14
|
"title": "Muse Crew polling loop"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"body_template": "cron-body-update-watch.md",
|
|
18
|
+
"enabled": true,
|
|
19
|
+
"id": "crew-update-watch",
|
|
20
|
+
"mode": "task",
|
|
21
|
+
"owner": "cli:{instanceId}",
|
|
22
|
+
"schedule": {
|
|
23
|
+
"every": "24h",
|
|
24
|
+
"kind": "interval"
|
|
25
|
+
},
|
|
26
|
+
"timeout_secs": 600,
|
|
27
|
+
"title": "Muse Crew automatic update watcher"
|
|
15
28
|
}
|
|
16
29
|
],
|
|
17
30
|
"version": 1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Upgrade
|
|
2
|
+
|
|
3
|
+
Upgrade the crew itself through its own dispatch loop. The task description carries a `source:` line naming the upgrade source — `repo` (default, the task project's repo HEAD) or `npm@<x.y.z>` (the published npm package). The stable `crew-release.sh` deploys, the workflow verifies mechanically, and handover to the new release is automatic on the next tick.
|
|
4
|
+
|
|
5
|
+
## Steps
|
|
6
|
+
|
|
7
|
+
### Triage
|
|
8
|
+
**Identity:** Sage
|
|
9
|
+
Validate the `source:` line (exactly `repo`, or `npm@<x.y.z>` — anything else parks fail-closed), verify the repo checkout and resolve `git rev-parse HEAD` for repo source, read the live release via `crew-release.sh current`, and compute the target. If the crew is already on the target, record the idempotent no-op and skip Deploy.
|
|
10
|
+
|
|
11
|
+
### Deploy
|
|
12
|
+
**Identity:** Wren
|
|
13
|
+
Run the stable `$CREW_HOME/crew-release.sh deploy` — against the repo HEAD for repo source, or against a staging dir under `$CREW_HOME/.upgrade-staging/<version>` (public npm install only) for npm source. Single command, no merge lock. Any non-zero exit parks fail-closed with the exact output; no retry, no auto-rollback — `crew-release.sh rollback` is the human recovery path.
|
|
14
|
+
|
|
15
|
+
### Verify
|
|
16
|
+
**Identity:** Wren
|
|
17
|
+
Mechanical, no LLM judgment: (1) `crew-release.sh current` equals the target; (2) `<crewHome>/workflows/registry.json` parses as JSON — and for repo source carries the `upgrade` key (npm releases predating the upgrade workflow are still valid upgrades); (3) `crew-api get-state` works via the pinned old-release CLI. Handover is automatic: the next poll tick launches the dispatcher through the `current` symlink, i.e. the new release. In-flight runs finish on the old release via per-task lib pinning.
|
|
18
|
+
|
|
19
|
+
## Safety
|
|
20
|
+
|
|
21
|
+
Never mutate the crew repo — repo source always means the repo's current HEAD. Never publish to npm — the npm source only installs the published package. Never touch the scheduler. Fail closed everywhere.
|
package/workflows/AGENTS.md
CHANGED
|
@@ -5,7 +5,7 @@ Executable Muse workflow scripts (JavaScript). These are what the workflow runti
|
|
|
5
5
|
- `crew-dispatch.js` — reads the board, recommends eligible tasks, returns structured launch records (the launched workflow self-claims; the dispatcher never writes claims). Skips tasks on quiesced projects and on projects with no repo_path configured.
|
|
6
6
|
|
|
7
7
|
All four workflow scripts share byte-identical transport helpers (`workRetryKey`, `buildTransportRetryTrailer`, `describeWorkAgentFailure`, `workerMissingArtifactTools` — pinned by `tests/closeout.test.js`). The transport-retry loop treats a worker report naming the missing artifact tool namespace (bug 3472bf36, a per-launch platform flake) as a retryable attempt with a fresh launch rather than accepting a useless report.
|
|
8
|
-
- `crew-init.js` — sets up a new crew instance: Gate 0 validates crewHome is workspace-contained and a valid git repo, and (in dashboard mode) validates dashboardRepoPath is workspace-contained and an existing git repo (never auto-created); bootstraps the release system; scaffolds orchestration folders; registers the first project with repo_path set to the validated dashboard repo — skipped in CLI-only mode (no dashboardSlug/dashboardRepoPath), where projects are created later via crew-api.js; creates/converges cron jobs from the declarative manifest (
|
|
8
|
+
- `crew-init.js` — sets up a new crew instance: Gate 0 validates crewHome is workspace-contained and a valid git repo, and (in dashboard mode) validates dashboardRepoPath is workspace-contained and an existing git repo (never auto-created); bootstraps the release system; scaffolds orchestration folders; registers the first project with repo_path set to the validated dashboard repo — skipped in CLI-only mode (no dashboardSlug/dashboardRepoPath), where projects are created later via crew-api.js; creates/converges cron jobs from the declarative manifest (scheduler identity is dashboard-independent: first init uses the crew-home basename, re-runs keep the instanceId recorded in `.cron-registry.json`; owner `cli:<instanceId>` — deleting a dashboard artifact never stops the crew). Idempotent. The crew owns its state via the Crew API (lib/crew-api.js); the dashboard is an optional client and is never a dependency.
|
|
9
9
|
- `standard.js` — default task workflow: Triage → Capture → Map → Build → Review → Integrate → Publish → QA
|
|
10
10
|
- `bugfix.js` — adds Capture after Triage, then Reproduce
|
|
11
11
|
- `chore.js` — adds Capture after Triage, drops QA (low-risk)
|