@tyroneross/build-loop 0.36.0 → 0.43.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.
Files changed (270) hide show
  1. package/.agents/plugins/marketplace.json +2 -2
  2. package/.claude-plugin/marketplace.json +3 -3
  3. package/.claude-plugin/plugin.json +1 -1
  4. package/.codex-plugin/plugin.json +1 -1
  5. package/.cursor/rules/build-loop-surface.mdc +12 -11
  6. package/.cursor/rules/skill-index.mdc +33 -0
  7. package/AGENTS.md +213 -34
  8. package/README.md +99 -31
  9. package/agents/advisor.md +4 -4
  10. package/agents/alignment-checker.md +2 -2
  11. package/agents/architecture-scout.md +4 -4
  12. package/agents/build-orchestrator.md +38 -36
  13. package/agents/database-assessor.md +11 -5
  14. package/agents/design-contract-specialist.md +8 -8
  15. package/agents/fact-checker.md +13 -3
  16. package/agents/fix-critique.md +2 -2
  17. package/agents/independent-auditor.md +60 -7
  18. package/agents/leak-scanner.md +82 -0
  19. package/agents/overfitting-reviewer.md +2 -2
  20. package/agents/plan-critic.md +1 -1
  21. package/agents/promotion-reviewer.md +5 -5
  22. package/agents/retrospective-synthesizer.md +138 -35
  23. package/agents/scope-auditor.md +82 -11
  24. package/agents/security-reviewer.md +56 -2
  25. package/agents/self-improvement-architect.md +17 -3
  26. package/agents/transcript-pattern-miner.md +5 -5
  27. package/agents/ui-validator.md +1 -1
  28. package/bin/build-loop-debugger.js +143 -0
  29. package/bin/build-loop-install.js +1 -4
  30. package/bin/build-loop-load-probe.js +345 -0
  31. package/codex-skills/build-loop/SKILL.md +28 -6
  32. package/commands/feedback.md +37 -0
  33. package/dist/src/interactive-verifier.d.ts +1 -14
  34. package/dist/src/interactive-verifier.d.ts.map +1 -1
  35. package/dist/src/interactive-verifier.js +6 -113
  36. package/dist/src/interactive-verifier.js.map +1 -1
  37. package/dist/src/quality.d.ts +5 -0
  38. package/dist/src/quality.d.ts.map +1 -0
  39. package/dist/src/quality.js +81 -0
  40. package/dist/src/quality.js.map +1 -0
  41. package/dist/src/storage.d.ts.map +1 -1
  42. package/dist/src/storage.js +37 -3
  43. package/dist/src/storage.js.map +1 -1
  44. package/docs/agent-surface-policy.md +35 -31
  45. package/docs/memory-setup.md +19 -0
  46. package/hooks/git/pre-push +65 -4
  47. package/hooks/hooks.json +95 -38
  48. package/hooks/pre-commit +20 -1
  49. package/hooks/pre-edit-rally-point.sh +10 -3
  50. package/hooks/session-start-codex-hook-trust.sh +30 -0
  51. package/hooks/session-start-git-hooks.sh +3 -1
  52. package/hooks/session-start-rally-point.sh +52 -4
  53. package/hooks/session-start-worktree-gc.sh +47 -94
  54. package/hooks/stop-transcript-sweep.sh +173 -0
  55. package/hooks/test_closeout.sh +14 -2
  56. package/package.json +8 -7
  57. package/scripts/README.md +1 -1
  58. package/scripts/_paths.py +65 -0
  59. package/scripts/groundwork_exchange.py +1012 -0
  60. package/scripts/install_memory.py +33 -1
  61. package/scripts/lessons_index/ingest.py +13 -2
  62. package/scripts/lessons_index/query.py +36 -13
  63. package/scripts/memory_context/__init__.py +108 -14
  64. package/scripts/memory_graph/__init__.py +5 -1
  65. package/scripts/project_resolver.py +42 -36
  66. package/scripts/sync_plugin_cache.py +37 -2
  67. package/skills/agent-rally-point/SKILL.md +46 -0
  68. package/skills/api-registry-bridge/SKILL.md +1 -1
  69. package/skills/architecture/dead/SKILL.md +1 -1
  70. package/skills/architecture/impact/SKILL.md +1 -1
  71. package/skills/architecture/review/SKILL.md +1 -1
  72. package/skills/architecture/rules/SKILL.md +3 -3
  73. package/skills/architecture/scan/SKILL.md +1 -1
  74. package/skills/architecture/trace/SKILL.md +1 -1
  75. package/skills/attribution-standard/SKILL.md +6 -6
  76. package/skills/auto-decision-capture/SKILL.md +31 -2
  77. package/skills/auto-finding-capture/SKILL.md +28 -1
  78. package/skills/build-loop/SKILL.md +131 -23
  79. package/skills/build-loop/fallbacks.md +16 -21
  80. package/skills/build-loop/phases/ui-validation.md +2 -2
  81. package/skills/build-loop/references/advisor-dispatch-ladder.md +1 -1
  82. package/skills/build-loop/references/apple-native-planning.md +1 -1
  83. package/skills/build-loop/references/autonomous-and-per-commit-modes.md +11 -5
  84. package/skills/build-loop/references/autonomy-dashboard.md +115 -0
  85. package/skills/build-loop/references/capability-routing.md +24 -2
  86. package/skills/build-loop/references/coordination.md +24 -6
  87. package/skills/build-loop/references/experiment-results-template.md +15 -3
  88. package/skills/build-loop/references/leadership.md +1 -1
  89. package/skills/build-loop/references/memory.md +14 -3
  90. package/skills/build-loop/references/modular-systems-pack.md +8 -0
  91. package/skills/build-loop/references/output-style.md +86 -0
  92. package/skills/build-loop/references/phase-1-assess.md +102 -2
  93. package/skills/build-loop/references/phase-2-plan.md +9 -1
  94. package/skills/build-loop/references/phase-3-execute.md +5 -2
  95. package/skills/build-loop/references/phase-4-review.md +85 -8
  96. package/skills/build-loop/references/phase-5-iterate.md +76 -8
  97. package/skills/build-loop/references/phase-6-learn.md +10 -17
  98. package/skills/build-loop/references/privileged-request-broker.md +254 -0
  99. package/skills/build-loop/references/resource-aware-execution.md +183 -0
  100. package/skills/build-loop/references/self-recursive-dev.md +2 -2
  101. package/skills/build-loop/references/status-output-format.md +207 -0
  102. package/skills/build-loop/references/verify-dispatch.md +56 -2
  103. package/skills/building-with-deepagents/SKILL.md +1 -1
  104. package/skills/claim-scope/SKILL.md +185 -0
  105. package/skills/color-engine/SKILL.md +103 -0
  106. package/skills/color-engine/_core.py +464 -0
  107. package/skills/color-engine/color_engine.py +175 -0
  108. package/skills/cost-rca/SKILL.md +61 -0
  109. package/skills/data-plane-worktrees/SKILL.md +139 -0
  110. package/skills/data-plane-worktrees/agents/openai.yaml +4 -0
  111. package/skills/database-practice/SKILL.md +200 -0
  112. package/skills/database-practice/references/diagnostic-queries.sql +126 -0
  113. package/skills/database-practice/references/vector-and-graph-tuning.md +208 -0
  114. package/skills/database-practice/scripts/db_table_map.py +1244 -0
  115. package/skills/database-practice/scripts/test_db_table_map.py +514 -0
  116. package/skills/debug-loop/SKILL.md +36 -6
  117. package/skills/debugging-memory/SKILL.md +32 -430
  118. package/skills/debugging-memory/references/pattern-extraction.md +4 -4
  119. package/skills/debugging-memory/references/search.md +32 -120
  120. package/skills/debugging-memory/references/store.md +32 -126
  121. package/skills/debugging-memory/references/subagent-integration.md +1 -1
  122. package/skills/decision-queue/SKILL.md +251 -0
  123. package/skills/decision-queue/assets/template.html +1242 -0
  124. package/skills/decision-queue/references/example-large-queue-batching.md +164 -0
  125. package/skills/decision-queue/scripts/regen_template_constants.py +160 -0
  126. package/skills/defenseclaw-bridge/SKILL.md +2 -2
  127. package/skills/defenseclaw-bridge/references/dc-config-mapping.md +2 -9
  128. package/skills/drain-proposals/SKILL.md +53 -0
  129. package/skills/focused-loop-builder/SKILL.md +31 -0
  130. package/skills/focused-loop-builder/references/spec-format.md +27 -0
  131. package/skills/handoff/SKILL.md +169 -8
  132. package/skills/ibr-bridge/SKILL.md +4 -1
  133. package/skills/knowledge/SKILL.md +26 -14
  134. package/skills/knowledge/references/review-mode.md +2 -3
  135. package/skills/knowledge/templates/madr-minimal.md +1 -1
  136. package/skills/mcp-builder/SKILL.md +1 -1
  137. package/skills/model-bakeoff/SKILL.md +48 -10
  138. package/skills/model-tiering/SKILL.md +92 -31
  139. package/skills/native-ax-driver/SKILL.md +38 -5
  140. package/skills/native-ax-driver/scripts/native_driver.py +278 -22
  141. package/skills/native-ax-driver/scripts/test_native_driver.py +227 -0
  142. package/skills/optimize/SKILL.md +1 -1
  143. package/skills/plugin-builder/SKILL.md +48 -1
  144. package/skills/plugin-builder/references/build-loop-phase-guidance.md +3 -4
  145. package/skills/plugin-builder/references/distribution.md +13 -2
  146. package/skills/plugin-builder/references/plugin-hygiene-lessons.md +2 -2
  147. package/skills/plugin-tests/SKILL.md +2 -2
  148. package/skills/recursive-retrospective/SKILL.md +1 -1
  149. package/skills/repo-closeout/SKILL.md +17 -0
  150. package/skills/repo-closeout/agents/openai.yaml +4 -0
  151. package/skills/repo-maintenance/SKILL.md +179 -0
  152. package/skills/repo-maintenance/agents/openai.yaml +4 -0
  153. package/skills/repo-maintenance/references/pre-public-hygiene.md +134 -0
  154. package/skills/repo-maintenance/references/repository-taxonomy.md +161 -0
  155. package/skills/repo-maintenance/references/safety-protocol.md +106 -0
  156. package/skills/repo-maintenance/references/stack-profiles.md +138 -0
  157. package/skills/repo-maintenance/scripts/audit_repo_maintenance.py +1198 -0
  158. package/skills/repo-maintenance/scripts/test_audit_repo_maintenance.py +506 -0
  159. package/skills/repository-intelligence/SKILL.md +189 -0
  160. package/skills/repository-intelligence/agents/openai.yaml +4 -0
  161. package/skills/repository-intelligence/references/assessment-rubric.md +88 -0
  162. package/skills/repository-intelligence/scripts/repository_inventory.py +347 -0
  163. package/skills/research/SKILL.md +12 -2
  164. package/skills/root-cause-analysis/SKILL.md +1 -1
  165. package/skills/runtime-parity-verification/SKILL.md +36 -1
  166. package/skills/security-methodology/SKILL.md +23 -10
  167. package/skills/security-methodology/references/agentic-handoff-templates.md +220 -0
  168. package/skills/security-methodology/references/cross-source-matrix.md +1 -1
  169. package/skills/security-methodology/references/owasp-agentic-top-10.md +1 -1
  170. package/skills/security-scan/SKILL.md +55 -15
  171. package/skills/self-improve/SKILL.md +70 -50
  172. package/skills/silent-assumptions/SKILL.md +341 -0
  173. package/skills/silent-assumptions/references/elicitation-detectors.md +342 -0
  174. package/skills/spec-writing/SKILL.md +128 -24
  175. package/skills/spec-writing/scripts/check_checklist.py +114 -15
  176. package/skills/ui-design/SKILL.md +6 -4
  177. package/skills/ui-design/references/color-engine.md +132 -0
  178. package/skills/ui-design/references/design-preferences-from-owned-apps.md +8 -8
  179. package/skills/ui-design/references/ui-guidance-sources.md +1 -1
  180. package/skills/ui-design/references/universal-design-principles.alt.md +2 -2
  181. package/plugin-artifacts/codex/.codex-plugin/plugin.json +0 -41
  182. package/plugin-artifacts/codex/AGENTS.md +0 -560
  183. package/plugin-artifacts/codex/BUILD-ARTIFACT.md +0 -5
  184. package/plugin-artifacts/codex/LICENSE +0 -202
  185. package/plugin-artifacts/codex/README.md +0 -313
  186. package/plugin-artifacts/codex/assets/build-loop-plugin-icon.png +0 -0
  187. package/plugin-artifacts/codex/docs/agent-surface-policy.md +0 -63
  188. package/plugin-artifacts/codex/references/advisor-dispatch-ladder.md +0 -62
  189. package/plugin-artifacts/codex/references/agent-role-taxonomy.md +0 -135
  190. package/plugin-artifacts/codex/references/autonomous-and-per-commit-modes.md +0 -161
  191. package/plugin-artifacts/codex/references/autonomy-config.md +0 -231
  192. package/plugin-artifacts/codex/references/backlog-system.md +0 -285
  193. package/plugin-artifacts/codex/references/capability-routing.md +0 -231
  194. package/plugin-artifacts/codex/references/codex-subagents.md +0 -106
  195. package/plugin-artifacts/codex/references/coordination-file-template.md +0 -181
  196. package/plugin-artifacts/codex/references/coordination-rules.md +0 -552
  197. package/plugin-artifacts/codex/references/dogfood-reload-checkpoint.md +0 -112
  198. package/plugin-artifacts/codex/references/halt-and-ask-protocol.md +0 -102
  199. package/plugin-artifacts/codex/references/implementer-envelope-schema.md +0 -302
  200. package/plugin-artifacts/codex/references/intent-capability-pack.md +0 -257
  201. package/plugin-artifacts/codex/references/intent-exploration-prompts.md +0 -96
  202. package/plugin-artifacts/codex/references/leadership.md +0 -72
  203. package/plugin-artifacts/codex/references/memory-systems.md +0 -261
  204. package/plugin-artifacts/codex/references/memory.md +0 -313
  205. package/plugin-artifacts/codex/references/model-tier-mapping.md +0 -296
  206. package/plugin-artifacts/codex/references/modular-systems-pack.md +0 -96
  207. package/plugin-artifacts/codex/references/phase-1-assess.md +0 -249
  208. package/plugin-artifacts/codex/references/phase-2-plan.md +0 -86
  209. package/plugin-artifacts/codex/references/phase-3-execute.md +0 -49
  210. package/plugin-artifacts/codex/references/phase-4-review.md +0 -341
  211. package/plugin-artifacts/codex/references/phase-5-iterate.md +0 -72
  212. package/plugin-artifacts/codex/references/phase-6-learn.md +0 -58
  213. package/plugin-artifacts/codex/references/recent-design-structures.md +0 -274
  214. package/plugin-artifacts/codex/references/research-trigger-policy.md +0 -140
  215. package/plugin-artifacts/codex/references/runtime-smoke-triggers.md +0 -42
  216. package/plugin-artifacts/codex/references/self-review.md +0 -234
  217. package/plugin-artifacts/codex/references/single-writer-commit-protocol.md +0 -90
  218. package/plugin-artifacts/codex/references/task-capture-policy.md +0 -68
  219. package/plugin-artifacts/codex/references/ui-io-contract.md +0 -116
  220. package/plugin-artifacts/codex/references/ui-spotcheck-protocol.md +0 -65
  221. package/plugin-artifacts/codex/references/verify-dispatch.md +0 -85
  222. package/plugin-artifacts/codex/skills/build-loop/SKILL.md +0 -381
  223. package/plugin-artifacts/codex/skills/build-loop/detect-plugins.mjs +0 -82
  224. package/plugin-artifacts/codex/skills/build-loop/eval-guide.md +0 -65
  225. package/plugin-artifacts/codex/skills/build-loop/fallbacks.md +0 -549
  226. package/plugin-artifacts/codex/skills/build-loop/phases/fact-check.md +0 -42
  227. package/plugin-artifacts/codex/skills/build-loop/phases/ui-validation.md +0 -267
  228. package/plugin-artifacts/codex/skills/build-loop/references/advisor-dispatch-ladder.md +0 -62
  229. package/plugin-artifacts/codex/skills/build-loop/references/apple-native-planning.md +0 -439
  230. package/plugin-artifacts/codex/skills/build-loop/references/autonomous-and-per-commit-modes.md +0 -161
  231. package/plugin-artifacts/codex/skills/build-loop/references/capability-routing.md +0 -231
  232. package/plugin-artifacts/codex/skills/build-loop/references/codex-subagents.md +0 -106
  233. package/plugin-artifacts/codex/skills/build-loop/references/coordination.md +0 -161
  234. package/plugin-artifacts/codex/skills/build-loop/references/correction-aware-capture.md +0 -177
  235. package/plugin-artifacts/codex/skills/build-loop/references/experiment-results-template.md +0 -101
  236. package/plugin-artifacts/codex/skills/build-loop/references/independent-auditor.md +0 -72
  237. package/plugin-artifacts/codex/skills/build-loop/references/intent-capability-pack.md +0 -257
  238. package/plugin-artifacts/codex/skills/build-loop/references/intent-exploration-prompts.md +0 -96
  239. package/plugin-artifacts/codex/skills/build-loop/references/leadership.md +0 -72
  240. package/plugin-artifacts/codex/skills/build-loop/references/memory.md +0 -313
  241. package/plugin-artifacts/codex/skills/build-loop/references/modular-systems-pack.md +0 -96
  242. package/plugin-artifacts/codex/skills/build-loop/references/output-style.md +0 -222
  243. package/plugin-artifacts/codex/skills/build-loop/references/pay-it-forward-arch.md +0 -98
  244. package/plugin-artifacts/codex/skills/build-loop/references/phase-1-assess.md +0 -249
  245. package/plugin-artifacts/codex/skills/build-loop/references/phase-2-plan.md +0 -86
  246. package/plugin-artifacts/codex/skills/build-loop/references/phase-3-execute.md +0 -49
  247. package/plugin-artifacts/codex/skills/build-loop/references/phase-4-review.md +0 -341
  248. package/plugin-artifacts/codex/skills/build-loop/references/phase-5-iterate.md +0 -72
  249. package/plugin-artifacts/codex/skills/build-loop/references/phase-6-learn.md +0 -58
  250. package/plugin-artifacts/codex/skills/build-loop/references/recent-design-structures.md +0 -274
  251. package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/ASSESSMENT.md +0 -85
  252. package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/STANDALONE_TEST_RUN.md +0 -149
  253. package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/01-simple-bugfix.md +0 -32
  254. package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/02-ui-build-with-iteration.md +0 -48
  255. package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/03-multi-failure-escalation.md +0 -60
  256. package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/04-ui-build-ibr-absent.md +0 -51
  257. package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/05-refactor-navgator-absent.md +0 -71
  258. package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/06-recurring-bug-debugger-absent.md +0 -52
  259. package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/trace-comparison.md +0 -202
  260. package/plugin-artifacts/codex/skills/build-loop/references/self-recursive-dev.md +0 -77
  261. package/plugin-artifacts/codex/skills/build-loop/references/self-review.md +0 -234
  262. package/plugin-artifacts/codex/skills/build-loop/references/ui-io-contract.md +0 -116
  263. package/plugin-artifacts/codex/skills/build-loop/references/verify-dispatch.md +0 -85
  264. package/plugin-artifacts/codex/skills/build-loop/scanners/audit-design-rules.mjs +0 -476
  265. package/plugin-artifacts/codex/skills/build-loop/scanners/require-visual-evidence.mjs +0 -239
  266. package/plugin-artifacts/codex/skills/build-loop/templates/backlog-item.md +0 -35
  267. package/plugin-artifacts/codex/skills/build-loop/templates/codex-worker-prompt.md +0 -100
  268. package/plugin-artifacts/codex/skills/build-loop/templates/ui-subagent-prompt.md +0 -179
  269. package/plugin-artifacts/codex/skills/build-loop/templates/ux-fix-plan.md +0 -40
  270. package/scripts/build_codex_plugin_artifact.py +0 -321
@@ -1,552 +0,0 @@
1
- <!-- SPDX-FileCopyrightText: 2025-2026 Tyrone Ross, Jr <46267523+tyroneross@users.noreply.github.com> | SPDX-License-Identifier: Apache-2.0 -->
2
-
3
- # Coordination Rules (Binding Constitution)
4
-
5
- **Audience:** Any agent participating in a multi-session build-loop run — Claude Code orchestrator, Codex verifier, peer Claude session, CI, headless host.
6
-
7
- **Status:** Binding. Every rule below was codified from a concrete prior-run failure; cross-reference cited next to each rule.
8
-
9
- This file is the durable source of truth for **how peers coordinate**. It replaces tone-suggesting "should" framings with operational rules that have automated enforcement where possible. New coordination files (`.build-loop/coordination/<topic>.md`) start from `references/coordination-file-template.md` and inherit this constitution by reference; per-run files MUST NOT contradict it.
10
-
11
- ---
12
-
13
- ## Operating Rule (verdicts are gating, not advisory)
14
-
15
- **Claude does not proceed past a step marked `verification-pending` until the latest verifier feedback entry for that step is one of:**
16
-
17
- - `PASS` — acceptance criteria verified end-to-end.
18
- - `VARIANCE` that has been resolved (Claude fixed the variance, documented non-acceptance with rationale, OR escalated to the user with explicit decision).
19
- - Explicit user override (recorded in the coord file under "Codex feedback log" or in `state.json.userOverrides[]`).
20
-
21
- A `VARIANCE` left unresolved blocks the next step. A `BLOCKED` entry (verifier could not verify because evidence is missing) requires the producing peer to supply the missing evidence before the next step dispatches.
22
-
23
- **Why binding (not advisory):** the 2026-05-20 audit-execution run found that even when a fresh verifier session reads the coord file, the default reading of "verifier" leans advisory. The operating rule must be stated up-front in the coord file and re-stated in the brief sent to the verifier. Memory citation: `feedback_codex_pass_is_gate_not_comment`.
24
-
25
- **Detection:** `python3 scripts/coordination_status.py --workdir . --session-id <id> --coordination-file <path> --json` — `unresolved: []` means safe to advance; non-empty array means hold and resolve. See [Cheap detection](#cheap-detection-at-step-boundaries) below.
26
-
27
- ---
28
-
29
- ## Channel & Rally Point
30
-
31
- **Every cross-session signal goes through Rally Point using the canonical `scripts/rally_point/post.py` `post()` helper.** Raw `append_change(...)` without a subsequent `bump_revision(...)` is a silent-no-op for consumers — the record lands on disk but no peer's `checkpoint_read(...)` ever surfaces it because their cursor still matches the unchanged revision.
32
-
33
- ```python
34
- from scripts.rally_point.post import post
35
- from scripts.rally_point.discovery_bridge import resolve
36
- from pathlib import Path
37
- envelope = resolve(Path.cwd())
38
- channel = Path(envelope.channel_dir)
39
- post(
40
- channel_dir=channel,
41
- kind="feedback", # or "phase", "commit", "dep-change", "handoff", "arch-scan-complete"
42
- tool="codex", # or "claude_code", "gemini_cli", etc.
43
- model="gpt-5",
44
- run_id="<run-id>",
45
- app_slug=envelope.app_slug,
46
- payload={"step": "<id>", "verdict": "PASS", "evidence": {...}, "impact": "...", "requested_action": "..."},
47
- )
48
- ```
49
-
50
- `post()` bumps the revision FIRST, then appends the record. That ordering guarantees readers who see the new revision can always find the corresponding record (no race where revision is ahead of the log).
51
-
52
- **Channel scope (worktree- and clone-independent):** resolve the channel through `scripts/rally_point/discovery_bridge.resolve(workdir)`. Native `agent-rally-point` discovery returns the canonical shared channel (currently `~/.agent-rally-point/apps/<repo-id>/`). The embedded build-loop fallback also defaults to `~/.agent-rally-point/apps/<slug>/`, where `slug` comes from `git rev-parse --git-common-dir` via `scripts/rally_point/channel_paths.app_slug(cwd)`. The main checkout, every worktree, and every clone of the same canonical repo share ONE channel. Different canonical repos get different channel directories (cross-repo isolation).
53
-
54
- **Rally's REAL CLI surface (the only commands build-loop shells out to).** Rally exposes `enter`, `say <kind>`, `whoami`, `room`, `next`, `recent`, `stop <session|name|tool>`, `sessions [--reap]`, `migrate-legacy`, `mission`, `version`, and `check`. It has NO `setup`, NO `post`, NO `start`, and NO `replay` — those were anticipated but never shipped, and a discovery tier that gated on them was dead code (removed). A write goes out as `rally say <kind> --json …` (build-loop's `post()` helper translates to this); identity/channel info comes from `rally whoami --json` (`repo_root`/`repo_id`/`worktree`/`build_id`/`cwd`), NOT a `setup` probe. The surface-acceptance check (`discovery_bridge._rally_binary_supports_required_surface`) is pinned to a real rally binary's `--help` by `scripts/test_discovery_bridge.py::RequiredSurfacePinnedToRealRallyTests`, so it can never silently drift back to a phantom surface. When adding a rally call, read the binary's real `--help` first; do not assume a command exists.
55
-
56
- **Anti-pattern (silent no-op):**
57
-
58
- ```python
59
- # Never do this — readers' checkpoint_read returns changed: false
60
- from scripts.rally_point.changes import append_change
61
- append_change(channel_dir, record) # forgot bump_revision; record invisible
62
- ```
63
-
64
- Memory citation: `feedback_post_helper_prevents_revision_bump_bug`.
65
-
66
- ---
67
-
68
- ## Trust model (unauthenticated channel; advisory leadership lease)
69
-
70
- **The coordination channel is unauthenticated and trusted-local-peers-only.** `changes.jsonl`, `presence/`, `rally/lead.json`, and the coordination markdown all live under the channel returned by `discovery_bridge.resolve(workdir)` with ordinary user-account file permissions. Any process running as the same local user can append a change record, write a presence file, or claim/transfer the leadership lease. There is no signing, no authentication, and no identity verification — and there should not be: build-loop is a local single-user developer tool, so a cryptographic trust layer would be disproportionate to the threat.
71
-
72
- What this means in practice:
73
-
74
- - **Change-record payloads are untrusted free text.** A buggy or hostile channel writer can put arbitrary text — including prompt-injection content — into a `payload` field. Records flow into orchestrator LLM context via `checkpoint_read` `new_changes[]` and `coordination_status` `new_changes` / `open_escalations`. **Mitigation (SEC-002):** the consume boundary sanitizes every record before surfacing — `scripts/rally_point/checkpoint.sanitize_change_for_surface()` keeps only known structured metadata keys and length-caps every free-text string. The raw `changes.jsonl` log stays immutable and untouched; only the *surfaced projection* is sanitized. Reactions (`dep-change`, `arch-scan-complete`, `soft-claim`) are derived from raw records first, because they read only the structured `kind` field.
75
-
76
- - **The leadership lease is advisory coordination, not access control (SEC-003).** Every mutating call in `scripts/rally_point/leadership.py` (`claim_lead`, `renew_lease`, `transfer_lead`, `relinquish_lead`) trusts a caller-supplied `session_id`. `claim_lead` succeeds for anyone whenever the lease is absent or expired; `renew`/`transfer`/`relinquish` "authorize" only by string-matching `session_id` against the world-readable `lead.json`. Any local process that reads `lead.json` learns the incumbent's `session_id` and can forge a renew, transfer, or relinquish. **The orchestrator MUST NOT gate an irreversible action on a lead claim** — a lead claim answers "who is coordinating" for cooperating peers, not "who is authorized". The proportionate control is observability: every `claim_lead` / `transfer_lead` / `relinquish_lead` emits a stderr audit line (`[rally-point audit] ...`) recording the requesting tool and `run_id`, in addition to the durable `lead-*` record in `changes.jsonl`, so an unexpected lease mutation is visible after the fact.
77
-
78
- Threats this model does NOT cover (out of scope by design): a hostile process running as the same user, a compromised local account, or a multi-tenant host. Those are the operating system's responsibility, not the coordination channel's.
79
-
80
- ---
81
-
82
- ## Evidence boundary (Rally is not a verifier)
83
-
84
- Rally records are peer-authored coordination metadata. They can tell an agent
85
- what another agent claimed, handed off, reviewed, blocked, or released, and they
86
- can point to artifacts worth inspecting. They do not prove the artifact, code,
87
- package, tag, release, or remote state is correct.
88
-
89
- Before making a factual claim about repo or release state, check the authoritative
90
- surface directly:
91
-
92
- - Code and docs: working tree, `git diff`, file contents, and tests.
93
- - Package/version surface: manifests, package tests, dry-run pack/publish output,
94
- and release-surface verifier scripts.
95
- - Remote release state: GitHub/npm/GitHub Packages API or public pages, not a
96
- Rally `release` or `artifact` record.
97
-
98
- A Rally `artifact`, `release`, `resolve`, `review_artifact`, or `next` record is
99
- a routing signal. It may identify what to inspect; it is never the inspection.
100
-
101
- ---
102
-
103
- ## Cheap detection at step boundaries
104
-
105
- **Poll `coordination_status.py` BEFORE any step-boundary decision.** Costs ~100 tokens; prevents stale-state recommendations that cost full plan rewrites (~5K tokens).
106
-
107
- ```bash
108
- python3 scripts/coordination_status.py \
109
- --workdir . \
110
- --session-id <my-session-id> \
111
- --owned-file <path>... \
112
- --coordination-file .build-loop/coordination/<active-coord-file>.md \
113
- --json
114
- ```
115
-
116
- **Always pass `--coordination-file` explicitly.** The default-pick heuristic resolves to safe candidates (active.json pointer → oldest `audit-execution-*.md` → oldest direct markdown), but explicit beats implicit. Fresh handoff stubs are often newer than the run ledger they point at; relying on default-pick has misfired before.
117
-
118
- **Step-boundary triggers (poll BEFORE each):**
119
-
120
- 1. Recommending next steps to the user.
121
- 2. Dispatching a subagent.
122
- 3. Committing (any commit).
123
- 4. Bumping plugin version.
124
- 5. Archiving / deleting files.
125
- 6. Editing a shared / no-touch-zone file.
126
- 7. Transitioning a step from `verification-pending` to `done`.
127
- 8. Declaring a self-recursive runtime-changing stage ready for the next stage
128
- after a dogfood reload checkpoint.
129
-
130
- Between triggers, no polling is needed only when there is no active peer, no
131
- active coord file, and no tool inbox message. When a host is waiting on an
132
- async peer response, has an active peer, or has an inbox message, keep a cheap
133
- watcher running:
134
-
135
- ```bash
136
- python3 scripts/coordination_watch.py --workdir "$PWD" --session-id "$SESSION_ID" --tool "$TOOL_NAME" --interval 5 --jsonl --baseline-current
137
- ```
138
-
139
- For long-running task ownership, write a task heartbeat at task start and at
140
- least every 10 minutes:
141
-
142
- ```bash
143
- python3 scripts/agent_rally.py heartbeat --workdir "$PWD" --session-id "$SESSION_ID" --tool "$TOOL_NAME" --task-ref "$TASK_REF" --progress "still on task" --json
144
- ```
145
-
146
- Then pass `--task-ref "$TASK_REF"` to `status` or `watch`. Presence only says
147
- the session is live; task heartbeat says whether it is still on the claimed
148
- task and when the next check-in is due.
149
-
150
- For self-recursive runtime-changing stages, use
151
- `scripts/dogfood_reload_checkpoint.py` and
152
- `references/dogfood-reload-checkpoint.md`. A Rally handoff or inject is not reload proof.
153
- Each participating terminal must ACK runtime root + commit, or
154
- the live agent must record a fallback (`reassign`, `defer`, or
155
- `continue_solo`) before continuing.
156
-
157
- Use stable tool ids (`claude_code`, `codex`, `cursor`, etc.) so targeted
158
- `inbox/<tool>.jsonl` messages route cleanly. Broadcast messages live in
159
- `inbox/all.jsonl`; every tool's read path includes that file in addition to
160
- its direct inbox. Unread counts are session-ack aware: after reading and acting
161
- on current inbox payloads, run `agent_rally.py ack-inbox --session-id <id>
162
- --tool <tool>` so resolved notes stop appearing as new doorbells. Status
163
- `clear` → proceed; status `warn` → review peer
164
- overlap + dirty files; status `blocked` → resolve unresolved verdicts before
165
- any of the above. Memory citation:
166
- `feedback_poll_channel_at_step_boundaries`, `feedback_script_first_coordination_checks`.
167
-
168
- ## Peer liveness & orphaned lanes (never wait on an idle peer)
169
-
170
- **An interactive CLI peer (Codex, Cursor, a peer Claude terminal) is NOT a daemon.** It acts only within a turn its user prompts, then idles awaiting the next input — it does **not** autonomously poll this channel and resume. A handoff to such a peer therefore executes only when its user next drives that terminal; it may sit unread indefinitely. Do not model a CLI peer as a continuously-running worker.
171
-
172
- **Liveness rule:** treat a peer with no channel activity for **>10 minutes while it owns an open handoff lane** as *idle* (silent ≠ dead, but ≠ progressing). Detect via the peer's last `recorded_at` in `changes.jsonl` vs now; a clean `stop`/`relinquish` also means idle.
173
-
174
- **Orphaned-lane absorption:** when a lane assigned to an idle peer is **local and reversible** (commits, doc/agent edits, dead-code or dead-key trims, version bumps, test updates), the live agent **absorbs it** — does the work itself, then records in the report `absorbed <peer>'s idle lane: <what> [<evidence>]`. Do **not** block a release, a finish, or "done" on an idle peer's local lane — that is the same manufactured wait as a turn-length stop (see `skills/build-loop/SKILL.md` §"Keep going until done"). Only surface/hold a lane that is genuinely **peer-exclusive**: needs the other vendor's model (true cross-vendor review), the peer's environment/credentials, or an irreversible action only that peer is authorized to take. Coordination is cooperative, not a dependency that can deadlock the live agent.
175
-
176
- ## Recency decay & size-scaled lead/ownership auto-reclaim
177
-
178
- A single coordination policy governs message aging and stale-claim reclaim,
179
- mirrored from the canonical Rust implementation (agent-rally-point). Tunables
180
- live under `coordinationPolicy` in `.build-loop/config.json` (defaults shown):
181
-
182
- ```json
183
- {
184
- "coordinationPolicy": {
185
- "half_life_hours": 48,
186
- "archive_floor_weight": 0.05,
187
- "reclaim_small_minutes": 30,
188
- "reclaim_large_minutes": 120
189
- }
190
- }
191
- ```
192
-
193
- - **Recency decay (listing order + archive).** Every coordination change gets a
194
- weight `0.5 ** (age_hours / half_life_hours)` (default half-life 48h). The
195
- status / recent-changes listing orders fresh-first by weight and EXCLUDES any
196
- change whose weight has fallen below the archive floor (default `0.05`, ≈14d).
197
- Archived changes are losslessly retrievable with `--include-archived`
198
- (`coordination_status.py --include-archived`), which also folds back any
199
- physically-rotated `changes.jsonl.<date>` logs. Decay applies only to the
200
- historical change stream — never to the live direct-message inbox or to active
201
- state. Fails OPEN: a change with a malformed `ts` is treated as fresh.
202
- - **Size-scaled lead/ownership auto-reclaim.** A lead lease (`rally/lead.json`)
203
- whose `lease_until` has passed is auto-reclaimable by the next `claim_lead`.
204
- The lease WINDOW scales with the claimed work size: a small (single-file /
205
- effort XS·S) claim expires after `reclaim_small_minutes` (default 30m); a
206
- large (multi-file / coarse / effort M·L·XL) claim after `reclaim_large_minutes`
207
- (default 2h). Pass `work_size`/`effort`/`owns` to `claim_lead`; with NO size
208
- signal the lease window stays the historical `renew_every_minutes` cadence
209
- (backward-compatible). An auto-reclaim posts a durable `lead-reclaim` record
210
- naming who reclaimed, the prior owner, and the reason (`stale-by-timeout`).
211
- - **Preserved invariants.** Reclaim stays race-safe (the `rally/lead.lock`
212
- fcntl lock is untouched) and FAIL-CLOSED: a present incumbent lease whose
213
- `lease_until` is unparseable is NEVER auto-reclaimed (we refuse rather than
214
- reclaim on a timestamp we cannot trust). An empty seat is still freely
215
- claimable.
216
- - **Policy is Rust-only; the Python math is an in-process helper.** Reclaim and
217
- reap DECISIONS are made by the Rust binary (facade in `reaper.py` /
218
- `leadership.py`, fail-loud below full capability). `decay.py` survives only as
219
- pure in-process math the lead-lease window sizing needs (`recency_weight`,
220
- `reclaim_timeout_seconds`, `classify_work_size`); it is NOT a behavioral mirror
221
- of `decay.rs` and is no longer double-pinned against a cross-repo golden
222
- fixture. The status/recent-changes LISTING still applies the decay weight in
223
- Python over build-loop's own change-log so an old on-PATH binary cannot
224
- un-decay the listing (pinned by `scripts/test_coordination_decay_invariant.py`);
225
- that is a presentation concern, distinct from the now-Rust-only reclaim/reap
226
- actuation.
227
-
228
- This complements (does not replace) the >10-minute idle absorption rule above:
229
- idle-absorption handles local reversible lanes a quiet peer left open; the lease
230
- timeout governs the formal lead/ownership role handover.
231
-
232
- ## In-room stale-state reaper (actuator) — RUST-ONLY via a fail-loud facade
233
-
234
- The sections above define WHEN records become stale. Physically removing them
235
- (reaping) is **Rust-only**. There is no Python reaper sweep — the prior Python
236
- parity mirror (presence/claim-index/lead deletion, double-pinned against golden
237
- fixtures) was RETIRED in the Rust-rally facade migration. A Python process
238
- deleting coordination records the Rust binary owns is the exact shadow
239
- implementation that is worse than no coordination.
240
-
241
- **Canonical actuator (Rust):** the `rally` binary's reaper (`rally sessions
242
- --reap`, or `rally doctor --reap-stale` on newer builds). Dry-run by default;
243
- `--reap`/`--apply` physically removes over-TTL presence, claims, and leads.
244
-
245
- **Facade (`scripts/rally_point/reaper.py`).** `reap_channel(channel, workdir,
246
- apply=…)` resolves coordination capability via `discovery_bridge` and then:
247
- - **full capability** (a real binary owns the channel — `repo-local-rally-cli`
248
- / `fetched-binary` / env-override / path-binary / python-import) → shells
249
- `rally sessions --reap` and surfaces the result (`capability_level: full`,
250
- `deferred_to_rust: false`).
251
- - **below full** (degraded-breadcrumb or unavailable) → REFUSES. It reaps
252
- nothing and returns a capability-marked report (`deferred_to_rust: true`,
253
- `coordination_unavailable: <reason>`). A degraded session must never reap a
254
- peer it cannot prove is dead.
255
-
256
- CLI: `python3 scripts/rally_point/reaper.py --workdir <path> [--apply] [--json]`.
257
-
258
- **Capability field on every coordination envelope.** Every facade return and
259
- `DiscoveryEnvelope` carries `capability_level` (`full` / `degraded-breadcrumb` /
260
- `unavailable`) + a `coordination_unavailable` reason. The single source of truth
261
- is `scripts/rally_point/capability.py`; `FULL_ONLY_OPERATIONS`
262
- (claim/release/reclaim/lead/reap/liveness/before_write/checkpoint) are permitted
263
- only at full capability.
264
-
265
- **Degraded breadcrumb path (the ONLY thing a sub-full session may write).** When
266
- no binary is available but the host is supported, a session may write
267
- capability-marked presence/handoff *breadcrumb* facts so a later full-capability
268
- peer (or a human) sees it existed. It must NOT — and structurally cannot — claim
269
- ownership, reclaim, infer liveness, reap, or imply before-write protection.
270
-
271
- **Unsupported-host = loud no-coordination.** A host with no fetchable pinned-
272
- binary asset (Intel macOS, musl/Alpine, exotic arch) resolves to
273
- `capability_level: unavailable` (`coordination_unavailable: unsupported_host`).
274
- The facade is a loud no-op there — NEVER a policy mirror.
275
-
276
- **Remaining Python guards** (the destructive paths that survive as in-process
277
- guards, all FAIL-CLOSED): `presence.reap_stale` physically unlinks only at full
278
- capability; `leadership` reclaim (taking a peer's lease) is full-only, while
279
- seeding an EMPTY lead seat and self-relinquishing one's OWN seat stay breadcrumb-
280
- class. The in-process `decay.py` / `liveness.py` are now pure window/weight math
281
- helpers (lead-lease sizing, adaptive presence window), not behavioral mirrors.
282
-
283
- **Codex parity.** A codex session emits the same presence record claude does, via
284
- the `.codex/hooks.json` `SessionStart` hook that calls `session_probe.py --tool
285
- codex`, so it ages and decays identically — now enforced by the single Rust
286
- reaper both tools share, not by a cross-language golden fixture.
287
-
288
- **Session-end self-release (primary).** Both tool hooks release the agent's Rally
289
- file-claims at turn completion (`Stop` event) so peers immediately see the agent's
290
- absence and the claims do not leak past Stop. The two hosts reach this differently
291
- because their session models differ:
292
-
293
- - **Codex** emits `rally stop codex` (`.codex/hooks.json` Stop). `codex` resolves to
294
- Codex's managed `rally-*` tmux session, so that one call both self-kills the
295
- session (it can never become a detached orphan) AND releases the session's claims.
296
- - **Claude Code** is normally NOT a `rally run`-managed session, so `rally stop
297
- claude_code` fails (`unknown managed session`). The Claude Stop hook therefore
298
- releases claims via the portable primitive instead: `scripts/stop_closeout.py`
299
- (`release_my_claims`, called from `hooks/closeout.sh stop`) enumerates this tool's
300
- open claims with `rally room --tool claude_code --json` and releases each by event
301
- id with `rally say release --tool claude_code --ref <event-id>`. It fires on EVERY
302
- Stop (a stopped session is no longer editing), is advisory + fail-open (`command -v
303
- rally` guarded; swallows all errors; exit 0 always), and is capped per Stop so a
304
- backlog can't make the hook run long. History: before this, the Claude hook
305
- released only the run IDENTITY, never the file claims, so `claude_code` auto-claims
306
- accreted unbounded (112→127 observed). Added 2026-06-29.
307
-
308
- **Reaper = backstop, not primary (NON-DEFAULT).** The reaper that expires abandoned
309
- claims/presence (`rally sessions --reap`, faced by `scripts/rally_point/reaper.py`)
310
- is Rust-only and dry-run/manual by default — it is NOT turned on. Self-release above
311
- is the primary mechanism. The reaper exists ONLY to catch the dead: sessions that
312
- crash (SIGKILL / 529 / network drop) before their Stop hook fires, so self-release
313
- never ran. Do not enable it as a default-on sweep; run it deliberately as the
314
- explicit backstop for crash-orphaned claims.
315
-
316
- ## Adaptive multi-signal liveness (squad-projection decay + tmux orphan reaper)
317
-
318
- Fixed staleness cutoffs are replaced by liveness that ADAPTS to each session's
319
- planned heartbeat cadence and weighs four signals. The liveness DECISION (reap /
320
- self-exit) is Rust-only via the facade; `scripts/rally_point/liveness.py` retains
321
- only the in-process window/verdict MATH the presence squad-projection uses,
322
- verified by its own inline unit tests (`test_liveness.py`). The cross-repo golden
323
- fixture `liveness_vectors.json` and its `_provenance.json` drift entry were
324
- RETIRED in the Rust-rally migration — there is no longer a byte-identical parity
325
- contract to maintain across the two codebases.
326
-
327
- **Adaptive cadence.** A session declares its beat via `planned_heartbeat_secs`
328
- (presence record) or `renew_every_minutes` (lead.json); undeclared → the default
329
- cadence. Staleness is RELATIVE: `window = planned_interval * MISS_MULTIPLIER + GRACE`.
330
- Defaults `DEFAULT_CADENCE_SECS=300`, `MISS_MULTIPLIER=6`, `GRACE_SECS=60` →
331
- a 5-min cadence is stale at ~31 min (≈6 missed beats); a 5-hour cadence not until
332
- ~30 h. Tunable via `.rally/config.json` `coordination{}` (Rust) /
333
- `.build-loop/config.json` `coordinationPolicy{}` (Python):
334
- `default_cadence_secs`, `miss_multiplier`, `grace_secs` (+ `RALLY_*` env in Rust).
335
- Legacy `heartbeat_minutes` is honored as a cadence source for backward compat.
336
-
337
- **Four signals — LIVE if ANY is fresh within the adaptive window:**
338
- (a) heartbeat/presence `last_seen`; (b) inject/ack (a `receipt`/`wake`/`handoff`
339
- naming the session); (c) forward code progress (the session's worktree branch HEAD
340
- MOVED since the last poll — Rust compares the two newest presence facts' shas, the
341
- Python reaper compares a cached `branch_head_sha`); (d) declared active work (a
342
- live claim or authored mission/handoff).
343
-
344
- **Two fail-directions, each on the safe side:**
345
- - **Squad VISIBILITY projection** (Rust `snapshot_from_facts_with_policy`) is
346
- FAIL-OPEN. A squad whose four signals are ALL provably stale is DROPPED from the
347
- default `rally room` view; `--include-archived` restores it (mirrors the message
348
- archive model). A `Live` OR `Unknown` (any absent/unparseable signal) verdict
349
- KEEPS the squad visible — hiding a still-alive peer could cause the very
350
- write-collision this system prevents.
351
- - **Reaper REMOVAL** (presence-file unlink, claim/lead) stays FAIL-CLOSED — never
352
- remove on a signal it cannot trust.
353
-
354
- **tmux orphan reaper.** `rally sessions --reap` also detects DETACHED `rally-*`
355
- tmux sessions whose last activity is past the adaptive window and which are not
356
- tracked as managed sessions, kills them, and tombstones the reap (closing the gap
357
- where `--reap` saw 0 of the real detached orphans). Attached sessions (a human is
358
- looking) are never killed.
359
-
360
- ## Zombie-tmux prevention — three layers over ONE liveness model
361
-
362
- Root cause of accreted zombie tmux sessions: rally `exec`s the agent, so a session
363
- auto-closes when its agent EXITS, but agents that never exit (a disabled autonomy
364
- poller, idle detached panes) leave the session forever — tmux has no native
365
- idle/lifetime timeout. The fix is three layers, all reusing the single
366
- `liveness::is_live` 4-signal model and the adaptive window above. NONE adds a
367
- fixed/brute-force idle clock; lifetime follows real liveness/ownership.
368
-
369
- **Layer 1 — completion-scoped self-exit (prevent at source).** `rally
370
- self-exit-check --tool <self>` is a stateless re-check: a task-scoped session that
371
- holds NO active claims AND for which `rally next` is non-actionable for a SUSTAINED
372
- streak self-kills its own `rally-*` tmux session, so `exec` auto-closes it. The
373
- streak (default 2 consecutive empty re-checks, `liveness::DEFAULT_SELF_EXIT_STREAK`)
374
- is persisted in the session's OWN tmux env (`RALLY_SELFEXIT_STREAK`, dies with the
375
- session — no new filesystem surface) so a brief lull between claims never exits
376
- mid-task. **Opt-out:** `--persistent` short-circuits to "never self-exit" for a
377
- deliberately-long-lived session. The existing `rally stop` self-kill remains the
378
- explicit-completion path; Layer 1 adds the implicit "work done" path. Decision is
379
- the shared `liveness::completion_self_exit_eligible(work_resolved,
380
- next_empty_streak, required_streak, persistent_optout)`.
381
-
382
- **Layer 2 — event-driven liveness-lease safety net.** `rally enter` (a new agent
383
- joining) opportunistically sweeps detached `rally-*` orphan tmux sessions via the
384
- SAME reaper Layer-3 logic, in addition to `rally sessions --reap`. Best-effort and
385
- fail-open: it runs AFTER presence (so the entering agent's own session is in the
386
- guard set), never blocks the enter path, and never raises. A live / parent-alive
387
- session is never reaped. No daemon/cron (those would themselves need worktree
388
- isolation). Both the enter sweep and `sessions --reap` call ONE shared actuator
389
- (`sweep_orphan_tmux`).
390
-
391
- **Layer 3 — parent-lifecycle binding.** At launch (`tmux_start_command`) the new
392
- session's env is stamped with `RALLY_PARENT_PID=<launcher pid>` in the SAME atomic
393
- `tmux new-session -e` call. The reaper reads it back (`show-environment`), probes
394
- `kill -0 <pid>` (no new crate dependency — the repo keeps a zero-extra-dep
395
- contract), and feeds the result to the shared `liveness::reapable(liveness,
396
- parent_alive)`. This targets the exact failure mode here (autonomy poller died →
397
- its child sessions orphaned).
398
-
399
- **The single reaper-eligibility authority** is `liveness::reapable` (mirrored
400
- Rust↔Python, asserted by the byte-identical `liveness_vectors.json` `reapable_cases`):
401
-
402
- | liveness | parent_alive | reapable | rationale |
403
- |----------|--------------|----------|-----------|
404
- | Live | any | NO | any of 4 signals fresh → independently live |
405
- | Unknown | any | NO | fail-closed: untrustworthy signals |
406
- | Stale | alive | NO | stale by signals but a live parent may re-drive it (conservative) |
407
- | Stale | dead | YES | the Layer-3 orphan target |
408
- | Stale | none (no info)| YES | window criterion ALONE — fail-safe degradation |
409
-
410
- **Fail-safe directions (binding):**
411
- - A session making code progress / heartbeating on cadence / recently injected /
412
- holding a live plan is NEVER reaped — that's `Live` → not reapable, regardless of
413
- parent state.
414
- - The control NEVER reaps on the parent criterion ALONE: parent-dead reaps only a
415
- session that is ALSO `Stale` by the 4-signal liveness.
416
- - Missing/unparseable parent info (`parent_alive = None`) degrades to the
417
- liveness-window criterion alone (`Stale → reap`), preserving the pre-Layer-3
418
- orphan-window behavior exactly — never reaped *because* the parent is unknown.
419
- - `kill -0` failing for any reason other than "no such process" (e.g. EPERM) reads
420
- ALIVE (a live-but-unsignalable process is never treated as dead).
421
-
422
- ## Idle-agent self-selection (rally facilitates, the agent decides)
423
-
424
- **Rally is a facilitator, not an orchestrator or verifier.** It exposes room state (`rally room` / `rally next`), file-level deconfliction (`rally check before-write --path P`), and claims/handoffs. It does **not** assign work, pick work, or verify code/release truth. A waiting agent runs this decision tree itself and chooses — the agent's LLM reasons over Rally's surfaced coordination records. This keeps coordination decentralized: no single point that hands out tasks (which would be a failure site and a bottleneck).
425
-
426
- When an agent is idle and `rally next` returns no actionable item, walk the tree top-down, stop at the first match:
427
-
428
- 1. **Pending handoff/inject addressed to me** (by session, name, or tool) → handle it, record the response, then run `ack-inbox`.
429
- 2. **An open blocker I can resolve** → resolve it; post the resolution.
430
- 3. **A no-regret item is free** → pick from the project's no-regret backlog (`.build-loop/followup/`, deferred-but-safe items, the run's recorded follow-ups). For its files, run `rally check before-write --path <each>`; if clear, `claim` them, `say` what you're starting, then do it. Reversible + behavior-preserving + tests-pass only.
431
- 4. **All coding candidates are claimed or conflicted** → do read-only research or assessment that helps and has zero file conflict (simplification scans of untouched areas, duplication/test-gap audits, docs the room needs).
432
- 5. **Nothing fits, or the only work left is risky/deferred/peer-exclusive** → stay idle and say so; do not start risky/deferred work, do not touch another session's claimed paths.
433
-
434
- The tree is the guideline; Rally supplies coordination records (claims, collisions, pending items) each branch needs. Two same-tool agents running it independently land on different work because claim-first + `check before-write` makes the first claimant win and the second re-select — no central referee required.
435
-
436
- ## Coordination reliability (verify the room before trusting it)
437
-
438
- Room resolution can shift under you — a binary update, a repo-keying change, or a worktree path can move which channel you resolve to. Before concluding "no peers" or "empty room", verify it:
439
-
440
- - **Check which channel you actually resolved.** If `rally enter` / `rally room` returns a null or empty channel, your *read* is suspect, not the room. A peer you "can't see" is often in a **different room** (different repo slug, or pre/post a keying change), not absent. Confirm the channel's `repo_root` (in `rally.channel.json`) matches the repo you mean.
441
- - **One repo = one room** (keyed off the canonical repo root, shared by all worktrees); **different repos = different rooms**, correctly. Before declaring a peer missing, confirm you are both keyed to the same repo root — two agents in sibling repos (e.g. `build-loop` vs the spun-out `agent-rally-point`) are *supposed* to be in separate rooms.
442
- - **A lead that posted then went quiet is idle** (same as any interactive CLI peer — see §Peer liveness). Do not block on it; absorb local lanes, leave a relay for its return.
443
- - **Never hand-append the hash-chain channel files** (`changes.jsonl`) to "reach" a peer — corruption risk. If the CLI cannot post, relay out-of-band; do not edit the chain.
444
-
445
- ## Room-policy reconciliation (mission / envelopes vs dispatch brief)
446
-
447
- Named failure (2026-06-09, agent-rally-point): two orchestrator runs hit the same in-room mission guardrail ("No push to origin without Tyrone go") and split — one pushed past it without addressing it; the other held a finished build at push time even though the line was stale (superseded in practice by five operator-approved pushes). The rule below makes the reconciliation explicit and early.
448
-
449
- - **Read room policy at entry.** After `rally enter`/`ack`, read the room mission and this agent's autonomy envelope (`rally mission --json`) and reconcile them against the dispatch brief's authorizations for gated actions (push, deploy, destructive).
450
- - **Surface conflicts at Phase 1, not at push time.** A mission/brief conflict on a gated action is posted on-channel as a decision-needed fact AND returned to the dispatcher immediately — never first discovered after the work is done.
451
- - **Precedence when reconciling:** newer ledger decision facts supersede older mission text; a per-agent autonomy envelope `may` grant covers its named action; an operator-attributed decision fact satisfies a "without <operator> go" guardrail. Operator-attributed means posted by the operator or from an operator-present interactive session — a subagent cannot mint its own go signal by posting a decision fact mid-run.
452
- - **Genuine conflict after checking all three → hold the gated action and surface.** Holding is the correct terminal behavior; the failure mode this rule removes is holding late.
453
-
454
- ---
455
-
456
- ## MECE Packets (briefs require all seven; rally packets six + optional 7th)
457
-
458
- **Every implementation handoff to a peer MUST spell out seven elements: `owns / does-not-own / interface-contract / integration-checkpoint / allowed-tools / denied-tools / acceptance-criteria`. The hard seven-field lint applies to dispatch BRIEFS (`brief_mece_validator.py`); rally `kind=handoff` ownership packets require the six structural fields (`mece_gate.py`) and validate `acceptance_criteria` when present — bootstrap/presence posts are not delegations and may omit it.** Anything less is "informational handoff" — produces drift, two writers on the same file, ambiguous "done" definitions.
459
-
460
- | Element | What it answers | Example |
461
- |---|---|---|
462
- | **Owns** | Which files/scopes may the peer write? | `scripts/coordination_status.py`, `scripts/test_coordination_status.py` |
463
- | **Does not own** | Which files/scopes must the peer NOT touch? | any agent body; any coord-file content |
464
- | **Interface contract** | What shape does the deliverable take? (schema, format, exit code, location) | CLI `--json` returns `{status, latest_verdicts, ...}`; exit 0 clear / 1 warn / 2 blocked |
465
- | **Integration checkpoint** | How does Claude verify the handoff landed and how does it plug back in? | regression test passes; orchestrator parses returned JSON; entry appears in coord file |
466
- | **Allowed tools** | Which tools may the peer use? (empty list = no restriction) | `["Bash", "Read", "Edit"]` or `[]` |
467
- | **Denied tools** | Which tools must the peer NOT use? (empty list = no restriction) | `["WebSearch"]` or `[]` |
468
-
469
- Both `allowed-tools` and `denied-tools` MUST be present on every `kind=handoff` post; either MAY be an empty list. An empty `allowed_tools` is a valid explicit "no lateral limits" declaration — only a missing or non-list field is rejected by `mece_gate.validate_handoff`. These fields are the G2 lateral-limits feature (`feat(rally): tool-level lateral limits on handoff packets`, 2026-05-22).
470
-
471
- **Enforcement:** `python3 scripts/brief_mece_validator.py --brief-file <path> --json`. Exit 0 → all seven present (briefs). Exit 1 → at least one missing; orchestrator surfaces a `[warn]` and may still dispatch (C-FLOW pattern — non-blocking lint). The orchestrator wires this lint into every `Agent(subagent_type=..., ...)` dispatch site for peer-handoff briefs.
472
-
473
- **Carve-out:** pure-read handoffs ("go look at this and tell me what you find") skip MECE. All write-handoffs need all seven. Memory citation: `feedback_handoffs_require_mece_packets`.
474
-
475
- ---
476
-
477
- ## Verification of release surface
478
-
479
- **Verifying a release means checking the release surface end-to-end, not just local files.** Manifest edited locally + test passing locally proves nothing about what shipped. The release surface includes seven checks:
480
-
481
- 1. **Manifests show target version** — every file the manifest test enforces (`.claude-plugin/plugin.json`, `.codex-plugin/plugin.json`, `.claude-plugin/marketplace.json` `metadata.version` + `plugins[name=<plugin>].version` for RossLabs-ecosystem plugins). See [Three-file lockstep](#three-file-lockstep-plugin-manifest) below.
482
- 2. **Manifest test exits 0** — `python3 scripts/test_plugin_manifest.py` (or the plugin's equivalent).
483
- 3. **Local commit log matches expected pattern** — `git log --oneline -1` shows the expected commit message shape and SHA.
484
- 4. **Local tag exists** — `git tag --list <tag>` returns the tag.
485
- 5. **Branch HEAD SHA matches commit SHA** — `git rev-parse <branch>` == commit SHA.
486
- 6. **Remote refs at same SHA** (load-bearing) — `git ls-remote origin <branch> <tag>` shows BOTH refs at the same SHA. Without this, a passing local verification can ship nothing (silent push failure, wrong remote, branch protection block).
487
- 7. **(Optional) Fresh-session load test** — `claude plugin refresh` + cache diff vs canonical returns empty.
488
-
489
- **Enforcement:** `python3 scripts/verify_release_surface.py --version <vN.N.N> --branch <name> --remote origin --json`. Returns structured JSON with per-check pass/fail + evidence; exit 0 if all pass, 1 if any fail. Verifier (Codex, CI, second Claude session) calls this instead of running the seven commands manually. Memory citation: `feedback_verification_checks_release_surface`.
490
-
491
- ### Three-file lockstep (plugin manifest)
492
-
493
- Plugin version bumps in the RossLabs ecosystem update **three** files in lockstep, not two:
494
-
495
- 1. `.claude-plugin/plugin.json` — `version` field.
496
- 2. `.codex-plugin/plugin.json` — `version` field.
497
- 3. `.claude-plugin/marketplace.json` — BOTH `metadata.version` AND `plugins[name=<plugin>].version`.
498
-
499
- `scripts/test_plugin_manifest.py` enforces all three via `VersionShapeTests.test_codex_manifest_matches_plugin_name_and_version` and `test_marketplace_versions_match_plugin`. Two-file bumps fail the marketplace test.
500
-
501
- **The test is the source of truth, not the prose.** Before drafting ANY version-bump brief, run `python3 scripts/test_plugin_manifest.py` first. Read failure messages to enumerate every enforced manifest. Build the file list from the test output, not from a docs paragraph that may undercount. Memory citation: `feedback_three_file_lockstep_plugin_manifest`. See also `skills/plugin-builder/SKILL.md` §"Dual-Host: Shipping to Claude Code AND Codex".
502
-
503
- ---
504
-
505
- ## Closeout hygiene
506
-
507
- **A coordination run is not complete until all live processes, presence records, worktrees, and active coord files are explicitly cleaned up.** Stale heartbeats in the resolved Rally Point channel's `sessions/` directory and locked worktrees under `.build-loop/worktrees/` mislead the next run's peer-detection — Rally Point may report "active peer" for a dead process; `git worktree list` may show locked entries that block branch operations.
508
-
509
- **Phase D closeout protocol (orchestrator runs by default at end of every run):**
510
-
511
- 1. **Reap this run's session presence:** `scripts/rally_point/lifecycle.reap_my_sessions(channel_dir, my_session_id)`.
512
- 2. **Stop watchers:** SIGTERM any `coordination_watch.py --interval N` processes started during the run.
513
- 3. **Collapse branches and worktrees:** merge the winning/validated line(s) to `main` first (solo-on-main runs skip this — work is already on main), then call `scripts/collapse_run.py` as described in `agents/build-orchestrator.md` §"Phase D: Closeout" step 4. That step is the single source of truth for the collapse invocation, ordering, JSON-to-report wiring, and `createdRefs[]` lifecycle status updates.
514
- 4. **Archive the coord file:** `mv .build-loop/coordination/<this-coord-file>.md .build-loop/coordination/archived/`. Not deletion — preserves the durable record while clearing the active queue.
515
- 5. **Optional changes.jsonl rotation:** `scripts/rally_point/lifecycle.rotate_changes_log(channel_dir, max_mb=1, max_entries=500)` rotates when either threshold is exceeded.
516
- 6. **Final post:** `post(kind="phase", payload={"phase": "run-closeout", ...})` signals to channel that this run is done; future readers know to skip its presence/changes when scoping.
517
- 7. **Track in state:** `state.json.runs[N].closeout_status`.
518
-
519
- The protocol is automated, not operator-discipline-dependent. Memory citation: `feedback_close_out_stops_the_watcher`.
520
-
521
- ---
522
-
523
- ## C-FLOW rules
524
-
525
- **C-FLOW/no_ask_to_commit** — Completed, validated, build-loop-authorized work commits automatically; pausing to ask the user whether to commit is a workflow violation. Only push/deploy verdicts of `confirm` or `block` from `autonomy_gate.py` / `deployment_policy.py` stop the loop; routine commit advancement does not require confirmation.
526
-
527
- **C-FLOW/no_ask_at_chunk_boundary** — The phrasing "want me to keep going?" / "should I continue with X next?" at a chunk boundary is a workflow violation when the items are same-shape and same-intent. Referenced from `skills/build-loop/SKILL.md`.
528
-
529
- **C-HEAL/self_heal_safe_issues** — Self-heal is **both reactive and proactive**. **Reactive arm:** when build-loop encounters an error or crash from its own tooling, a hook, a script, a Bash command, or a build/test/lint failure; OR a quality or performance issue surfaced by any Review sub-step, self-review, fact-check, simplify, or efficiency scan — ROOT-CAUSE and FIX it, then continue. Classify via `scripts/classify_action.py`: SAFE → apply, verify (re-run failed action and relevant tests), commit, continue; RISKY → isolate to worktree-branch + log + continue main + surface in report; DECISION/PRODUCTION → surface/escalate. **Proactive arm:** during deep self-review (and any self-recursive build), the self-review/self-heal loop ALSO proactively simplifies build-loop's own code — reducing complexity, splitting oversized files, removing dead/duplicated logic, adding missing tests — driven by `self_review.py`'s `self_simplification[]` findings. The loop MAY author new skills and new scripts (new scripts require a colocated `test_<name>.py`). **MANDATORY SAFETY GATE for self-modifications:** any change to build-loop's own plugin repo or the `build-loop-memory` durable repo MUST pass `python3 scripts/self_mod_verify.py --scope auto --auto-revert` (`verdict: pass`) before commit; on `verdict: fail` the gate auto-reverts and the change is not committed. **Self-modifications execute — they do not stop the loop.** A self-modification that is part of the accepted plan (including edits to the gate, tests, or the self-improvement loop) executes behind the test-suite gate. Build-loop never halts a planned self-modification for human approval. Oversight is post-hoc: (a) self-modifying runs trigger an ADDITIONAL adversarial review (independent-auditor at build scope; the periodic deep self-review re-audits recent self-modifications) — non-blocking; (b) the end-of-run readback reports every self-modification and the additional-review findings. The loop stays on task and reports once, at the end. Structural/architectural self-modifications (new phase, changed contract, agent-role change) surface as DECISION, never auto-apply. Full gate protocol: `skills/build-loop/references/self-review.md` §"Self-modification of the restricted repo". Banned anti-pattern: bypassing a fixable error — `--no-verify`, xfail-ing a test, commenting out failing code, `|| true` on a real failure — when a SAFE root-cause fix exists. Workarounds allowed only when the fix classifies RISKY/DECISION/PRODUCTION or is genuinely infeasible; record both the workaround and the issue.
530
-
531
- **C-RCA/root_cause_before_done** — Before any "done"/completion claim, investigate EVERY open issue — failed tests, loose ends, errors, warnings, minor issues — to ROOT CAUSE; none are left unaddressed. A surface/symptom patch is a violation. Use the debugging skills (`build-loop:debug-loop` / `root-cause-investigator` / `systematic-debugging`) and/or a **5-whys / causal-tree** analysis to determine the true cause AND how far it spans (same root cause affecting other sites → fix all of them). The fix MUST be **verified by another, independent subagent** (confirms root cause correctly identified, fix resolves it, no regression introduced) before "done." Both the investigation-before-done and the second-subagent verification are mandatory; the specific technique is the operator's choice. The second-subagent check reuses existing surfaces (`independent-auditor`, `fix-critique`, or a dispatched verifier) — no new agent. C-RCA pairs with C-HEAL: C-HEAL governs what to do when a SAFE error surfaces (reactive fix + proactive simplification); C-RCA mandates that the root cause is understood, the fix is durable, and a peer has confirmed both before the run closes. It also operationalizes the standing "attack over defense / always the durable fix / fix everything" preferences as a completion gate. The investigation-before-done and second-subagent verification are non-negotiable; the specific technique is the operator's choice. Referenced from `agents/build-orchestrator.md` §"Root cause before done" and `skills/build-loop/SKILL.md` §"Root cause before done".
532
-
533
- ---
534
-
535
- ## Quick-reference cross-index
536
-
537
- | Rule | Canonical implementation |
538
- |---|---|
539
- | Operating rule (verdicts gating) | `scripts/coordination_status.py` `BLOCKING_VERDICTS` constant; coord file Operating Rule section |
540
- | `post()` mandatory | `scripts/rally_point/post.py` (the helper itself) |
541
- | Cheap detection at step boundaries | `scripts/coordination_status.py` + `scripts/coordination_watch.py` |
542
- | MECE packets enforcement | `scripts/brief_mece_validator.py` + `agents/build-orchestrator.md` dispatch wrappers |
543
- | Release-surface verification | `scripts/verify_release_surface.py` |
544
- | Three-file lockstep enforcement | `scripts/test_plugin_manifest.py` `VersionShapeTests` |
545
- | Closeout hygiene | `scripts/rally_point/lifecycle.py` + `scripts/collapse_run.py` + `agents/build-orchestrator.md` Phase D |
546
- | Coord-file shape | `references/coordination-file-template.md` |
547
-
548
- ---
549
-
550
- ## When to update this file
551
-
552
- This is a constitution, not a changelog. Edit only when a coordination rule itself changes — new operating rule, new enforcement script, retired pattern. Single-run details and step status belong in the per-run coordination file under `.build-loop/coordination/<topic>.md`, not here.