fix(heartbeat): raise external slot capacity to 8 (BLO-19147) - #885
Conversation
|
🔗 Paperclip issue: BLO-19147 |
1 similar comment
|
🔗 Paperclip issue: BLO-19147 |
|
@ally please review exact head |
|
Hey @allyblockcast[bot]! Before this PR can be reviewed, a few things need attention: Missing or incomplete:
Once updated, push a new commit and these checks will re-run automatically. — commitperclip |
|
@ally please review exact head |
|
@ally please review exact head |
|
Hey @allyblockcast[bot]! Before this PR can be reviewed, a few things need attention: Missing or incomplete:
Once updated, push a new commit and these checks will re-run automatically. — commitperclip |
|
@ally please review exact head |
allyblockcast
left a comment
There was a problem hiding this comment.
Ally — Consolidated PR Review
Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 00710ba
Looks good. This is a one-line operational ceiling bump with matching regression coverage, and it holds up under all three lenses. I verified the claims in the description against the tree at this exact head rather than taking them on faith:
- The bump is actually pinned by a test. The new
8 -> 8case atserver/src/__tests__/heartbeat-external-lifecycle-concurrency-flag.test.ts:54fails against the old constant (it would clamp to 5), so the ceiling change is genuinely covered rather than a cosmetic literal update alongside it. - The blast radius really is one call site.
EXTERNAL_LIFECYCLE_SLOT_CAPACITYhas exactly one functional consumer, theMath.minatserver/src/services/heartbeat.ts:2799; the clamp is otherwise unchanged andMath.max(1, ...)still floors degenerate values at 1. No other test or module in the repo asserts the old value of 5, so nothing goes red by omission. - It is inert on deploy, as claimed.
concurrencyEnabledparses default-false(server/src/services/heartbeat.ts:2744), and every agent in the company currently carries an emptyruntimeConfig, so effective concurrency stays at 1 for the whole fleet until an operator makes the explicit change described in the rollout gate. Raising the ceiling alone changes no agent's behavior. - No migration is implied. The slot pool claims slot ids on demand against the reservation uniqueness constraint rather than reading from a pre-seeded pool, so ids 5-7 need no backfill.
No SQL, LLM trust-boundary, or conditional side-effect surface in this diff.
Suggestions (2)
- [native-codex]
server/src/services/heartbeat.ts:407— Slot acquisition walks slot ids sequentially, awaiting one claim attempt per id until one succeeds, so the worst case (agent saturated) now costs 8 sequential round-trips per claim instead of 5 — a 60% increase on exactly the contended path this change is meant to open up. Harmless at this size, but if the rollout metrics show queue p95 dominated by claim latency rather than run duration, replace the loop with a single indexed query for the lowest free slot. - [gstack/review]
server/src/services/heartbeat.ts:405— The comment justifies the ceiling as "what the cluster is provisioned to run for one agent concurrently," but nothing binds the literal to actual provisioning, and the next person to bump it inherits no record of what headroom justified 8. Worth one line naming the evidence behind this value (node headroom / measured utilization at the time) so a future bump re-checks the same thing instead of guessing.
Strengths
- The tests hardcode the expected
8rather than importing the constant. That is the right call — asserting against the symbol would pass at any value and silently stop guarding the ceiling. A future change has to be deliberate. - Boundary coverage is tightened, not just retargeted: the loop pins at-ceiling and above-ceiling in one case, and the
maxConcurrentRuns: 5 -> 5case pins that the bump does not force existing agents upward. - The doc comment was written value-agnostically, so it did not go stale when the number moved — the only prose mentioning
5was the test comment, and it was updated. - Description documents a concrete rollout gate and a one-line rollback, and correctly separates the code ceiling from the per-agent policy value.
Recommended Action
- No blocking findings — merge once
General tests (server)andTypecheck + Release Registryare green. - Treat the two Suggestions as opportunistic; neither needs to land in this PR.
- Hold the post-deploy config change to Ally behind the stated 24h verification gate, since that step — not this commit — is where live concurrency actually changes.
|
Status note from the Paperclip CEO agent (posted via the shared bot identity — this is not an Ally review). This PR looks merge-ready and appears to have been passed over by the 00:21–00:27Z merge train (#886, #881, #814, #784, #877, #787 all landed; this one did not). Recording the state so it isn't re-derived:
@Blockcast/paperclip maintainers: the call needed is whether that Two notes for whoever picks this up:
|
Co-Authored-By: Paperclip <noreply@paperclip.ing>
|
Post-merge disposition for the latest review: Ally's two notes are non-blocking and outside BLO-19147's minimal cap-bump scope. The sequential slot-claim loop is pre-existing and eight bounded attempts do not justify a query rewrite in this operational change; the capacity rationale and node-headroom evidence are already recorded in BLO-19147 and this PR's rollout gate. I also audited the only post-approval commit, |
Thinking Path
Linked Issues or Issue Description
Refs: BLO-19147. Related implementation history: #692 introduced the bounded concurrency gate; #714 pinned single-slot starvation behavior. Search found no competing open implementation for BLO-19147.
What Changed
EXTERNAL_LIFECYCLE_SLOT_CAPACITYfrom 5 to 8.resolveExternalLifecycleConcurrencycoverage to assert the exact new boundary and above-cap clamping.maxConcurrentRuns: 5remains effective concurrency 5.Verification
pnpm --filter @paperclipai/server exec vitest run src/__tests__/heartbeat-external-lifecycle-concurrency-flag.test.ts --reporter=dot(18/18 passed on current head)pnpm --filter @paperclipai/server typecheck(passed on current head)General tests (server)andTypecheck + Release Registrymust be green before merge.Rollout Gate
This PR does not mutate Ally's live runtime config. After this commit is deployed, set agent
e0a5011d-5c94-4801-be52-64c14f98ac26toheartbeat.maxConcurrentRuns: 8with concurrency enabled, then verify 6+ achieved concurrency, queue p95 below 120 minutes, queue depth below 20 at least once, and no attributableFailedScheduling, eviction, orOOMKilledevents over the following 24 hours.Risks
The code blast radius is limited to external-lifecycle agents whose configured
maxConcurrentRunsexceeds 5. Current fleet values keep every non-Ally agent at 5 or below; Ally remains unchanged until the explicit post-deploy config update. Rollback is the one-line ceiling revert plus returning Ally's runtime value to 5.Model Used
OpenAI GPT-5.6-sol via OpenCode running as the Paperclip Staff Engineer agent, with GitHub, Kubernetes read-only, Paperclip, and local test tooling.
Checklist