@mmerterden/multi-agent-pipeline 20.36.0 → 20.37.1

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.
Files changed (222) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/docs/facts.json +2 -2
  3. package/docs/features.md +1 -1
  4. package/docs/recovery-guide.md +1 -1
  5. package/install/catalog-history.json +1 -1
  6. package/manifest.json +225 -148
  7. package/package.json +1 -1
  8. package/pipeline/commands/multi-agent/SKILL.md +33 -154
  9. package/pipeline/commands/multi-agent/analysis/SKILL.md +14 -68
  10. package/pipeline/commands/multi-agent/analysis-jira/SKILL.md +5 -8
  11. package/pipeline/commands/multi-agent/autopilot-on/SKILL.md +38 -109
  12. package/pipeline/commands/multi-agent/channels/SKILL.md +38 -167
  13. package/pipeline/commands/multi-agent/complaint-analysis/SKILL.md +37 -144
  14. package/pipeline/commands/multi-agent/design-check/SKILL.md +27 -131
  15. package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +22 -48
  16. package/pipeline/commands/multi-agent/ios-coding-standard/SKILL.md +37 -180
  17. package/pipeline/commands/multi-agent/jira/SKILL.md +16 -46
  18. package/pipeline/commands/multi-agent/manual-test/SKILL.md +12 -7
  19. package/pipeline/commands/multi-agent/refactor/SKILL.md +23 -184
  20. package/pipeline/commands/multi-agent/resume/SKILL.md +30 -136
  21. package/pipeline/commands/multi-agent/review/SKILL.md +24 -199
  22. package/pipeline/commands/multi-agent/setup/SKILL.md +25 -181
  23. package/pipeline/commands/multi-agent/stack/SKILL.md +9 -53
  24. package/pipeline/commands/multi-agent/status/SKILL.md +13 -2
  25. package/pipeline/commands/multi-agent/store-ready/SKILL.md +27 -226
  26. package/pipeline/commands/multi-agent/sync/SKILL.md +35 -164
  27. package/pipeline/commands/multi-agent/test-gen/SKILL.md +2 -4
  28. package/pipeline/commands/multi-agent/update/SKILL.md +18 -60
  29. package/pipeline/contract/CHANGELOG.md +9 -0
  30. package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
  31. package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
  32. package/pipeline/contract/fixtures/error-unsigned.json +1 -1
  33. package/pipeline/contract/fixtures/runs-awaiting-question.json +1 -1
  34. package/pipeline/contract/fixtures/runs-empty.json +1 -1
  35. package/pipeline/contract/fixtures/runs-failed.json +1 -1
  36. package/pipeline/contract/fixtures/runs-pr-opened-redacted.json +1 -1
  37. package/pipeline/contract/fixtures/runs-pr-opened.json +1 -1
  38. package/pipeline/contract/fixtures/runs-running.json +1 -1
  39. package/pipeline/contract/manifest.json +5 -5
  40. package/pipeline/contract/types/index.d.ts +3 -2
  41. package/pipeline/multi-agent-refs/audit-guide.md +20 -0
  42. package/pipeline/multi-agent-refs/autopilot-on/details.md +148 -0
  43. package/pipeline/multi-agent-refs/channels/generator.md +14 -0
  44. package/pipeline/multi-agent-refs/channels/preview.md +13 -0
  45. package/pipeline/multi-agent-refs/channels/reference.md +46 -0
  46. package/pipeline/multi-agent-refs/complaint-analysis/phases.md +159 -0
  47. package/pipeline/multi-agent-refs/design-check/export.md +1 -0
  48. package/pipeline/multi-agent-refs/design-check/init-inventory.md +1 -1
  49. package/pipeline/multi-agent-refs/design-check/scope-and-ledger.md +64 -0
  50. package/pipeline/multi-agent-refs/features/analysis-command-notes.md +53 -0
  51. package/pipeline/multi-agent-refs/features/commit-multi-repo.md +63 -0
  52. package/pipeline/multi-agent-refs/features/dev-multi-repo.md +51 -0
  53. package/pipeline/multi-agent-refs/features/test-gap.md +40 -0
  54. package/pipeline/multi-agent-refs/features/worktree-finalize.md +1 -1
  55. package/pipeline/multi-agent-refs/garbage-collect/phases.md +60 -0
  56. package/pipeline/multi-agent-refs/ios-coding-standard/plan.md +71 -0
  57. package/pipeline/multi-agent-refs/ios-coding-standard/resolve-and-scan.md +135 -0
  58. package/pipeline/multi-agent-refs/jira/picker-detail.md +71 -0
  59. package/pipeline/multi-agent-refs/orchestrator/copilot-pipeline.md +88 -0
  60. package/pipeline/multi-agent-refs/orchestrator/dispatcher.md +96 -1
  61. package/pipeline/multi-agent-refs/orchestrator/phase-3-user-test.md +12 -9
  62. package/pipeline/multi-agent-refs/phases/phase-1-plan.md +4 -4
  63. package/pipeline/multi-agent-refs/phases/phase-2-dev.md +8 -47
  64. package/pipeline/multi-agent-refs/phases/phase-3-review.md +21 -65
  65. package/pipeline/multi-agent-refs/phases/phase-4-commit.md +9 -59
  66. package/pipeline/multi-agent-refs/phases/phase-5-report.md +0 -31
  67. package/pipeline/multi-agent-refs/refactor/sweep.md +190 -0
  68. package/pipeline/multi-agent-refs/resume/details.md +115 -2
  69. package/pipeline/multi-agent-refs/review/inputs.md +33 -1
  70. package/pipeline/multi-agent-refs/review/module-guides.md +9 -1
  71. package/pipeline/multi-agent-refs/review/panel.md +75 -0
  72. package/pipeline/multi-agent-refs/review/valven.md +5 -1
  73. package/pipeline/multi-agent-refs/rules.md +1 -1
  74. package/pipeline/multi-agent-refs/setup/hooks-and-plugins.md +4 -0
  75. package/pipeline/multi-agent-refs/setup/token-save-flow.md +13 -0
  76. package/pipeline/multi-agent-refs/stack/background.md +72 -0
  77. package/pipeline/multi-agent-refs/store-ready/gates.md +139 -0
  78. package/pipeline/multi-agent-refs/store-ready/report.md +40 -2
  79. package/pipeline/multi-agent-refs/sync/office.md +10 -0
  80. package/pipeline/multi-agent-refs/sync/overview.md +61 -0
  81. package/pipeline/multi-agent-refs/sync/repo.md +91 -0
  82. package/pipeline/multi-agent-refs/update/details.md +83 -0
  83. package/pipeline/office/index.html +6 -3
  84. package/pipeline/office/toolkit.json +1 -1
  85. package/pipeline/rules/figma-pipeline.md +6 -3
  86. package/pipeline/schemas/prefs.schema.json +1 -1
  87. package/pipeline/schemas/runs-index.schema.json +2 -1
  88. package/pipeline/scripts/_memory-policy.mjs +27 -5
  89. package/pipeline/scripts/checkpoint-fold.sh +59 -0
  90. package/pipeline/scripts/contract-server.mjs +1 -1
  91. package/pipeline/scripts/gate-ledger.mjs +1 -1
  92. package/pipeline/scripts/office-control.mjs +41 -15
  93. package/pipeline/scripts/offload-ref.sh +1 -1
  94. package/pipeline/scripts/rework-rulings.mjs +5 -3
  95. package/pipeline/scripts/runs-index.mjs +34 -7
  96. package/pipeline/scripts/scope-check-gate.mjs +15 -2
  97. package/pipeline/scripts/status-overview.mjs +401 -0
  98. package/pipeline/scripts/user-test-handoff.sh +203 -0
  99. package/pipeline/scripts/validate-planning.mjs +5 -3
  100. package/pipeline/scripts/validate-triage.mjs +2 -1
  101. package/pipeline/scripts/write-state.mjs +13 -4
  102. package/pipeline/skills/.skill-manifest.json +64 -64
  103. package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +7 -70
  104. package/pipeline/skills/shared/core/apple-archive-compliance/references/examples.md +67 -0
  105. package/pipeline/skills/shared/core/apple-archive-compliance/references/version-history.md +8 -0
  106. package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +10 -72
  107. package/pipeline/skills/shared/core/google-play-compliance/references/examples.md +69 -0
  108. package/pipeline/skills/shared/core/google-play-compliance/references/notes.md +7 -0
  109. package/pipeline/skills/shared/core/google-play-compliance/references/version-history.md +5 -0
  110. package/pipeline/skills/shared/core/multi-agent/SKILL.md +39 -262
  111. package/pipeline/skills/shared/core/multi-agent-autopilot-on/SKILL.md +35 -73
  112. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +37 -337
  113. package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +27 -122
  114. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +8 -340
  115. package/pipeline/skills/shared/core/multi-agent-ios-coding-standard/SKILL.md +37 -180
  116. package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +12 -7
  117. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +23 -184
  118. package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +14 -82
  119. package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +25 -277
  120. package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +13 -2
  121. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +73 -217
  122. package/pipeline/skills/shared/external/alarmkit/SKILL.md +11 -162
  123. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-code.md +257 -0
  124. package/pipeline/skills/shared/external/android-architecture/SKILL.md +7 -58
  125. package/pipeline/skills/shared/external/android-architecture/references/code-examples.md +68 -0
  126. package/pipeline/skills/shared/external/app-intents/SKILL.md +3 -45
  127. package/pipeline/skills/shared/external/app-intents/references/code-examples.md +54 -0
  128. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +5 -81
  129. package/pipeline/skills/shared/external/app-store-optimization/references/engagement-and-localization.md +96 -0
  130. package/pipeline/skills/shared/external/app-store-review/SKILL.md +6 -112
  131. package/pipeline/skills/shared/external/app-store-review/references/monetization-and-submission.md +138 -0
  132. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +10 -117
  133. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models-code.md +186 -0
  134. package/pipeline/skills/shared/external/authentication/SKILL.md +3 -50
  135. package/pipeline/skills/shared/external/authentication/references/code-examples.md +59 -0
  136. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +3 -84
  137. package/pipeline/skills/shared/external/callkit-voip/references/code-examples.md +93 -0
  138. package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +7 -263
  139. package/pipeline/skills/shared/external/ci-cd-pipelines/references/pipeline-examples.md +281 -0
  140. package/pipeline/skills/shared/external/clean-code/SKILL.md +8 -41
  141. package/pipeline/skills/shared/external/clean-code/references/code-examples.md +56 -0
  142. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +8 -146
  143. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-code.md +214 -0
  144. package/pipeline/skills/shared/external/compose-components/SKILL.md +6 -80
  145. package/pipeline/skills/shared/external/compose-components/references/code-examples.md +89 -0
  146. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +5 -64
  147. package/pipeline/skills/shared/external/compose-navigation/references/code-examples.md +70 -0
  148. package/pipeline/skills/shared/external/core-data/SKILL.md +9 -157
  149. package/pipeline/skills/shared/external/core-data/references/core-data-code.md +221 -0
  150. package/pipeline/skills/shared/external/core-nfc/SKILL.md +3 -89
  151. package/pipeline/skills/shared/external/core-nfc/references/code-examples.md +98 -0
  152. package/pipeline/skills/shared/external/coreml/SKILL.md +17 -133
  153. package/pipeline/skills/shared/external/coreml/references/coreml-code.md +208 -0
  154. package/pipeline/skills/shared/external/css-modern/SKILL.md +8 -131
  155. package/pipeline/skills/shared/external/css-modern/references/code-examples.md +142 -0
  156. package/pipeline/skills/shared/external/database-patterns/SKILL.md +6 -103
  157. package/pipeline/skills/shared/external/database-patterns/references/orm-examples.md +112 -0
  158. package/pipeline/skills/shared/external/html-semantic/SKILL.md +7 -48
  159. package/pipeline/skills/shared/external/html-semantic/references/code-examples.md +54 -0
  160. package/pipeline/skills/shared/external/humanizer/SKILL.md +6 -19
  161. package/pipeline/skills/shared/external/humanizer/references/voice.md +24 -0
  162. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +13 -170
  163. package/pipeline/skills/shared/external/ios-accessibility/references/assistive-tech-review.md +224 -0
  164. package/pipeline/skills/shared/external/ios-localization/SKILL.md +2 -24
  165. package/pipeline/skills/shared/external/ios-localization/references/code-examples.md +30 -0
  166. package/pipeline/skills/shared/external/ios-networking/SKILL.md +3 -58
  167. package/pipeline/skills/shared/external/ios-networking/references/code-examples.md +67 -0
  168. package/pipeline/skills/shared/external/ios-security/SKILL.md +4 -64
  169. package/pipeline/skills/shared/external/ios-security/references/code-examples.md +74 -0
  170. package/pipeline/skills/shared/external/live-activities/SKILL.md +4 -90
  171. package/pipeline/skills/shared/external/live-activities/references/code-examples.md +108 -0
  172. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +9 -23
  173. package/pipeline/skills/shared/external/localization-reuse-map/references/project-settings.md +22 -0
  174. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +5 -95
  175. package/pipeline/skills/shared/external/mapkit-location/references/code-examples.md +117 -0
  176. package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +7 -106
  177. package/pipeline/skills/shared/external/nextjs-app-router/references/app-router-examples.md +124 -0
  178. package/pipeline/skills/shared/external/permissionkit/SKILL.md +2 -31
  179. package/pipeline/skills/shared/external/permissionkit/references/code-examples.md +37 -0
  180. package/pipeline/skills/shared/external/play-store-review/SKILL.md +6 -74
  181. package/pipeline/skills/shared/external/play-store-review/references/signing-and-policies.md +83 -0
  182. package/pipeline/skills/shared/external/push-notifications/SKILL.md +8 -125
  183. package/pipeline/skills/shared/external/push-notifications/references/notification-code.md +176 -0
  184. package/pipeline/skills/shared/external/python-patterns/SKILL.md +7 -158
  185. package/pipeline/skills/shared/external/python-patterns/references/python-examples.md +176 -0
  186. package/pipeline/skills/shared/external/rest-api-design/SKILL.md +7 -157
  187. package/pipeline/skills/shared/external/rest-api-design/references/api-examples.md +175 -0
  188. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +11 -230
  189. package/pipeline/skills/shared/external/swift-architecture/references/pattern-examples.md +295 -0
  190. package/pipeline/skills/shared/external/swift-codable/SKILL.md +17 -149
  191. package/pipeline/skills/shared/external/swift-codable/references/codable-recipes.md +207 -0
  192. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +12 -99
  193. package/pipeline/skills/shared/external/swift-concurrency/references/code-examples.md +100 -0
  194. package/pipeline/skills/shared/external/swift-concurrency/references/swift-6-2-additions.md +18 -0
  195. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +9 -78
  196. package/pipeline/skills/shared/external/swift-formatstyle/references/formatstyle-examples.md +113 -0
  197. package/pipeline/skills/shared/external/swift-language/SKILL.md +3 -56
  198. package/pipeline/skills/shared/external/swift-language/references/code-examples.md +65 -0
  199. package/pipeline/skills/shared/external/swift-testing-pro/SKILL.md +1 -0
  200. package/pipeline/skills/shared/external/swiftdata/SKILL.md +6 -122
  201. package/pipeline/skills/shared/external/swiftdata/references/code-examples.md +140 -0
  202. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +12 -139
  203. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/interop-code.md +198 -0
  204. package/pipeline/skills/shared/external/tailwind-css/SKILL.md +9 -81
  205. package/pipeline/skills/shared/external/tailwind-css/references/code-examples.md +91 -0
  206. package/pipeline/skills/shared/external/testing-backend/SKILL.md +7 -207
  207. package/pipeline/skills/shared/external/testing-backend/references/testing-examples.md +225 -0
  208. package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +8 -89
  209. package/pipeline/skills/shared/external/typescript-patterns/references/code-examples.md +100 -0
  210. package/pipeline/skills/shared/external/vision-framework/SKILL.md +2 -20
  211. package/pipeline/skills/shared/external/vision-framework/references/code-examples.md +26 -0
  212. package/pipeline/skills/shared/external/vue-composition/SKILL.md +7 -169
  213. package/pipeline/skills/shared/external/vue-composition/references/vue-examples.md +187 -0
  214. package/pipeline/skills/shared/external/web-accessibility/SKILL.md +7 -174
  215. package/pipeline/skills/shared/external/web-accessibility/references/a11y-examples.md +192 -0
  216. package/pipeline/skills/shared/external/web-performance/SKILL.md +8 -66
  217. package/pipeline/skills/shared/external/web-performance/references/code-examples.md +77 -0
  218. package/pipeline/skills/shared/external/web-testing/SKILL.md +11 -186
  219. package/pipeline/skills/shared/external/web-testing/references/testing-examples.md +213 -0
  220. /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/format-and-output.md +0 -0
  221. /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/publish-and-snapshot.md +0 -0
  222. /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/sources-and-recipes.md +0 -0
@@ -0,0 +1,203 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # user-test-handoff.sh - hand a task branch from its worktree to the main
4
+ # repository for a manual test, and hand it back.
5
+ #
6
+ # A branch can be checked out in one place only, so testing in the main repo
7
+ # means removing the worktree first. That removal is where work gets lost: an
8
+ # uncommitted change, a submodule's local branches and stashes (its git dir
9
+ # lives inside the worktree's admin dir), and every ignored file (the run
10
+ # artefacts under `.pipeline/` and `.multi-agent/`, logs, `.env`, build
11
+ # output) live only in the worktree. This script refuses what it cannot carry
12
+ # and carries the rest.
13
+ #
14
+ # It never commits. The caller makes the WIP commit itself first, so the
15
+ # commit passes through the same approval and secret-scan hooks as any other.
16
+ #
17
+ # checkout check that the worktree and its submodules hold no uncommitted
18
+ # or unpushed work, move every ignored entry and the main repo's
19
+ # current branch into a hold under the git common dir, remove the
20
+ # worktree, check the branch out in the main repo.
21
+ # restore put the main repo back on the branch it was on, re-add the
22
+ # worktree on the task branch, move the held entries back. Safe to
23
+ # run again after an interrupted checkout or restore.
24
+ #
25
+ # The hold sits in the same repository as the worktree, so moving even a large
26
+ # ignored tree is a rename. Success is judged by where the branch ends up, not
27
+ # by git's exit code: a failing post-checkout hook (git LFS, core.hooksPath)
28
+ # makes `git checkout` and `git worktree add` exit non-zero after they did
29
+ # their work. The hold is deleted only once every entry is back.
30
+ #
31
+ # Usage:
32
+ # user-test-handoff.sh checkout|restore --task <id> --worktree <path>
33
+ # --root <main repo> --branch <task branch>
34
+ #
35
+ # Prints one JSON line on success. Exit codes:
36
+ # 0 done
37
+ # 1 usage error, or the worktree is not on the task branch
38
+ # 3 the main repo has uncommitted changes to tracked files; nothing moved
39
+ # 5 the worktree or a submodule has uncommitted, unpushed or stashed work;
40
+ # nothing moved
41
+ # 6 checkout: a handoff is already held (run restore), or git refused and
42
+ # everything was put back (restore then answers 7). restore: the main repo
43
+ # could not leave the branch, or the worktree could not be re-added; the
44
+ # hold is kept and the message names the cause
45
+ # 7 restore found nothing held for this task
46
+ set -uo pipefail
47
+
48
+ usage() {
49
+ echo "usage: user-test-handoff.sh checkout|restore --task <id> --worktree <path> --root <repo> --branch <name>" >&2
50
+ exit 1
51
+ }
52
+
53
+ MODE="${1:-}"
54
+ [ $# -gt 0 ] && shift
55
+ TASK="" WT="" ROOT="" BRANCH=""
56
+ while [ $# -gt 0 ]; do
57
+ [ $# -ge 2 ] || usage
58
+ case "$1" in
59
+ --task) TASK="$2" ;;
60
+ --worktree) WT="$2" ;;
61
+ --root) ROOT="$2" ;;
62
+ --branch) BRANCH="$2" ;;
63
+ *) echo "user-test-handoff: unknown argument $1" >&2; usage ;;
64
+ esac
65
+ shift 2
66
+ done
67
+ case "$MODE" in checkout | restore) ;; *) usage ;; esac
68
+ [ -n "$TASK" ] && [ -n "$WT" ] && [ -n "$ROOT" ] && [ -n "$BRANCH" ] || usage
69
+ case "$TASK" in */* | *..* | .* | *'"'* | *\\*) echo "user-test-handoff: task id must be a plain name" >&2; exit 1 ;; esac
70
+
71
+ ROOT="$(cd "$ROOT" 2>/dev/null && pwd)" || { echo "user-test-handoff: --root is not a directory" >&2; exit 1; }
72
+ case "$WT" in /*) ;; *) WT="$ROOT/$WT" ;; esac
73
+ COMMON="$(git -C "$ROOT" rev-parse --path-format=absolute --git-common-dir 2>/dev/null)" ||
74
+ { echo "user-test-handoff: --root is not a git repository" >&2; exit 1; }
75
+ HOLD="$COMMON/multi-agent-user-test/$TASK"
76
+
77
+ esc() { printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'; }
78
+ json() { printf '{"status":"%s","task":"%s","branch":"%s","previous":"%s"}\n' "$1" "$(esc "$TASK")" "$(esc "$BRANCH")" "$(esc "$2")"; }
79
+
80
+ on_branch() { [ "$(git -C "$1" branch --show-current 2>/dev/null)" = "$2" ]; }
81
+
82
+ root_dirty() { [ -n "$(git -C "$ROOT" status --porcelain --untracked-files=no 2>/dev/null)" ]; }
83
+
84
+ # Every ignored entry of the worktree, directories collapsed, NUL-separated.
85
+ ignored_entries() { git -C "$WT" ls-files --others --ignored --exclude-standard --directory -z 2>/dev/null; }
86
+
87
+ # Move the listed entries (NUL-separated file $3) from tree $1 to tree $2.
88
+ move_entries() {
89
+ local from="$1" to="$2" list="$3" e rc=0
90
+ while IFS= read -r -d '' e; do
91
+ e="${e%/}"
92
+ [ -n "$e" ] && [ -e "$from/$e" ] || continue
93
+ mkdir -p "$to/$(dirname "$e")" && rm -rf "${to:?}/$e" && mv "$from/$e" "$to/$e" || rc=1
94
+ done <"$list"
95
+ return "$rc"
96
+ }
97
+
98
+ return_entries() {
99
+ [ -f "$HOLD/entries" ] || : >"$HOLD/entries"
100
+ if move_entries "$HOLD/tree" "$WT" "$HOLD/entries"; then
101
+ rm -rf "$HOLD"
102
+ else
103
+ echo "user-test-handoff: some held entries are still under $HOLD/tree" >&2
104
+ fi
105
+ }
106
+
107
+ submodule_problems() {
108
+ # shellcheck disable=SC2016 # submodule foreach expands $displaypath itself
109
+ git -C "$WT" submodule foreach --quiet --recursive '
110
+ if [ -n "$(git status --porcelain)" ]; then echo "$displaypath (uncommitted changes)"
111
+ elif ! git branch -r --contains HEAD 2>/dev/null | grep -q .; then echo "$displaypath (HEAD on no remote branch; push it, or add a remote if there is none)"
112
+ elif [ -n "$(git rev-list -n1 --branches --not --remotes 2>/dev/null)" ]; then echo "$displaypath (a local branch has unpushed commits)"
113
+ elif [ -n "$(git stash list 2>/dev/null)" ]; then echo "$displaypath (has stashes)"
114
+ fi' 2>/dev/null
115
+ }
116
+
117
+ checkout() {
118
+ on_branch "$WT" "$BRANCH" || { echo "user-test-handoff: $WT is not a worktree on $BRANCH" >&2; exit 1; }
119
+ if [ -e "$HOLD" ]; then
120
+ echo "user-test-handoff: a handoff for $TASK is already held; run restore first" >&2
121
+ exit 6
122
+ fi
123
+ if root_dirty; then
124
+ echo "user-test-handoff: the main repo has uncommitted changes; commit or stash them, then retry" >&2
125
+ git -C "$ROOT" status --short --untracked-files=no >&2
126
+ exit 3
127
+ fi
128
+ local problems
129
+ problems="$(submodule_problems)"
130
+ if [ -n "$problems" ]; then
131
+ printf 'user-test-handoff: these submodules hold work the removal would lose:\n%s\n' "$problems" >&2
132
+ exit 5
133
+ fi
134
+ if [ -n "$(git -C "$WT" status --porcelain)" ]; then
135
+ echo "user-test-handoff: the worktree has uncommitted changes; make the WIP commit first" >&2
136
+ git -C "$WT" status --short >&2
137
+ exit 5
138
+ fi
139
+ local previous
140
+ previous="$(git -C "$ROOT" symbolic-ref --short -q HEAD || git -C "$ROOT" rev-parse HEAD)"
141
+ mkdir -p "$HOLD/tree" || exit 6
142
+ printf '%s\n' "$previous" >"$HOLD/previous" || exit 6
143
+ ignored_entries >"$HOLD/entries" || exit 6
144
+ if ! move_entries "$WT" "$HOLD/tree" "$HOLD/entries"; then
145
+ return_entries
146
+ exit 6
147
+ fi
148
+ if ! git -C "$ROOT" worktree remove --force "$WT"; then
149
+ return_entries
150
+ exit 6
151
+ fi
152
+ git -C "$ROOT" checkout -q "$BRANCH" || true
153
+ if ! on_branch "$ROOT" "$BRANCH"; then
154
+ git -C "$ROOT" worktree add -q "$WT" "$BRANCH" || true
155
+ on_branch "$WT" "$BRANCH" && return_entries
156
+ echo "user-test-handoff: the main repo could not check out $BRANCH" >&2
157
+ exit 6
158
+ fi
159
+ json checked-out "$previous"
160
+ }
161
+
162
+ restore() {
163
+ [ -f "$HOLD/previous" ] || { echo "user-test-handoff: nothing is held for $TASK" >&2; exit 7; }
164
+ local previous
165
+ previous="$(cat "$HOLD/previous")"
166
+ if on_branch "$ROOT" "$BRANCH"; then
167
+ if root_dirty; then
168
+ echo "user-test-handoff: the main repo has uncommitted changes on $BRANCH; stash them (git stash), restore, then git stash pop in the worktree" >&2
169
+ git -C "$ROOT" status --short --untracked-files=no >&2
170
+ exit 3
171
+ fi
172
+ if [ -n "$previous" ] && [ "$previous" != "$BRANCH" ]; then
173
+ git -C "$ROOT" checkout -q "$previous" || true
174
+ else
175
+ git -C "$ROOT" checkout -q --detach || true
176
+ fi
177
+ if on_branch "$ROOT" "$BRANCH"; then
178
+ echo "user-test-handoff: the main repo could not leave $BRANCH for $previous (an untracked file in the way?); move it and run restore again" >&2
179
+ exit 6
180
+ fi
181
+ fi
182
+ if ! on_branch "$WT" "$BRANCH"; then
183
+ git -C "$ROOT" worktree unlock "$WT" 2>/dev/null || true
184
+ git -C "$ROOT" worktree prune
185
+ if [ -e "$WT" ]; then
186
+ if ! { mkdir -p "$HOLD/leftover" && mv "$WT" "$HOLD/leftover/$(date +%s)"; }; then
187
+ echo "user-test-handoff: $WT exists and is not a worktree; move it and run restore again" >&2
188
+ exit 6
189
+ fi
190
+ fi
191
+ git -C "$ROOT" worktree add -q "$WT" "$BRANCH" || true
192
+ on_branch "$WT" "$BRANCH" || { echo "user-test-handoff: the worktree could not be re-added on $BRANCH" >&2; exit 6; }
193
+ fi
194
+ if [ -d "$HOLD/leftover" ]; then
195
+ mkdir -p "$COMMON/multi-agent-user-test-leftover" &&
196
+ mv "$HOLD/leftover" "$COMMON/multi-agent-user-test-leftover/$TASK-$(date +%s)" &&
197
+ echo "user-test-handoff: files found at the worktree path were kept under $COMMON/multi-agent-user-test-leftover" >&2
198
+ fi
199
+ return_entries
200
+ json restored "$previous"
201
+ }
202
+
203
+ "$MODE"
@@ -12,8 +12,10 @@
12
12
  // cat plan.json | node validate-planning.mjs - [--spec <path>]
13
13
  //
14
14
  // --spec names the analysis document or task text the plan was written from.
15
- // A plan more than SPEC_RATIO times its length gets a warning: a plan should
16
- // record decisions, and one far longer than its spec is usually a transcript.
15
+ // A named spec that cannot be read is an error, because the design checks
16
+ // below would otherwise be skipped without a trace. A plan more than
17
+ // SPEC_RATIO times its length gets a warning: a plan should record decisions,
18
+ // and one far longer than its spec is usually a transcript.
17
19
  //
18
20
  // Exit codes:
19
21
  // 0 - valid
@@ -326,7 +328,7 @@ async function main() {
326
328
  specText = readFileSync(spec, "utf-8");
327
329
  specChars = specText.length;
328
330
  } catch {
329
- warnings.push(`spec not readable: ${spec}`);
331
+ errors.push(`spec not readable: ${spec}`);
330
332
  }
331
333
  // With a design behind the plan, values come from its tokens, never from a
332
334
  // number someone read off a screenshot.
@@ -27,6 +27,7 @@
27
27
  // 0 - valid, safe to act on
28
28
  // 1 - invalid structure or required-field violation - caller MUST retry or fallback
29
29
  // 2 - over-rejection guard tripped - caller MUST pause for human (or log+accept in autopilot)
30
+ // (takes precedence over 3; `corrected` is still set when a contradiction was fixed)
30
31
  // 3 - contradiction auto-corrected - caller MAY proceed with corrected output
31
32
  //
32
33
  // Output: JSON result to stdout with shape:
@@ -427,7 +428,7 @@ function validate(raw) {
427
428
  warnings.push(
428
429
  `over-rejection guard: ${parsed.rejected.length}/${total} rejected (${(ratio * 100).toFixed(0)}%) - caller must pause for human confirm (triage=high-rejection-rate); autopilot may accept and log`,
429
430
  );
430
- triageCode = Math.max(triageCode, 2);
431
+ triageCode = 2;
431
432
  }
432
433
  }
433
434
 
@@ -390,7 +390,8 @@ export function completionRefusal(next, payload, replace, diskStatus) {
390
390
  * background launches approve nothing by design, and in analysis mode
391
391
  * Phase 1 is not a plan, so neither is asked.
392
392
  * - A `sessionId` already set is the run's identity for resume; a write that
393
- * changes it detaches the run from the session that started it.
393
+ * changes it detaches the run from the session that started it. A replace
394
+ * document is the whole record, so leaving the field out also changes it.
394
395
  * - Phase 2 to a later phase in an attended run with a tracked issue (jiraId
395
396
  * or githubIssue) needs `phases["2"].trackerStatus` verified, declined or
396
397
  * not-tracked: the issue moves to In Progress before any code, and a failed
@@ -399,15 +400,16 @@ export function completionRefusal(next, payload, replace, diskStatus) {
399
400
  * @param {object} next the document as it would be written
400
401
  * @param {unknown} payload the patch as sent
401
402
  * @param {object} current the document on disk
403
+ * @param {{replace?: boolean}} [opts]
402
404
  * @returns {string|null}
403
405
  */
404
- export function transitionRefusal(next, payload, current) {
406
+ export function transitionRefusal(next, payload, current, { replace = false } = {}) {
405
407
  const patch = payload && typeof payload === "object" ? payload : {};
406
408
  const was = current && typeof current === "object" ? current : {};
407
409
  if (
408
410
  typeof was.sessionId === "string" &&
409
411
  was.sessionId &&
410
- Object.hasOwn(patch, "sessionId") &&
412
+ (replace || Object.hasOwn(patch, "sessionId")) &&
411
413
  next.sessionId !== was.sessionId
412
414
  ) {
413
415
  return `write-state: refusing to change sessionId from "${was.sessionId}". It identifies the run for resume; nothing was written.`;
@@ -531,6 +533,13 @@ async function main() {
531
533
  let current = {};
532
534
  if (replace) {
533
535
  next = payload;
536
+ if (existsSync(path)) {
537
+ try {
538
+ current = JSON.parse(readFileSync(path, "utf-8"));
539
+ } catch {
540
+ current = {};
541
+ }
542
+ }
534
543
  } else {
535
544
  if (existsSync(path)) {
536
545
  try {
@@ -597,7 +606,7 @@ async function main() {
597
606
 
598
607
  const refusal =
599
608
  completionRefusal(next, payload, replace, diskStatus) ||
600
- transitionRefusal(next, payload, current);
609
+ transitionRefusal(next, payload, current, { replace });
601
610
  if (refusal) {
602
611
  console.error(refusal);
603
612
  releaseLock(path);