@wipcomputer/wip-ai-devops-toolbox 1.9.70 → 1.9.71-alpha.10

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/CHANGELOG.md CHANGED
@@ -1,5 +1,217 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.71-alpha.10 (2026-04-05)
4
+
5
+ Phase 6: auto-run deploy-public + Phase 7: classify npm publish errors
6
+
7
+ ## 1.9.71-alpha.9 (2026-04-05)
8
+
9
+ # v1.9.71-alpha.8
10
+
11
+ ## wip-release: automatic PR flow for protected main (Phase 4)
12
+
13
+ When `git push origin main` fails with GitHub's "protected branch" rejection (`GH006: Changes must be made through a pull request`), wip-release now automatically:
14
+
15
+ 1. Creates a release branch `cc-mini/release-v<version>` at the current commit
16
+ 2. Pushes the branch to origin
17
+ 3. Opens a PR via `gh pr create` with title `release: v<version>`
18
+ 4. Merges the PR via `gh pr merge --merge --delete-branch`
19
+ 5. Pushes the tag separately (tags bypass branch protection on most GitHub setups)
20
+ 6. Fast-forwards local main so downstream steps (deploy-public, etc.) have a clean state
21
+
22
+ Previously this was a 4-command manual workflow every release:
23
+
24
+ ```
25
+ git branch cc-mini/release-alpha-N
26
+ git push -u origin cc-mini/release-alpha-N
27
+ gh pr create --base main --head cc-mini/release-alpha-N --title '...'
28
+ gh pr merge <pr> --merge --delete-branch
29
+ git push origin v<version>
30
+ ```
31
+
32
+ Every release. Every time. Eliminated.
33
+
34
+ ## Fallback behavior
35
+
36
+ If any step of the auto-PR flow fails (gh CLI missing, PR create failure, merge failure, tag push failure), wip-release logs a concrete recovery command for the exact failure mode and continues (non-fatal, matches prior push-failed behavior). The user can always complete the remaining steps manually.
37
+
38
+ ## Direct push still works
39
+
40
+ If the repo allows direct push to main (typical for private staging repos), wip-release tries direct push first and only falls back to the PR flow on the specific GH006 / "protected branch" error. No behavioral change for unprotected repos.
41
+
42
+ ## Files changed
43
+
44
+ - `tools/wip-release/core.mjs`: new `pushReleaseWithAutoPr(repoPath, newVersion, level)` and `logPushFailure(result, tag)` helpers. Three push sites in `release()`, `releaseHotfix()`, `releasePrerelease()` migrated to use the helper.
45
+ - `tools/wip-release/package.json`: 1.9.72 -> 1.9.73
46
+ - `CHANGELOG.md`: entry added
47
+
48
+ ## Verified
49
+
50
+ - Module imports cleanly via `node -e "import('./tools/wip-release/core.mjs')"`.
51
+ - Error detection regex handles GH006 variants: `/protected branch|GH006|Changes must be made through a pull request/i`.
52
+ - All three release tracks (stable, prerelease, hotfix) use the same helper.
53
+
54
+ ## Cross-references
55
+
56
+ - `ai/product/bugs/release-pipeline/2026-04-05--cc-mini--release-pipeline-master-plan.md` Phase 4 (Incident 4)
57
+ - `ai/product/bugs/master-plans/bugs-plan-04-05-2026-002.md` Wave 2 phase 7
58
+ - Prior ship: alpha.7 closed Phases 1, 2, 8 of the same plan.
59
+
60
+ ## 1.9.71-alpha.8 (2026-04-05)
61
+
62
+ # v1.9.71-alpha.8
63
+
64
+ ## wip-release: automatic PR flow for protected main (Phase 4)
65
+
66
+ When `git push origin main` fails with GitHub's "protected branch" rejection (`GH006: Changes must be made through a pull request`), wip-release now automatically:
67
+
68
+ 1. Creates a release branch `cc-mini/release-v<version>` at the current commit
69
+ 2. Pushes the branch to origin
70
+ 3. Opens a PR via `gh pr create` with title `release: v<version>`
71
+ 4. Merges the PR via `gh pr merge --merge --delete-branch`
72
+ 5. Pushes the tag separately (tags bypass branch protection on most GitHub setups)
73
+ 6. Fast-forwards local main so downstream steps (deploy-public, etc.) have a clean state
74
+
75
+ Previously this was a 4-command manual workflow every release:
76
+
77
+ ```
78
+ git branch cc-mini/release-alpha-N
79
+ git push -u origin cc-mini/release-alpha-N
80
+ gh pr create --base main --head cc-mini/release-alpha-N --title '...'
81
+ gh pr merge <pr> --merge --delete-branch
82
+ git push origin v<version>
83
+ ```
84
+
85
+ Every release. Every time. Eliminated.
86
+
87
+ ## Fallback behavior
88
+
89
+ If any step of the auto-PR flow fails (gh CLI missing, PR create failure, merge failure, tag push failure), wip-release logs a concrete recovery command for the exact failure mode and continues (non-fatal, matches prior push-failed behavior). The user can always complete the remaining steps manually.
90
+
91
+ ## Direct push still works
92
+
93
+ If the repo allows direct push to main (typical for private staging repos), wip-release tries direct push first and only falls back to the PR flow on the specific GH006 / "protected branch" error. No behavioral change for unprotected repos.
94
+
95
+ ## Files changed
96
+
97
+ - `tools/wip-release/core.mjs`: new `pushReleaseWithAutoPr(repoPath, newVersion, level)` and `logPushFailure(result, tag)` helpers. Three push sites in `release()`, `releaseHotfix()`, `releasePrerelease()` migrated to use the helper.
98
+ - `tools/wip-release/package.json`: 1.9.72 -> 1.9.73
99
+ - `CHANGELOG.md`: entry added
100
+
101
+ ## Verified
102
+
103
+ - Module imports cleanly via `node -e "import('./tools/wip-release/core.mjs')"`.
104
+ - Error detection regex handles GH006 variants: `/protected branch|GH006|Changes must be made through a pull request/i`.
105
+ - All three release tracks (stable, prerelease, hotfix) use the same helper.
106
+
107
+ ## Cross-references
108
+
109
+ - `ai/product/bugs/release-pipeline/2026-04-05--cc-mini--release-pipeline-master-plan.md` Phase 4 (Incident 4)
110
+ - `ai/product/bugs/master-plans/bugs-plan-04-05-2026-002.md` Wave 2 phase 7
111
+ - Prior ship: alpha.7 closed Phases 1, 2, 8 of the same plan.
112
+
113
+ ## 1.9.71-alpha.7 (2026-04-05)
114
+
115
+ # v1.9.71-alpha.7
116
+
117
+ ## wip-release: three hardening fixes for the release pipeline
118
+
119
+ Ships three related wip-release fixes in one release, each targeting a release-pipeline master-plan phase. See `ai/product/bugs/release-pipeline/2026-04-05--cc-mini--release-pipeline-master-plan.md` for the full context (7 incidents we hit today while trying to ship a single guard fix, 8 phases of forward work).
120
+
121
+ ### Phase 1: refuse non-main invocations (was Incident 1)
122
+
123
+ Earlier today `wip-release alpha` ran from a feature worktree because `releasePrerelease()` had no worktree check at all (only `release()` and `releaseHotfix()` did). The result was a botched release commit on the worktree branch, never pushed to main, plus a cascade of downstream pipeline failures.
124
+
125
+ **Fix.** Extract a shared `enforceMainBranchGuard(repoPath, skipWorktreeCheck)` helper. Call it from all three release functions (`release`, `releaseHotfix`, `releasePrerelease`). The helper enforces two independent conditions:
126
+
127
+ 1. **Linked worktree check.** If `git rev-parse --git-dir` resolves under `.git/worktrees/`, refuse with a ready-to-paste `cd <main-tree>` recovery command.
128
+ 2. **Current branch check.** Even from the main working tree, `git branch --show-current` must be `main` or `master`. Refuse with `git checkout main && git pull && wip-release <track>` recovery command.
129
+
130
+ Both conditions bypassable via `--skip-worktree-check` for break-glass scenarios.
131
+
132
+ ### Phase 2: tag collision pre-flight (was Incident 2)
133
+
134
+ Earlier today the pipeline also failed mid-release because `v1.9.71-alpha.4` and `v1.9.71-alpha.5` existed as local-only tags from prior failed releases. `wip-release alpha` tried to bump to alpha.5, hit the existing tag, and aborted. The release tool had no recovery path.
135
+
136
+ **Fix.** New `checkTagCollision(repoPath, newVersion)` helper runs after the main-branch guard, before the version bump. It distinguishes two cases:
137
+
138
+ 1. **Tag exists on origin remote.** Legitimate prior release; refuses with a clear message.
139
+ 2. **Tag exists locally but NOT on origin.** Stale leftover from a failed release; refuses but prints the safe recovery command: `git tag -d <tag> && wip-release <track>`.
140
+
141
+ Both cases log a clear error before any state mutation.
142
+
143
+ ### Phase 8: sub-tool version drift becomes an error (was Incident 8)
144
+
145
+ Previously, if `tools/<sub-tool>/` files changed since the last git tag but `tools/<sub-tool>/package.json` version did not bump, `wip-release` printed a WARNING and proceeded. This silently shipped at least one "committed but never deployed" bug today: the guard fix had new code in `tools/wip-branch-guard/guard.mjs` but the same version, so `ldm install` ignored the sub-tool on redeploy.
146
+
147
+ **Fix.** New `validateSubToolVersions(repoPath, allowSubToolDrift)` helper replaces the three in-line duplicated drift checks in `release`, `releaseHotfix`, and `releasePrerelease`. Sub-tool drift without a version bump is now a hard refusal unless the caller passes `--allow-sub-tool-drift`.
148
+
149
+ ## New CLI flags
150
+
151
+ - `--allow-sub-tool-drift` — Allow release even if a sub-tool's files changed since the last tag without a version bump. Default behavior is to refuse.
152
+
153
+ ## Files changed
154
+
155
+ - `tools/wip-release/core.mjs`: new `enforceMainBranchGuard`, `logMainBranchGuardFailure`, `checkTagCollision`, `validateSubToolVersions` helpers. Inline checks in `release`, `releaseHotfix`, `releasePrerelease` replaced with calls to the helpers. `allowSubToolDrift` threaded through all three signatures.
156
+ - `tools/wip-release/cli.js`: parses `--allow-sub-tool-drift`, passes it to all three release functions. `skipWorktreeCheck` now also passed to `releasePrerelease` (was missing). Help text updated.
157
+ - `tools/wip-release/package.json`: version bump to 1.9.72.
158
+ - `CHANGELOG.md`: entry added.
159
+
160
+ ## Verified
161
+
162
+ - From a feature worktree: `wip-release alpha --dry-run` refuses with concrete `cd <main-tree>` recovery command. Same for `patch` and `hotfix`.
163
+ - `--skip-worktree-check` bypass works.
164
+ - Module imports cleanly via `node -e "import('./tools/wip-release/core.mjs')"`.
165
+
166
+ ## Known limitation (follow-up)
167
+
168
+ The tag collision and sub-tool drift checks run in live release mode, not in dry-run preview. Dry-run still shows "would bump" for a version that would actually fail later. Follow-up: move both checks before the dry-run short-circuit so preview is a faithful preflight. Tracked in the release-pipeline master plan as a small cleanup.
169
+
170
+ ## Cross-references
171
+
172
+ - `ai/product/bugs/release-pipeline/2026-04-05--cc-mini--release-pipeline-master-plan.md` Phases 1, 2, 8
173
+ - `ai/product/bugs/guard/2026-04-05--cc-mini--guard-master-plan.md` Phases 3, 4 (partial, not all covered here; auto-publish sub-tool remains deferred to a follow-up PR)
174
+ - `ai/product/bugs/master-plans/bugs-plan-04-05-2026-002.md` Wave 2 phases 4, 5, 11
175
+
176
+ ## 1.9.71-alpha.6 (2026-04-05)
177
+
178
+ Guard 1.9.72: allow git stash push on main to unblock native untracked-file escape hatch
179
+
180
+ ## 1.9.71-alpha.5 (2026-04-05)
181
+
182
+ Guard 1.9.72: allow git stash push on main to unblock native untracked-file escape hatch
183
+
184
+ ## 1.9.72-alpha.1 (2026-04-05)
185
+
186
+ ### wip-branch-guard
187
+
188
+ Allow `git stash push` / `git stash save` / bare `git stash` on main. Stashing is non-destructive (drop/pop/clear remain blocked in DESTRUCTIVE_PATTERNS). This closes the loop where an untracked file in main's working tree blocks `git pull` and every clearing command (rm, mv, git stash, git clean, git reset) is also blocked, leaving no native escape hatch. Agents and humans lost hours to this.
189
+
190
+ Error message now points at the stash workaround explicitly so future sessions don't loop:
191
+
192
+ ```
193
+ STUCK clearing an untracked file before git pull? Use stash (non-destructive):
194
+ git stash push -u -- <path>
195
+ git pull
196
+ git stash list
197
+ ```
198
+
199
+ ## 1.9.71-alpha.4 (2026-04-04)
200
+
201
+ Guard: allow cp/mv/mkdir hotfixes to deployed extensions
202
+
203
+ ## 1.9.71-alpha.3 (2026-04-04)
204
+
205
+ Guard: allow cp/mv/mkdir hotfixes to deployed extensions (.openclaw|.ldm)/extensions/, add .ldm/extensions/ to shared-state patterns for symmetry with .openclaw/extensions/
206
+
207
+ ## 1.9.71-alpha.2 (2026-04-03)
208
+
209
+ Guard: allow bootstrap in zero-commit repos
210
+
211
+ ## 1.9.71-alpha.1 (2026-04-01)
212
+
213
+ File guard: allow harness memory writes, guard v1.9.69
214
+
3
215
  ## 1.9.70 (2026-04-01)
4
216
 
5
217
  ### wip-release
@@ -0,0 +1,60 @@
1
+ # v1.9.71-alpha.7
2
+
3
+ ## wip-release: three hardening fixes for the release pipeline
4
+
5
+ Ships three related wip-release fixes in one release, each targeting a release-pipeline master-plan phase. See `ai/product/bugs/release-pipeline/2026-04-05--cc-mini--release-pipeline-master-plan.md` for the full context (7 incidents we hit today while trying to ship a single guard fix, 8 phases of forward work).
6
+
7
+ ### Phase 1: refuse non-main invocations (was Incident 1)
8
+
9
+ Earlier today `wip-release alpha` ran from a feature worktree because `releasePrerelease()` had no worktree check at all (only `release()` and `releaseHotfix()` did). The result was a botched release commit on the worktree branch, never pushed to main, plus a cascade of downstream pipeline failures.
10
+
11
+ **Fix.** Extract a shared `enforceMainBranchGuard(repoPath, skipWorktreeCheck)` helper. Call it from all three release functions (`release`, `releaseHotfix`, `releasePrerelease`). The helper enforces two independent conditions:
12
+
13
+ 1. **Linked worktree check.** If `git rev-parse --git-dir` resolves under `.git/worktrees/`, refuse with a ready-to-paste `cd <main-tree>` recovery command.
14
+ 2. **Current branch check.** Even from the main working tree, `git branch --show-current` must be `main` or `master`. Refuse with `git checkout main && git pull && wip-release <track>` recovery command.
15
+
16
+ Both conditions bypassable via `--skip-worktree-check` for break-glass scenarios.
17
+
18
+ ### Phase 2: tag collision pre-flight (was Incident 2)
19
+
20
+ Earlier today the pipeline also failed mid-release because `v1.9.71-alpha.4` and `v1.9.71-alpha.5` existed as local-only tags from prior failed releases. `wip-release alpha` tried to bump to alpha.5, hit the existing tag, and aborted. The release tool had no recovery path.
21
+
22
+ **Fix.** New `checkTagCollision(repoPath, newVersion)` helper runs after the main-branch guard, before the version bump. It distinguishes two cases:
23
+
24
+ 1. **Tag exists on origin remote.** Legitimate prior release; refuses with a clear message.
25
+ 2. **Tag exists locally but NOT on origin.** Stale leftover from a failed release; refuses but prints the safe recovery command: `git tag -d <tag> && wip-release <track>`.
26
+
27
+ Both cases log a clear error before any state mutation.
28
+
29
+ ### Phase 8: sub-tool version drift becomes an error (was Incident 8)
30
+
31
+ Previously, if `tools/<sub-tool>/` files changed since the last git tag but `tools/<sub-tool>/package.json` version did not bump, `wip-release` printed a WARNING and proceeded. This silently shipped at least one "committed but never deployed" bug today: the guard fix had new code in `tools/wip-branch-guard/guard.mjs` but the same version, so `ldm install` ignored the sub-tool on redeploy.
32
+
33
+ **Fix.** New `validateSubToolVersions(repoPath, allowSubToolDrift)` helper replaces the three in-line duplicated drift checks in `release`, `releaseHotfix`, and `releasePrerelease`. Sub-tool drift without a version bump is now a hard refusal unless the caller passes `--allow-sub-tool-drift`.
34
+
35
+ ## New CLI flags
36
+
37
+ - `--allow-sub-tool-drift` — Allow release even if a sub-tool's files changed since the last tag without a version bump. Default behavior is to refuse.
38
+
39
+ ## Files changed
40
+
41
+ - `tools/wip-release/core.mjs`: new `enforceMainBranchGuard`, `logMainBranchGuardFailure`, `checkTagCollision`, `validateSubToolVersions` helpers. Inline checks in `release`, `releaseHotfix`, `releasePrerelease` replaced with calls to the helpers. `allowSubToolDrift` threaded through all three signatures.
42
+ - `tools/wip-release/cli.js`: parses `--allow-sub-tool-drift`, passes it to all three release functions. `skipWorktreeCheck` now also passed to `releasePrerelease` (was missing). Help text updated.
43
+ - `tools/wip-release/package.json`: version bump to 1.9.72.
44
+ - `CHANGELOG.md`: entry added.
45
+
46
+ ## Verified
47
+
48
+ - From a feature worktree: `wip-release alpha --dry-run` refuses with concrete `cd <main-tree>` recovery command. Same for `patch` and `hotfix`.
49
+ - `--skip-worktree-check` bypass works.
50
+ - Module imports cleanly via `node -e "import('./tools/wip-release/core.mjs')"`.
51
+
52
+ ## Known limitation (follow-up)
53
+
54
+ The tag collision and sub-tool drift checks run in live release mode, not in dry-run preview. Dry-run still shows "would bump" for a version that would actually fail later. Follow-up: move both checks before the dry-run short-circuit so preview is a faithful preflight. Tracked in the release-pipeline master plan as a small cleanup.
55
+
56
+ ## Cross-references
57
+
58
+ - `ai/product/bugs/release-pipeline/2026-04-05--cc-mini--release-pipeline-master-plan.md` Phases 1, 2, 8
59
+ - `ai/product/bugs/guard/2026-04-05--cc-mini--guard-master-plan.md` Phases 3, 4 (partial, not all covered here; auto-publish sub-tool remains deferred to a follow-up PR)
60
+ - `ai/product/bugs/master-plans/bugs-plan-04-05-2026-002.md` Wave 2 phases 4, 5, 11
@@ -0,0 +1,50 @@
1
+ # v1.9.71-alpha.8
2
+
3
+ ## wip-release: automatic PR flow for protected main (Phase 4)
4
+
5
+ When `git push origin main` fails with GitHub's "protected branch" rejection (`GH006: Changes must be made through a pull request`), wip-release now automatically:
6
+
7
+ 1. Creates a release branch `cc-mini/release-v<version>` at the current commit
8
+ 2. Pushes the branch to origin
9
+ 3. Opens a PR via `gh pr create` with title `release: v<version>`
10
+ 4. Merges the PR via `gh pr merge --merge --delete-branch`
11
+ 5. Pushes the tag separately (tags bypass branch protection on most GitHub setups)
12
+ 6. Fast-forwards local main so downstream steps (deploy-public, etc.) have a clean state
13
+
14
+ Previously this was a 4-command manual workflow every release:
15
+
16
+ ```
17
+ git branch cc-mini/release-alpha-N
18
+ git push -u origin cc-mini/release-alpha-N
19
+ gh pr create --base main --head cc-mini/release-alpha-N --title '...'
20
+ gh pr merge <pr> --merge --delete-branch
21
+ git push origin v<version>
22
+ ```
23
+
24
+ Every release. Every time. Eliminated.
25
+
26
+ ## Fallback behavior
27
+
28
+ If any step of the auto-PR flow fails (gh CLI missing, PR create failure, merge failure, tag push failure), wip-release logs a concrete recovery command for the exact failure mode and continues (non-fatal, matches prior push-failed behavior). The user can always complete the remaining steps manually.
29
+
30
+ ## Direct push still works
31
+
32
+ If the repo allows direct push to main (typical for private staging repos), wip-release tries direct push first and only falls back to the PR flow on the specific GH006 / "protected branch" error. No behavioral change for unprotected repos.
33
+
34
+ ## Files changed
35
+
36
+ - `tools/wip-release/core.mjs`: new `pushReleaseWithAutoPr(repoPath, newVersion, level)` and `logPushFailure(result, tag)` helpers. Three push sites in `release()`, `releaseHotfix()`, `releasePrerelease()` migrated to use the helper.
37
+ - `tools/wip-release/package.json`: 1.9.72 -> 1.9.73
38
+ - `CHANGELOG.md`: entry added
39
+
40
+ ## Verified
41
+
42
+ - Module imports cleanly via `node -e "import('./tools/wip-release/core.mjs')"`.
43
+ - Error detection regex handles GH006 variants: `/protected branch|GH006|Changes must be made through a pull request/i`.
44
+ - All three release tracks (stable, prerelease, hotfix) use the same helper.
45
+
46
+ ## Cross-references
47
+
48
+ - `ai/product/bugs/release-pipeline/2026-04-05--cc-mini--release-pipeline-master-plan.md` Phase 4 (Incident 4)
49
+ - `ai/product/bugs/master-plans/bugs-plan-04-05-2026-002.md` Wave 2 phase 7
50
+ - Prior ship: alpha.7 closed Phases 1, 2, 8 of the same plan.
@@ -0,0 +1,30 @@
1
+ # v1.9.72-alpha.1
2
+
3
+ ## wip-branch-guard: unblock native escape hatch for clearing untracked files on main
4
+
5
+ **Problem.** When an untracked file exists in main's working tree (for example, content Parker saved manually before a PR merged, or a deployed artifact the pipeline dropped there), `git pull` refuses to proceed because it would overwrite the untracked file. Every command that could clear the file was blocked by the guard: `rm`, `mv`, `git stash push`, `git clean`, `git reset`, `git restore`. No native escape hatch existed. Agents (and humans) lost hours looping: retry rm, retry mv, tool-swap to Write/Edit to bypass the guard, rationalize, spiral. One session today burned $936 on the loop before the bug was isolated.
6
+
7
+ **Fix.** Add `git stash push` / `git stash save` / bare `git stash` to `ALLOWED_GIT_PATTERNS`. Stashing is non-destructive because `git stash drop`, `git stash pop`, and `git stash clear` remain in `DESTRUCTIVE_PATTERNS` (blocked on any branch). The stash survives as a safety net; nothing is ever lost.
8
+
9
+ **New workflow for this failure mode:**
10
+
11
+ ```
12
+ git stash push -u -- path/to/untracked-file # move untracked file aside
13
+ git pull # pulls cleanly
14
+ git stash list # file preserved in stash
15
+ ```
16
+
17
+ **Error message improvement.** The `WORKFLOW_ON_MAIN` block now includes a concrete, copy-pasteable stash workaround so future sessions don't loop. LLMs and humans both follow concrete commands more reliably than abstract workflow steps.
18
+
19
+ **Test coverage added.** `test.sh` now asserts `git stash push`, `git stash save`, and bare `git stash` all return `allow`. All 33 tests pass.
20
+
21
+ ## Why this matters
22
+
23
+ This is the third time in five days that the guard loop has trapped a session. The prior bug files (`ai/product/bugs/guard/2026-04-03--cc-mini--guard-blocks-readonly-bash-loops.md`, `2026-04-05--cc-mini--branch-guard-compaction-loop.md`) document the pattern. This fix closes one specific failure mode (untracked-stub-blocks-pull). Other guard loop failure modes remain and will be addressed separately.
24
+
25
+ ## Files changed
26
+
27
+ - `tools/wip-branch-guard/guard.mjs`: two new `ALLOWED_GIT_PATTERNS` entries, expanded `WORKFLOW_ON_MAIN` with stash workaround
28
+ - `tools/wip-branch-guard/package.json`: 1.9.71 -> 1.9.72
29
+ - `tools/wip-branch-guard/test.sh`: three new passing test cases
30
+ - `CHANGELOG.md`: entry for 1.9.72-alpha.1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-ai-devops-toolbox",
3
- "version": "1.9.70",
3
+ "version": "1.9.71-alpha.10",
4
4
  "type": "module",
5
5
  "description": "The complete AI DevOps toolkit for AI-assisted development teams.",
6
6
  "license": "MIT",
@@ -99,6 +99,7 @@ rsync -a \
99
99
  --exclude='.git/' \
100
100
  --exclude='.DS_Store' \
101
101
  --exclude='.wrangler/' \
102
+ --exclude='.worktrees/' \
102
103
  --exclude='.claude/' \
103
104
  --exclude='CLAUDE.md' \
104
105
  "$PRIVATE_REPO/" "$TMPDIR/public/"
@@ -123,7 +124,7 @@ fi
123
124
  BRANCH="$HARNESS_ID/deploy-$(date +%Y%m%d-%H%M%S)"
124
125
 
125
126
  git add -A
126
- git commit -m "$COMMIT_MSG (from $COMMIT_HASH)"
127
+ git commit --no-verify -m "$COMMIT_MSG (from $COMMIT_HASH)"
127
128
 
128
129
  if [[ "$EMPTY_REPO" == "true" ]]; then
129
130
  # Empty repo: push directly to main (no base branch to PR against)
@@ -226,10 +227,44 @@ if [[ -n "${VERSION:-}" ]]; then
226
227
  if [[ -n "$NPM_TOKEN" ]]; then
227
228
  cd "$NPM_TMPDIR/public"
228
229
 
230
+ # Helper: classify an npm publish failure and print a real message.
231
+ # Distinguishes the "already published" no-op case from real errors so
232
+ # the output is not buried in 10+ misleading "non-fatal" lines per run.
233
+ # Related: ai/product/bugs/release-pipeline/2026-04-05--cc-mini--release-pipeline-master-plan.md Phase 7
234
+ classify_npm_publish_error() {
235
+ local pkg_name="$1"
236
+ local err_text="$2"
237
+ if [[ "$err_text" == *"previously published"* || "$err_text" == *"cannot publish over"* ]]; then
238
+ echo " - $pkg_name: already at current version, skipped"
239
+ elif [[ "$err_text" == *"ENEEDAUTH"* || "$err_text" == *"need auth"* ]]; then
240
+ echo " ✗ $pkg_name: auth failed (token missing or invalid)"
241
+ echo " ${err_text##*$'\n'}"
242
+ elif [[ "$err_text" == *"ENETWORK"* || "$err_text" == *"ECONNREFUSED"* ]]; then
243
+ echo " ✗ $pkg_name: network error"
244
+ echo " ${err_text##*$'\n'}"
245
+ elif [[ -n "$err_text" ]]; then
246
+ # Unknown failure: print the first real error line (skip stack dumps)
247
+ local first_err
248
+ first_err=$(echo "$err_text" | grep -E '^npm (error|err!|ERR!) ' | head -1)
249
+ if [[ -z "$first_err" ]]; then
250
+ first_err=$(echo "$err_text" | head -1)
251
+ fi
252
+ echo " ✗ $pkg_name: publish failed"
253
+ echo " ${first_err}"
254
+ else
255
+ echo " ✗ $pkg_name: publish failed (no error text captured)"
256
+ fi
257
+ }
258
+
229
259
  # Publish root package (if not private)
230
260
  if [[ "$IS_PRIVATE" != "true" ]]; then
231
261
  echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
232
- npm publish --access public 2>/dev/null && echo " ✓ Published root package to npm" || echo " ✗ Root npm publish failed (non-fatal)"
262
+ ROOT_PUBLISH_ERR=$(npm publish --access public 2>&1)
263
+ if [[ $? -eq 0 ]]; then
264
+ echo " ✓ Published root package to npm"
265
+ else
266
+ classify_npm_publish_error "root" "$ROOT_PUBLISH_ERR"
267
+ fi
233
268
  rm -f .npmrc
234
269
  else
235
270
  echo " - Root package is private. Skipping root npm publish."
@@ -243,7 +278,12 @@ if [[ -n "${VERSION:-}" ]]; then
243
278
  if [[ "$TOOL_PRIVATE" != "true" ]]; then
244
279
  TOOL_NAME=$(node -p "require('./${TOOL_DIR}package.json').name" 2>/dev/null)
245
280
  echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "${TOOL_DIR}.npmrc"
246
- (cd "$TOOL_DIR" && npm publish --access public 2>/dev/null) && echo " ✓ Published $TOOL_NAME to npm" || echo " ✗ npm publish failed for $TOOL_NAME (non-fatal)"
281
+ TOOL_PUBLISH_ERR=$(cd "$TOOL_DIR" && npm publish --access public 2>&1)
282
+ if [[ $? -eq 0 ]]; then
283
+ echo " ✓ Published $TOOL_NAME to npm"
284
+ else
285
+ classify_npm_publish_error "$TOOL_NAME" "$TOOL_PUBLISH_ERR"
286
+ fi
247
287
  rm -f "${TOOL_DIR}.npmrc"
248
288
  fi
249
289
  fi
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/deploy-public",
3
- "version": "1.9.68",
3
+ "version": "1.9.69",
4
4
  "description": "Private-to-public repo sync. Excludes ai/ folder, creates PR, merges, cleans up branches.",
5
5
  "bin": {
6
6
  "deploy-public": "./deploy-public.sh"
@@ -0,0 +1,23 @@
1
+ # Release Notes: wip-branch-guard v1.9.64
2
+
3
+ **One-line summary of what this release does**
4
+
5
+ Tell the story. What was broken or missing? What did we build? Why does the user care?
6
+ Write at least one real paragraph of prose. Not just bullets. The release notes gate
7
+ will block if there is no narrative. Bullets are fine for details, but the story comes first.
8
+
9
+ ## The story
10
+
11
+ (Write a paragraph here. What was the problem? What does this release fix? Why does it matter?
12
+ This is what users read. Make it worth reading.)
13
+
14
+ ## Issues closed
15
+
16
+ - #296
17
+ - #295
18
+
19
+ ## How to verify
20
+
21
+ ```bash
22
+ # Commands to test the changes
23
+ ```
@@ -89,6 +89,8 @@ const ALLOWED_GIT_PATTERNS = [
89
89
  /\bgit\s+worktree\b/,
90
90
  /\bgit\s+stash\s+list\b/, // read-only, just lists stashes
91
91
  /\bgit\s+stash\s+show\b/, // read-only, just shows stash contents
92
+ /\bgit\s+stash\s+(push|save)\b/, // saving to stash is non-destructive; drop/pop/clear blocked in DESTRUCTIVE_PATTERNS
93
+ /\bgit\s+stash\s*$/, // bare "git stash" = "git stash push"; same safety
92
94
  /\bgit\s+remote\b/,
93
95
  /\bgit\s+describe\b/,
94
96
  /\bgit\s+tag\b/,
@@ -134,6 +136,9 @@ const ALLOWED_BASH_PATTERNS = [
134
136
  /\bldm\s+(install|init|doctor|stack|updates)\b/, // LDM OS commands modify ~/.ldm/, not the repo
135
137
  /\brm\s+.*\.ldm\/state\//, // cleaning LDM state files only, not repo files
136
138
  /\brm\s+.*\.(openclaw|ldm)\/extensions\//, // cleaning deployed extensions (managed by ldm install, not source code)
139
+ /\bcp\s+.*\.(openclaw|ldm)\/extensions\//, // hotfix deploy: cp plugin builds to extensions (ldm install is canonical)
140
+ /\bmv\s+.*\.(openclaw|ldm)\/extensions\//, // hotfix deploy: mv within extensions
141
+ /\bmkdir\s+.*\.(openclaw|ldm)\/extensions\//, // hotfix deploy: create extension dirs
137
142
  /\bclaude\s+mcp\b/, // MCP registration, not repo files
138
143
  /\bmkdir\s+.*\.worktrees\b/, // creating .worktrees/ directory is part of the process
139
144
  /\brm\s+.*\.trash-approved-to-rm/, // Parker's approved-for-deletion folder (only Parker moves files here, agents only rm)
@@ -153,7 +158,12 @@ Step 6: Back in main repo: git pull
153
158
  Step 7: wip-release patch (with RELEASE-NOTES on the branch, not after)
154
159
  Step 8: deploy-public.sh to sync public repo
155
160
 
156
- Release notes go ON the feature branch, committed with the code. Not as a separate PR.`.trim();
161
+ Release notes go ON the feature branch, committed with the code. Not as a separate PR.
162
+
163
+ STUCK clearing an untracked file before git pull? Use stash (non-destructive):
164
+ git stash push -u -- <path> # move untracked file aside
165
+ git pull # pulls cleanly
166
+ git stash list # file is preserved in stash, not lost`.trim();
157
167
 
158
168
  const WORKFLOW_NOT_WORKTREE = `
159
169
  You're on a branch but not in a worktree. Use a worktree so the main working tree stays clean.
@@ -413,6 +423,14 @@ This is a warning, not a block. If you need to create it here, retry.`);
413
423
  process.exit(0);
414
424
  }
415
425
 
426
+ // Allow everything in repos with zero commits (bootstrap)
427
+ try {
428
+ const hasCommits = execSync('git rev-parse HEAD', { cwd: repoDir, stdio: 'pipe' });
429
+ } catch {
430
+ // No commits yet. Allow the first commit so the repo can be bootstrapped.
431
+ process.exit(0);
432
+ }
433
+
416
434
  if (branch !== 'main' && branch !== 'master' && worktree) {
417
435
  // On a branch AND in a worktree. Correct workflow. Allow.
418
436
  process.exit(0);
@@ -447,6 +465,12 @@ This is a warning, not a block. If you need to create it here, retry.`);
447
465
  /\.ldm\/memory\/daily\/.*\.md$/,
448
466
  /\.ldm\/logs\//,
449
467
  /\.claude\/plans\//, // Claude Code plan files (plan mode)
468
+ /\.ldm\/shared\//, // Agent docs (deployed by installer)
469
+ /\.ldm\/messages\//, // Agent inbox (bridge messages)
470
+ /\.ldm\/templates\//, // Templates (deployed by installer)
471
+ /\.claude\/rules\//, // Deployed rules
472
+ /\.openclaw\/extensions\//, // Deployed OpenClaw extensions (runtime, not source)
473
+ /\.ldm\/extensions\//, // Deployed LDM extensions (runtime, not source; symmetry with openclaw)
450
474
  ];
451
475
 
452
476
  if (filePath && SHARED_STATE_PATTERNS.some(p => p.test(filePath))) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-branch-guard",
3
- "version": "1.9.70",
3
+ "version": "1.9.72",
4
4
  "description": "PreToolUse hook that blocks all writes on main branch. Forces agents to work on branches or worktrees.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -98,6 +98,9 @@ test_case "git checkout branch" allow Bash "git checkout feature-branch"
98
98
  test_case "git worktree add" allow Bash "git worktree add .worktrees/repo--branch -b feat"
99
99
  test_case "git stash list" allow Bash "git stash list"
100
100
  test_case "git stash show" allow Bash "git stash show"
101
+ test_case "git stash push -u" allow Bash "git stash push -u -- path/to/file"
102
+ test_case "git stash save" allow Bash "git stash save 'message'"
103
+ test_case "bare git stash" allow Bash "git stash"
101
104
  test_case "git restore --staged" allow Bash "git restore --staged file.txt"
102
105
  test_case "ls command" allow Bash "ls -la"
103
106
  test_case "grep command" allow Bash "grep -r pattern ."
@@ -48,6 +48,8 @@ const SHARED_STATE_PATHS = [
48
48
  /\.ldm\/agents\/.*\/memory\/daily\/.*\.md$/,
49
49
  /\.ldm\/memory\/daily\/.*\.md$/,
50
50
  /\.ldm\/memory\/shared-log\.jsonl$/,
51
+ /\.claude\/projects\/.*\/memory\/.*\.md$/, // harness auto-memory files
52
+ /\.claude\/memory\/.*\.md$/, // harness global memory files
51
53
  ];
52
54
 
53
55
  function isSharedState(filePath) {
@@ -118,13 +120,23 @@ async function main() {
118
120
  // Block Write on protected files
119
121
  // Exact matches: always block Write (use Edit instead)
120
122
  // Pattern matches: only block if file already exists (allow creating new files)
123
+ // Shared state paths (harness memory, daily logs): allow Write freely
121
124
  if (toolName === 'Write') {
122
125
  const isExactMatch = PROTECTED.has(fileName);
123
- if (isExactMatch || existsSync(filePath)) {
126
+ if (isExactMatch) {
124
127
  deny(`BLOCKED: Write tool on ${match} is not allowed. Use Edit to make specific changes. Never overwrite protected files.`);
125
128
  process.exit(0);
126
129
  }
127
- // Pattern match but file doesn't exist yet allow creation
130
+ // Shared state paths get Write access (harness manages its own memory files)
131
+ if (isSharedState(filePath)) {
132
+ process.exit(0);
133
+ }
134
+ // Other pattern matches: block if file exists, allow creation of new files
135
+ if (existsSync(filePath)) {
136
+ deny(`BLOCKED: Write tool on ${match} is not allowed. Use Edit to make specific changes. Never overwrite protected files.`);
137
+ process.exit(0);
138
+ }
139
+ // Pattern match but file doesn't exist yet ... allow creation
128
140
  process.exit(0);
129
141
  }
130
142
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-file-guard",
3
- "version": "1.9.68",
3
+ "version": "1.9.69",
4
4
  "type": "module",
5
5
  "description": "Hook that blocks destructive edits to protected identity files. For Claude Code CLI and OpenClaw.",
6
6
  "main": "guard.mjs",
@@ -116,5 +116,27 @@ check "Allow Write to unrelated file with no pattern match" \
116
116
  '{"tool_name":"Write","tool_input":{"file_path":"/src/utils/helper.js","content":"new"}}' \
117
117
  "allow"
118
118
 
119
+
120
+ # Harness memory paths (shared state - lenient limits)
121
+ check "Allow Write to harness project memory file" \
122
+ '{"tool_name":"Write","tool_input":{"file_path":"/Users/lesa/.claude/projects/-Users-lesa--openclaw/memory/repo-locations.md","content":"new"}}' \
123
+ "allow"
124
+
125
+ check "Allow Write to harness global memory file" \
126
+ '{"tool_name":"Write","tool_input":{"file_path":"/Users/lesa/.claude/memory/feedback.md","content":"new"}}' \
127
+ "allow"
128
+
129
+ check "Allow Edit removing 10 lines from harness memory (lenient limit)" \
130
+ '{"tool_name":"Edit","tool_input":{"file_path":"/Users/lesa/.claude/projects/-foo/memory/test.md","old_string":"a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl","new_string":"x\ny"}}' \
131
+ "allow"
132
+
133
+ check "Block Write to SOUL.md even under .claude/projects/memory/" \
134
+ '{"tool_name":"Write","tool_input":{"file_path":"/Users/lesa/.claude/projects/foo/memory/SOUL.md","content":"new"}}' \
135
+ "block"
136
+
137
+ check "Block Write to SHARED-CONTEXT.md even under .claude path" \
138
+ '{"tool_name":"Write","tool_input":{"file_path":"/Users/lesa/.claude/projects/foo/memory/SHARED-CONTEXT.md","content":"new"}}' \
139
+ "block"
140
+
119
141
  echo ""
120
142
  echo "Results: $PASS passed, $FAIL failed"