@haiyangbg/buildbeat 3.1.0 → 3.2.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 (45) hide show
  1. package/CHANGELOG.md +31 -10
  2. package/README.en.md +3 -3
  3. package/SKILL.md +19 -299
  4. package/docs/CAPABILITY-MATRIX.md +1 -1
  5. package/docs/README.md +6 -5
  6. package/docs/RELEASING.md +5 -5
  7. package/docs/v2/RFC-0001-product-definition.md +5 -5
  8. package/docs/v2/RFC-0002-domain-model.md +2 -2
  9. package/docs/v2/RFC-0003-workflow-policy.md +3 -3
  10. package/docs/v2/SPEC-0001-events-v1.md +2 -2
  11. package/docs/v2/guide/00-how-to-talk.en.md +61 -0
  12. package/docs/v2/guide/00-how-to-talk.md +2 -0
  13. package/docs/v2/guide/02-workflow-guide.en.md +125 -0
  14. package/docs/v2/guide/02-workflow-guide.md +7 -1
  15. package/docs/v2/guide/03-policy-guide.en.md +55 -0
  16. package/docs/v2/guide/03-policy-guide.md +2 -0
  17. package/docs/v2/guide/04-adapter-guide.en.md +66 -0
  18. package/docs/v2/guide/04-adapter-guide.md +2 -0
  19. package/docs/v2/guide/05-worker-contract.en.md +60 -0
  20. package/docs/v2/guide/05-worker-contract.md +2 -0
  21. package/docs/v2/guide/09-security-boundaries.en.md +41 -0
  22. package/docs/v2/guide/09-security-boundaries.md +4 -2
  23. package/docs/v2/guide/10-recovery.en.md +1 -1
  24. package/docs/v2/guide/10-recovery.md +1 -1
  25. package/docs/v2/guide/README.en.md +36 -0
  26. package/docs/v2/guide/README.md +8 -6
  27. package/docs/v2/skill/01-principles.md +26 -0
  28. package/docs/v2/skill/02-project-layout.md +31 -0
  29. package/docs/v2/skill/03-collaboration-rules.md +45 -0
  30. package/docs/v2/skill/04-rhythm-and-rituals.md +102 -0
  31. package/docs/v2/skill/05-red-lines.md +13 -0
  32. package/docs/v2/skill/06-bootstrap-and-takeover.md +84 -0
  33. package/docs/v2/skill/07-templates-and-lessons.md +24 -0
  34. package/package.json +7 -16
  35. package/src/v2/cli/run-config-check.js +10 -4
  36. package/src/v2/cli/run.js +35 -11
  37. package/src/v2/engine/yaml-subset.js +17 -11
  38. package/src/v2/presets/policies/ui-render-gate.yaml +1 -1
  39. package/src/v2/runtime/decisions.js +1 -1
  40. package/src/v2/runtime/gc.js +41 -25
  41. package/src/v2/runtime/metrics.js +3 -2
  42. package/src/v2/runtime/orchestrator.js +542 -412
  43. package/src/v2/workspace/workspace-manager.js +62 -3
  44. package/templates/v2/CLAUDE.md +1 -1
  45. package/templates/v2/run-config.example.yaml +2 -0
@@ -214,19 +214,78 @@ export function acquireLock(repoRoot, runId) {
214
214
  return lockPath;
215
215
  }
216
216
 
217
- // Run ids currently holding a lock in this repository (the repository-wide
218
- // active-run marker excluded): who a blocked `start` is queued behind.
217
+ // Locks that belong to no single run: the repository-wide active-run lock,
218
+ // and names starting with "@" (per-work, parallel-run marker, repo-git),
219
+ // which no run id can contain.
220
+ export function isRunLockName(name) {
221
+ return name !== "active-run" && !name.startsWith("@");
222
+ }
223
+
224
+ // Run ids currently holding a lock in this repository: who a blocked
225
+ // `start` is queued behind.
219
226
  export function listHeldRunLocks(repoRoot) {
220
227
  const lockDir = join(repoRoot, ".buildbeat", "runtime", "locks");
221
228
  if (!existsSync(lockDir)) {
222
229
  return [];
223
230
  }
224
231
  return readdirSync(lockDir)
225
- .filter((entry) => entry.endsWith(".lock") && entry !== "active-run.lock")
232
+ .filter((entry) => entry.endsWith(".lock"))
226
233
  .map((entry) => entry.slice(0, -".lock".length))
234
+ .filter(isRunLockName)
227
235
  .sort();
228
236
  }
229
237
 
238
+ // Parallel-run markers (@parallel.<RUN>) whose owner is alive or cannot be
239
+ // judged; markers of dead owners are reclaimed on the way and not returned.
240
+ export function liveParallelMarkers(repoRoot) {
241
+ const lockDir = join(repoRoot, ".buildbeat", "runtime", "locks");
242
+ if (!existsSync(lockDir)) {
243
+ return [];
244
+ }
245
+ const live = [];
246
+ for (const entry of readdirSync(lockDir).sort()) {
247
+ if (!entry.startsWith("@parallel.") || !entry.endsWith(".lock")) {
248
+ continue;
249
+ }
250
+ const lockPath = join(lockDir, entry);
251
+ const seen = inspectLock(lockPath);
252
+ if (seen.state === "dead" && reclaimStaleLock(lockPath, seen.owner)) {
253
+ rmSync(lockPath, { recursive: true, force: true });
254
+ continue;
255
+ }
256
+ live.push({ run: entry.slice("@parallel.".length, -".lock".length), ...seen });
257
+ }
258
+ return live;
259
+ }
260
+
261
+ function pause(ms) {
262
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
263
+ }
264
+
265
+ // Writes to the repository's shared git state (worktree add/remove, branch
266
+ // create/delete, .git/config) are serialised: with parallel runs two drivers
267
+ // could otherwise race on .git/config.lock or index.lock. Held for
268
+ // milliseconds, so a busy lock is waited for (bounded), not reported.
269
+ export function withRepoGitLock(repoRoot, fn, { waitMs = 10_000 } = {}) {
270
+ const deadline = Date.now() + waitMs;
271
+ for (;;) {
272
+ try {
273
+ acquireLock(repoRoot, "@repo-git");
274
+ break;
275
+ } catch (error) {
276
+ if (!error.lock || Date.now() >= deadline) {
277
+ throw error;
278
+ }
279
+ pause(100);
280
+ }
281
+ }
282
+ try {
283
+ return fn();
284
+ } finally {
285
+ releaseLock(repoRoot, "@repo-git");
286
+ }
287
+ }
288
+
230
289
  export function releaseLock(repoRoot, runId) {
231
290
  rmSync(lockPathFor(repoRoot, runId), { recursive: true, force: true });
232
291
  }
@@ -3,5 +3,5 @@
3
3
  本工作区的会话路由、协作规则、红线,**单点在同目录的 [`AGENTS.md`](AGENTS.md)** —— 请立即读取那份。
4
4
 
5
5
  > 本文件只为兼容「只认 `CLAUDE.md` 这个文件名的工具」而存在,**永远保持这几行**。
6
- > 往这里复制任何规则 = 两份文档必然漂移(上游 `lessons.md` 第 1 条:SSOT 腐烂)。
6
+ > 往这里复制任何规则 = 两份文档必然漂移(上游 `lessons.md`「SSOT 腐烂)。
7
7
  > 也不要改成符号链接:Windows 上 git 默认 `core.symlinks=false`,clone 出来会静默退化成一个内容是路径字符串的普通文件,装载即失效。
@@ -23,6 +23,8 @@ budgets:
23
23
  maxAttempts:
24
24
  review: 2
25
25
  reviewRoundsPerWork: 6
26
+ # 默认一个仓库同时只驱动一个 Run。确认本项目的测试不抢固定端口、不共用数据库后,
27
+ # 可加 parallel: true,让本 Work 的 Run 与其他同样打开开关的 Work 并行(同一 Work 仍互斥)
26
28
  # 同树 + 同命令 + 同信封已通过就复用 verify 证据
27
29
  cache:
28
30
  verify: tree