pi-crew 0.11.20 → 0.11.21
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 +13 -0
- package/dist/index.mjs +342 -342
- package/package.json +1 -1
- package/skills/real-test-pi-crew/SKILL.md +21 -0
- package/src/state/coordination/process-lock.ts +22 -2
package/package.json
CHANGED
|
@@ -1256,6 +1256,27 @@ When a tier fails, the recovery is usually quick. Match the symptom to the cause
|
|
|
1256
1256
|
| `team action='schedule' interval=<ms>` always says `Invalid schedule "<N>ms". Use "5m", …` | handle-schedule builds `${interval}ms`; both interval parsers lacked an `ms` unit — every numeric interval died. Fixed 2026-09-21 | The suggested "5m" formats only work via `cron`/`once` params, not `interval` (interval is schema-typed as a plain ms number) |
|
|
1257
1257
|
| Runs keep appearing under one scheduled job; children of YOUR pi process; machine thrashing | **Interval ≥ 2^31-1 ms (≥24.8 days) overflowed the Node timer to a 1ms hot loop** (fixed 2026-09-22 — clamped hops). Check `.crew/audit/prune.jsonl` for interleaved auto-prunes. Contain: remove job, then `pkill -f '[b]ackground-runner.ts --cwd <root>'` | Any new timer scheduling must clamp below MAX_TIMER_DELAY_MS — grep for `setInterval(`/`setTimeout(` with computed delays |
|
|
1258
1258
|
|
|
1259
|
+
## Release gate — battery → push → GitHub Actions GREEN → publish (canonical, 2026-10-14)
|
|
1260
|
+
|
|
1261
|
+
Local `npm run ci` là LINUX-ONLY — không thay được matrix 3-OS của GitHub Actions.
|
|
1262
|
+
Live-proven 2026-10-14: **0.11.19 và 0.11.20 đều publish khi CI GitHub ĐỎ** (local ci + pre-push
|
|
1263
|
+
hook xanh hết) — windows fail do child-env thiếu `SYSTEMROOT` (node con không khởi tạo Winsock được,
|
|
1264
|
+
stdout rỗng); macOS fail do transient ECONNREFUSED khiến accepting-probe 1 lần coi holder SỐNG là
|
|
1265
|
+
corpse → xóa file socket + rebind (STEAL lock) — fix bằng 3-strike takeover (`process-lock.ts`).
|
|
1266
|
+
|
|
1267
|
+
**Quy trình BẮT BUỘC cho mọi release (theo thứ tự, không bỏ bước):**
|
|
1268
|
+
|
|
1269
|
+
1. Cập nhật skill (wave entry) + CHANGELOG heading cho version sắp release — commit.
|
|
1270
|
+
2. Chạy battery theo decision table (đủ tier cho paths đổi) — xanh hết.
|
|
1271
|
+
3. `npm version patch` (tạo release commit + tag).
|
|
1272
|
+
4. `git push origin main --follow-tags` — pre-push hook tự chạy full unit local.
|
|
1273
|
+
5. **`gh run watch <runId> --exit-status` (hoặc poll `gh run list`) — CHỜ matrix 3-OS XANH TOÀN BỘ.**
|
|
1274
|
+
CI đỏ ⇒ KHÔNG publish — fix, push lại, chờ lại. (Check nhanh fail: `gh run view <id>` +
|
|
1275
|
+
`gh api repos/<o>/<r>/actions/jobs/<jobId>//logs` — KHÔNG grep "not ok" mù: tên test có thể
|
|
1276
|
+
chứa literal "not ok" ("malformed JSON is not ok with a message") — grep kèm assertion block.)
|
|
1277
|
+
6. CHỈ KHI xanh: `npm publish` → poll `npm view <pkg> dist-tags.latest` → `gh release create`.
|
|
1278
|
+
7. Post-release: knowledge.md entry SHIPPED + dọn branch/scratch.
|
|
1279
|
+
|
|
1259
1280
|
## Performance budget (per-tier soft limits)
|
|
1260
1281
|
|
|
1261
1282
|
| Tier | Soft limit | Hard limit | What happens over hard limit |
|
|
@@ -105,8 +105,20 @@ function socketAddress(name: string): SocketAddress {
|
|
|
105
105
|
return { address: fileSocketAddress(key), file: true };
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
/**
|
|
109
|
-
|
|
108
|
+
/**
|
|
109
|
+
* Probe whether a live server accepts connections on `address` (leftover-corpse
|
|
110
|
+
* check). A single refusal is NOT trusted on file-socket platforms: macOS can
|
|
111
|
+
* return ECONNREFUSED transiently against a LIVE listener (e.g. a momentarily
|
|
112
|
+
* full listen backlog under load — live-caught in CI 2026-10-14: a foreign
|
|
113
|
+
* child flipped a held lock to "corpse", DELETED the live socket file and
|
|
114
|
+
* rebound, stealing the lock). We require TAKEOVER_REFUSALS consecutive
|
|
115
|
+
* refusals, spaced TAKEOVER_RETRY_MS apart, before declaring a corpse.
|
|
116
|
+
*/
|
|
117
|
+
const TAKEOVER_REFUSALS = 3;
|
|
118
|
+
const TAKEOVER_RETRY_MS = 150;
|
|
119
|
+
|
|
120
|
+
/** One connect-probe: true = somebody accepts; false = refused (corpse-ish). */
|
|
121
|
+
function acceptingOnce(address: string): Promise<boolean> {
|
|
110
122
|
return new Promise((resolve) => {
|
|
111
123
|
const socket = net.connect(address);
|
|
112
124
|
socket.unref();
|
|
@@ -121,6 +133,14 @@ function accepting(address: string): Promise<boolean> {
|
|
|
121
133
|
});
|
|
122
134
|
}
|
|
123
135
|
|
|
136
|
+
async function accepting(address: string): Promise<boolean> {
|
|
137
|
+
for (let attempt = 0; attempt < TAKEOVER_REFUSALS; attempt++) {
|
|
138
|
+
if (attempt > 0) await new Promise((r) => setTimeout(r, TAKEOVER_RETRY_MS));
|
|
139
|
+
if (await acceptingOnce(address)) return true;
|
|
140
|
+
}
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
124
144
|
/**
|
|
125
145
|
* Acquire the process lock for `name`. Resolves with a handle once THIS process
|
|
126
146
|
* owns the kernel socket; rejects with {@link ProcessLockHeldError}
|