muse-crew 0.2.0 → 0.3.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 -1
- package/docs/guide.md +1 -1
- package/identities/cass.png +0 -0
- package/identities/hazel.png +0 -0
- package/identities/mara.png +0 -0
- package/identities/sage.png +0 -0
- package/identities/tate.png +0 -0
- package/identities/wren.png +0 -0
- package/lib/crew-release.sh +49 -2
- package/lib/worktree-lifecycle.sh +5 -2
- package/package.json +2 -2
- package/seed/workflows/AGENTS.md +4 -0
- package/workflows/bugfix.js +3 -0
- package/workflows/chore.js +3 -0
- package/workflows/standard.js +3 -0
package/API.md
CHANGED
|
@@ -33,7 +33,7 @@ Update an existing task's fields. Only `id` is required; all other fields are op
|
|
|
33
33
|
| `description` | string (≤ 3000) | no | |
|
|
34
34
|
| `state` | `todo` · `in_progress` · `parked` · `done` | no | |
|
|
35
35
|
| `priority` | `high` · `normal` · `low` | no | |
|
|
36
|
-
| `project` | slug | no | |
|
|
36
|
+
| `project` | slug | no | **Project-move guard:** changing this to a different project throws while the task has an active run (an agent session with status `running` that started within the last hour), because the live run keeps the old project's repo context and moving it mid-phase would work on the wrong repo. Wait for the run to finish, or recover/park the task first, then move it. |
|
|
37
37
|
| `workflow` | slug or null | no | |
|
|
38
38
|
| `deps` | string[] | no | |
|
|
39
39
|
|
|
@@ -147,6 +147,8 @@ Register a new project.
|
|
|
147
147
|
|
|
148
148
|
Update project fields. Only `id` is required; all others are optional patch fields. Same fields as `createproject`, except `simultaneity` minimum is 0 (for kill switch state).
|
|
149
149
|
|
|
150
|
+
**Context-change guard:** changing `repo_path`, `deploy_type`, or `deploy_slug` is blocked while any task in the project has an active run (an agent session with status `running` that started within the last hour): the call throws, because live runs keep the old project config and the change would split the project context mid-run. Wait for the runs to finish, or recover/park those tasks first, then retry.
|
|
151
|
+
|
|
150
152
|
### `deleteproject`
|
|
151
153
|
|
|
152
154
|
Remove a project registration.
|
package/docs/guide.md
CHANGED
|
@@ -213,7 +213,7 @@ Each phase has an assigned identity — a character with a defined personality:
|
|
|
213
213
|
| Map | **Mara** | Designer |
|
|
214
214
|
| Build | **Wren** | Quietest one, trusts the plan |
|
|
215
215
|
| Review | **Cass** | Fair but exacting — holds the spec as the contract |
|
|
216
|
-
| Integrate | **Wren** | Merges the work, pushes `main` to the repo |
|
|
216
|
+
| Integrate | **Wren** | Merges the work, pushes `main` to the repo (succeeds vacuously when the task branch is empty — runtime-state deliverable) |
|
|
217
217
|
| Publish | **Wren** | Ships the merged code to the publish target (skipped when none) |
|
|
218
218
|
| QA | **Hazel** | Code-blind, persistent, wears persona costumes |
|
|
219
219
|
| Reproduce | **Hazel** | Reproduces bugs before fixing |
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/lib/crew-release.sh
CHANGED
|
@@ -6,10 +6,13 @@
|
|
|
6
6
|
#
|
|
7
7
|
# Layout:
|
|
8
8
|
# ~/.jarvis/
|
|
9
|
-
# releases/<full-commit-hash>/ # immutable snapshot (workflows/ + lib/)
|
|
9
|
+
# releases/<full-commit-hash>/ # immutable snapshot (workflows/ + lib/ + seed/workflows/ docs)
|
|
10
10
|
# current -> releases/<hash> # active release
|
|
11
11
|
# workflows -> current/workflows # convenience (cron/dispatcher reads this)
|
|
12
12
|
# lib -> current/lib # convenience
|
|
13
|
+
# .orchestration/workflows/ # live workflow documents the dashboard
|
|
14
|
+
# # reads; re-synced from the activated
|
|
15
|
+
# # release on every deploy/rollback
|
|
13
16
|
#
|
|
14
17
|
# The Muse workflow runtime snapshots .js scripts at launch.
|
|
15
18
|
# /tmp/crew-lib-* pins lifecycle scripts per run.
|
|
@@ -91,6 +94,37 @@ _validate_workflows() {
|
|
|
91
94
|
echo "VALIDATED: workflow scripts parse"
|
|
92
95
|
}
|
|
93
96
|
|
|
97
|
+
# ── workflow doc sync ───────────────────────────────────────────────
|
|
98
|
+
# Decision (2026-09-09): the dashboard reads the workflow documents live
|
|
99
|
+
# from $CREW_HOME/.orchestration/workflows/, and `recovertask` validates
|
|
100
|
+
# target_phase against them — so those documents must always reflect the
|
|
101
|
+
# phases the dispatcher actually runs. Both the workflow scripts and the
|
|
102
|
+
# .md docs ship in the same repo commit, so we re-sync the docs from the
|
|
103
|
+
# activated release on every release activation (deploy AND rollback),
|
|
104
|
+
# automatically. A manual re-sync step is exactly what drifted here: the
|
|
105
|
+
# copy was seeded once by crew-init's `cp -n` and never refreshed, leaving
|
|
106
|
+
# the dashboard on pre-publish-architecture phases (no Integrate, no
|
|
107
|
+
# Publish) while the dispatcher ran the current phase lists.
|
|
108
|
+
_sync_workflow_docs() {
|
|
109
|
+
local home="${1:?usage: _sync_workflow_docs <jarvis-home>}"
|
|
110
|
+
local src="$home/current/seed/workflows"
|
|
111
|
+
local dest="$home/.orchestration/workflows"
|
|
112
|
+
if [ ! -d "$src" ]; then
|
|
113
|
+
echo "WORKFLOW-DOCS: no seed/workflows in activated release; skipping sync" >&2
|
|
114
|
+
return 0
|
|
115
|
+
fi
|
|
116
|
+
mkdir -p "$dest"
|
|
117
|
+
local f base copied=0
|
|
118
|
+
for f in "$src"/*.md; do
|
|
119
|
+
[ -f "$f" ] || continue
|
|
120
|
+
base="$(basename "$f")"
|
|
121
|
+
[ "$base" = "AGENTS.md" ] && continue # folder docs, not a workflow
|
|
122
|
+
cp -f "$f" "$dest/$base"
|
|
123
|
+
copied=$((copied + 1))
|
|
124
|
+
done
|
|
125
|
+
echo "WORKFLOW-DOCS: synced $copied workflow docs to $dest"
|
|
126
|
+
}
|
|
127
|
+
|
|
94
128
|
# ── deploy ────────────────────────────────────────────────────────────
|
|
95
129
|
# Build, validate, and atomically activate a release from repo HEAD.
|
|
96
130
|
# Single command — no cross-step lock needed.
|
|
@@ -124,11 +158,18 @@ cmd_deploy() {
|
|
|
124
158
|
mkdir -p "$staging_dir"
|
|
125
159
|
if [ -d .git ]; then
|
|
126
160
|
# Extract from committed tree (not working directory)
|
|
127
|
-
|
|
161
|
+
_seed=""
|
|
162
|
+
git cat-file -e "HEAD:seed/workflows" 2>/dev/null && _seed="seed/workflows" || true
|
|
163
|
+
# shellcheck disable=SC2086
|
|
164
|
+
git archive HEAD -- workflows lib $_seed | tar -xC "$staging_dir"
|
|
128
165
|
else
|
|
129
166
|
# Plain directory (npm install): copy directly
|
|
130
167
|
[ -d workflows ] && cp -r workflows "$staging_dir/"
|
|
131
168
|
[ -d lib ] && cp -r lib "$staging_dir/"
|
|
169
|
+
if [ -d seed/workflows ]; then
|
|
170
|
+
mkdir -p "$staging_dir/seed"
|
|
171
|
+
cp -r seed/workflows "$staging_dir/seed/"
|
|
172
|
+
fi
|
|
132
173
|
fi
|
|
133
174
|
# Gate: refuse to install a release whose workflow scripts don't parse.
|
|
134
175
|
if ! _validate_workflows "$staging_dir"; then
|
|
@@ -147,6 +188,9 @@ cmd_deploy() {
|
|
|
147
188
|
|
|
148
189
|
echo "ACTIVATED: $hash"
|
|
149
190
|
|
|
191
|
+
# Keep the dashboard's workflow documents in sync with the release.
|
|
192
|
+
_sync_workflow_docs "$CREW_HOME"
|
|
193
|
+
|
|
150
194
|
# Prune old releases — keep the 5 most recent
|
|
151
195
|
_prune_releases "$home" 5
|
|
152
196
|
}
|
|
@@ -172,6 +216,9 @@ cmd_rollback() {
|
|
|
172
216
|
mv -T "$tmp_link" "$CREW_HOME/current"
|
|
173
217
|
|
|
174
218
|
echo "ROLLED_BACK: $current_hash -> $prev"
|
|
219
|
+
|
|
220
|
+
# Keep the dashboard's workflow documents in sync with the release.
|
|
221
|
+
_sync_workflow_docs "$CREW_HOME"
|
|
175
222
|
}
|
|
176
223
|
|
|
177
224
|
# ── current ───────────────────────────────────────────────────────────
|
|
@@ -158,8 +158,11 @@ cmd_integrate() {
|
|
|
158
158
|
local ahead
|
|
159
159
|
ahead=$(git rev-list --count "main..task/$task_id")
|
|
160
160
|
if [ "$ahead" -eq 0 ]; then
|
|
161
|
-
|
|
162
|
-
|
|
161
|
+
# Approved empty diff: the deliverable was runtime state (cron,
|
|
162
|
+
# scheduler, dashboard config), not a repo change. Nothing to merge,
|
|
163
|
+
# nothing to serialize — the merge lock is intentionally not taken.
|
|
164
|
+
echo "MERGED_EMPTY: task/$task_id has no commits ahead of main — runtime-state deliverable, nothing to merge"
|
|
165
|
+
return 0
|
|
163
166
|
fi
|
|
164
167
|
|
|
165
168
|
# Acquire merge lock
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "muse-crew",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Opinionated orchestration for Muse — workflows, identities, and tooling for autonomous software development.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
12
|
"workflows/",
|
|
13
|
-
"identities
|
|
13
|
+
"identities/",
|
|
14
14
|
"personas/",
|
|
15
15
|
"lib/",
|
|
16
16
|
"seed/",
|
package/seed/workflows/AGENTS.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
# AGENTS.md
|
|
2
2
|
|
|
3
3
|
Human-readable workflow definitions — the canonical source. `crew-init` copies these to `$CREW_HOME/.orchestration/workflows/` during setup. Each file describes one workflow type: its phases, identity assignments, and progression rules.
|
|
4
|
+
|
|
5
|
+
## Sync policy
|
|
6
|
+
|
|
7
|
+
The workflow `.md` docs are synced from the activated release into `$CREW_HOME/.orchestration/workflows/` on every `crew-release.sh` release activation (deploy and rollback) — automatically, never as a manual step. The invariant is that the dashboard's workflow documents always reflect the phases the dispatcher actually runs: the dashboard reads those documents live from disk and `recovertask` validates target phases against them, so drift breaks recovery. Because the docs and the workflow scripts ship in the same repo commit, syncing at activation maintains the invariant by construction; the manual-step approach is what drifted (docs seeded once by `crew-init`'s `cp -n` went stale while the phases gained Integrate and Publish), so there is no manual re-sync step.
|
package/workflows/bugfix.js
CHANGED
|
@@ -209,6 +209,7 @@ while (i < STEPS.length) {
|
|
|
209
209
|
"cd " + REPO_PATH + "/.worktrees/" + taskId + "\n" +
|
|
210
210
|
"git add -A\n" +
|
|
211
211
|
"git commit -m \"fix: " + safeTitle + "\"\n\n" +
|
|
212
|
+
"If the task's deliverable is runtime state (a cron definition, scheduler change, or dashboard/config state created outside the repo) and the repository genuinely needs no change, do NOT fabricate a commit: leave the branch with no commits ahead of main and declare `repo_diff: none` in your summary, naming the runtime-state deliverable. Otherwise commit your changes normally.\n\n" +
|
|
212
213
|
(rejectionNotes ? "This is REWORK after rejection. Address these specific issues:\n" + rejectionNotes + "\n\n" : "") +
|
|
213
214
|
"Your final response MUST be valid JSON and nothing else: { \"summary\": \"what you built\", \"passed\": true }. No prose, no markdown, just the JSON object.";
|
|
214
215
|
|
|
@@ -222,6 +223,7 @@ while (i < STEPS.length) {
|
|
|
222
223
|
REPO_PATH + "/.worktrees/" + taskId + "/\n\n" +
|
|
223
224
|
"Check quality, correctness, and spec compliance.\n" +
|
|
224
225
|
"Check that public-affecting changes have matching public doc updates (API.md or the published API contract). If the docs are missing or inaccurate, reject with notes on what is stale.\n" +
|
|
226
|
+
"If the branch has no commits ahead of main (inspect shows an empty commit log), approve ONLY if the Build summary declares `repo_diff: none` with a plausible runtime-state deliverable (e.g. a cron created via the cron tool). Otherwise reject: 'no commits ahead of main and no repo_diff: none declaration — the builder likely forgot to commit'.\n" +
|
|
225
227
|
(PUBLISH_TYPE === "npm" ? "PACKAGE VERSION: this project publishes to the npm registry. Validate the builder's version choice: package.json must hold valid semver; if the version was bumped it must be greater than the registry version (npm view muse-crew version), the bump scope (patch/minor/major) must fit the change, and exactly one version field may change. If the version is invalid, already published, or mis-scoped, reject with notes.\n" : "") +
|
|
226
228
|
"If the work passes review, your final response MUST be valid JSON and nothing else: { \"passed\": true, \"summary\": \"approval notes\" }.\n" +
|
|
227
229
|
"If the work fails review, your final response MUST be valid JSON and nothing else: { \"passed\": false, \"summary\": \"rejection notes explaining what needs to change\" }.\n" +
|
|
@@ -232,6 +234,7 @@ while (i < STEPS.length) {
|
|
|
232
234
|
"Run: CREW_REPO=" + REPO_PATH + " " + LIFECYCLE + " integrate " + taskId + " \"merge: fix: " + safeTitle + "\"\n\n" +
|
|
233
235
|
"Read the output:\n" +
|
|
234
236
|
"- If it contains MERGED, integration succeeded. Report the merged commit hash.\n" +
|
|
237
|
+
"- If it contains MERGED_EMPTY, the branch had no commits ahead of main (a runtime-state deliverable, declared by Build as repo_diff: none). Integration succeeded vacuously: the merge lock was NOT taken and there is no new commit. Set passed to true with summary 'merged empty: no repo changes — deliverable was runtime state'. SKIP STEP 2 (push): there is no new commit to push.\n" +
|
|
235
238
|
"- If it contains LOCK_HELD, another task holds the merge lock (mid Integrate/Publish). Set passed to false.\n" +
|
|
236
239
|
"- If it contains CONFLICT, a merge conflict occurred. Set passed to false with details.\n" +
|
|
237
240
|
"- If it contains ERROR, something else failed. Set passed to false.\n\n" +
|
package/workflows/chore.js
CHANGED
|
@@ -191,6 +191,7 @@ while (i < STEPS.length) {
|
|
|
191
191
|
"cd " + REPO_PATH + "/.worktrees/" + taskId + "\n" +
|
|
192
192
|
"git add -A\n" +
|
|
193
193
|
"git commit -m \"chore: " + safeTitle + "\"\n\n" +
|
|
194
|
+
"If the task's deliverable is runtime state (a cron definition, scheduler change, or dashboard/config state created outside the repo) and the repository genuinely needs no change, do NOT fabricate a commit: leave the branch with no commits ahead of main and declare `repo_diff: none` in your summary, naming the runtime-state deliverable. Otherwise commit your changes normally.\n\n" +
|
|
194
195
|
(rejectionNotes ? "REWORK after rejection. Address:\n" + rejectionNotes + "\n\n" : "") +
|
|
195
196
|
"Your final response MUST be valid JSON and nothing else: { \"summary\": \"what you built\", \"passed\": true }. No prose, no markdown, just the JSON object.";
|
|
196
197
|
|
|
@@ -204,6 +205,7 @@ while (i < STEPS.length) {
|
|
|
204
205
|
REPO_PATH + "/.worktrees/" + taskId + "/\n\n" +
|
|
205
206
|
"Check quality, correctness, spec compliance.\n" +
|
|
206
207
|
"Check that public-affecting changes have matching public doc updates (API.md or the published API contract). If the docs are missing or inaccurate, reject with notes on what is stale.\n" +
|
|
208
|
+
"If the branch has no commits ahead of main (inspect shows an empty commit log), approve ONLY if the Build summary declares `repo_diff: none` with a plausible runtime-state deliverable (e.g. a cron created via the cron tool). Otherwise reject: 'no commits ahead of main and no repo_diff: none declaration — the builder likely forgot to commit'.\n" +
|
|
207
209
|
(PUBLISH_TYPE === "npm" ? "PACKAGE VERSION: this project publishes to the npm registry. Validate the builder's version choice: package.json must hold valid semver; if the version was bumped it must be greater than the registry version (npm view muse-crew version), the bump scope (patch/minor/major) must fit the change, and exactly one version field may change. If the version is invalid, already published, or mis-scoped, reject with notes.\n" : "") +
|
|
208
210
|
"If it passes, your final response MUST be valid JSON and nothing else: { \"passed\": true, \"summary\": \"approval notes\" }.\n" +
|
|
209
211
|
"If it fails, your final response MUST be valid JSON and nothing else: { \"passed\": false, \"summary\": \"rejection notes\" }.\n" +
|
|
@@ -214,6 +216,7 @@ while (i < STEPS.length) {
|
|
|
214
216
|
"Run: CREW_REPO=" + REPO_PATH + " " + LIFECYCLE + " integrate " + taskId + " \"merge: chore: " + safeTitle + "\"\n\n" +
|
|
215
217
|
"Read the output:\n" +
|
|
216
218
|
"- If it contains MERGED, integration succeeded. Report the merged commit hash.\n" +
|
|
219
|
+
"- If it contains MERGED_EMPTY, the branch had no commits ahead of main (a runtime-state deliverable, declared by Build as repo_diff: none). Integration succeeded vacuously: the merge lock was NOT taken and there is no new commit. Set passed to true with summary 'merged empty: no repo changes — deliverable was runtime state'. SKIP STEP 2 (push): there is no new commit to push.\n" +
|
|
217
220
|
"- If it contains LOCK_HELD, another task holds the merge lock (mid Integrate/Publish). Set passed to false.\n" +
|
|
218
221
|
"- If it contains CONFLICT, a merge conflict occurred. Set passed to false with details.\n" +
|
|
219
222
|
"- If it contains ERROR, something else failed. Set passed to false.\n\n" +
|
package/workflows/standard.js
CHANGED
|
@@ -207,6 +207,7 @@ while (i < STEPS.length) {
|
|
|
207
207
|
"cd " + REPO_PATH + "/.worktrees/" + taskId + "\n" +
|
|
208
208
|
"git add -A\n" +
|
|
209
209
|
"git commit -m \"" + safeTitle + "\"\n\n" +
|
|
210
|
+
"If the task's deliverable is runtime state (a cron definition, scheduler change, or dashboard/config state created outside the repo) and the repository genuinely needs no change, do NOT fabricate a commit: leave the branch with no commits ahead of main and declare `repo_diff: none` in your summary, naming the runtime-state deliverable. Otherwise commit your changes normally.\n\n" +
|
|
210
211
|
(rejectionNotes ? "This is REWORK after rejection. Address these specific issues:\n" + rejectionNotes + "\n\n" : "") +
|
|
211
212
|
"Your final response MUST be valid JSON and nothing else: { \"summary\": \"what you built\", \"passed\": true }. No prose, no markdown, just the JSON object.";
|
|
212
213
|
|
|
@@ -220,6 +221,7 @@ while (i < STEPS.length) {
|
|
|
220
221
|
REPO_PATH + "/.worktrees/" + taskId + "/\n\n" +
|
|
221
222
|
"Check quality, correctness, and spec compliance.\n" +
|
|
222
223
|
"Check that public-affecting changes have matching public doc updates (API.md or the published API contract). If the docs are missing or inaccurate, reject with notes on what is stale.\n" +
|
|
224
|
+
"If the branch has no commits ahead of main (inspect shows an empty commit log), approve ONLY if the Build summary declares `repo_diff: none` with a plausible runtime-state deliverable (e.g. a cron created via the cron tool). Otherwise reject: 'no commits ahead of main and no repo_diff: none declaration — the builder likely forgot to commit'.\n" +
|
|
223
225
|
(PUBLISH_TYPE === "npm" ? "PACKAGE VERSION: this project publishes to the npm registry. Validate the builder's version choice: package.json must hold valid semver; if the version was bumped it must be greater than the registry version (npm view muse-crew version), the bump scope (patch/minor/major) must fit the change, and exactly one version field may change. If the version is invalid, already published, or mis-scoped, reject with notes.\n" : "") +
|
|
224
226
|
"If the work passes review, your final response MUST be valid JSON and nothing else: { \"passed\": true, \"summary\": \"approval notes\" }.\n" +
|
|
225
227
|
"If the work fails review, your final response MUST be valid JSON and nothing else: { \"passed\": false, \"summary\": \"rejection notes explaining what needs to change\" }.\n" +
|
|
@@ -230,6 +232,7 @@ while (i < STEPS.length) {
|
|
|
230
232
|
"Run: CREW_REPO=" + REPO_PATH + " " + LIFECYCLE + " integrate " + taskId + " \"merge: " + safeTitle + "\"\n\n" +
|
|
231
233
|
"Read the output:\n" +
|
|
232
234
|
"- If it contains MERGED, integration succeeded. Report the merged commit hash.\n" +
|
|
235
|
+
"- If it contains MERGED_EMPTY, the branch had no commits ahead of main (a runtime-state deliverable, declared by Build as repo_diff: none). Integration succeeded vacuously: the merge lock was NOT taken and there is no new commit. Set passed to true with summary 'merged empty: no repo changes — deliverable was runtime state'. SKIP STEP 2 (push): there is no new commit to push.\n" +
|
|
233
236
|
"- If it contains LOCK_HELD, another task holds the merge lock (mid Integrate/Publish). Set passed to false with summary 'merge lock held'.\n" +
|
|
234
237
|
"- If it contains CONFLICT, a merge conflict occurred. Set passed to false with the conflict details.\n" +
|
|
235
238
|
"- If it contains ERROR, something else failed. Set passed to false with the error.\n\n" +
|