feat(tui): per-path concurrency limit + /health observability (PR-B)#139
Merged
Merged
Conversation
Two P1 audit fixes for the TUI subscription-pool bridge (ADR 0007). The
default path (CLAUDE_TUI_MODE unset) is unchanged except the additive
/health `tui` block (enabled:false when off).
C-4 — TUI path had NO concurrency bound. The global MAX_CONCURRENT gate
lives in spawnClaudeProcess (the -p/stream-json path); callClaudeTui never
calls it — it calls runTuiTurn, which cold-boots a full interactive claude
in tmux. So N concurrent TUI requests spawned N simultaneous cold boots (a
family burst of 5 on a Pi 4 = OOM risk + subscription rate-limit pressure).
Adds an independent in-process limiter (lib/tui/semaphore.mjs, TuiSemaphore)
gating callClaudeTui: OCP_TUI_MAX_CONCURRENT (default 2 — a TUI turn is heavy:
per-request cold-boot + up to 120s wallclock). Queues rather than rejects
(mirrors MAX_CONCURRENT intent), with a bounded wait queue (default 32x the
limit) → tui_queue_full (503) on overflow rather than unbounded growth. The
slot releases in a finally, so PR-A's honesty-gate throws / timeouts / paste
failures never leak a slot.
C-5 — no operator-visible TUI drift surface. The tui_entrypoint_mismatch
warning only reached journald; after the 6/15 flip a silent sdk-cli drift
(the documented top risk) would drain metered credits invisibly. Adds an
additive `tui` block to /health: { enabled, entrypointMode, lastEntrypoint,
entrypointMismatches, inflight, queued, maxConcurrent }. lastEntrypoint /
entrypointMismatches are recorded in callClaudeTui (same mismatch branch the
journald warning covers); inflight/queued come from the C-4 semaphore.
ALIGNMENT (Class B): cli.js does NOT perform this operation — both the TUI
path and /health are OCP-owned, so no cli.js citation applies. /health is a
grandfathered B.2 endpoint (ADR 0006, frozen at v3.16.4). The response-shape
change is authorized by the ADR 0007 PR-B amendment added in this commit and
is behaviour-preserving: the `tui` block is NEW fields only — no existing
/health field is changed, renamed, removed, or re-typed, and no existing
semantics change, so existing consumers (dashboard, ocp-connect, monitoring)
are unaffected. Per ALIGNMENT.md's grandfather provision, an additive
behaviour-preserving change to a grandfathered B.2 endpoint is authorized by
an ADR; ADR 0007 is the authority for the TUI observability surface. No
Class A forwarding path, no alignment.yml, no models.json touched — alignment
blacklist is unaffected (zero new network tokens).
Tests: 11 new (lib/tui/semaphore.mjs is importable, so the semaphore + the
two pure /health helpers are tested directly): limit=1 serializes two
overlapping calls; limit=2 runs two + queues the third; slot released on
throw; bounded queue → tui_queue_full; mismatch counter increments on
cli→other drift; auto mode never counts a mismatch; /health tui block shape
+ live inflight/queued. npm test: 235 passed, 0 failed (was 224).
Co-Authored-By: Claude <claude-opus> <noreply@anthropic.com>
dtzp555-max
added a commit
that referenced
this pull request
Jun 10, 2026
… (PR-A/B/C, #137-139) (#140) Bump 3.19.0 → 3.20.0 + CHANGELOG. Aggregates three already-reviewed, already-merged PRs: - #137 (PR-A) TUI honesty/cache correctness - #139 (PR-B) TUI concurrency limit + /health observability - #138 (PR-C) 6/15 canary + flip/rollback runbooks + setup auth-probe guard README env-var / endpoint tables were updated in the respective feature PRs. Co-authored-by: dtzp555 <dtzp555@gmail.com> Co-authored-by: Claude <claude-fable-5> <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR-B — TUI-path concurrency limit +
/healthobservability (audit findings C-4/C-5).OCP_TUI_MAX_CONCURRENT(default 2) bounds concurrent interactiveclaudeboots via a queuing semaphore (lib/tui/semaphore.mjs). The slot is released in afinally, so PR-A's honesty-gate throws (tui_wallclock_truncated/tui_upstream_error) andrunTuiTurn's throws (tui_spawn_failed/tui_paste_not_landed) never leak a slot. Bounded wait-queue →tui_queue_full(503) on overflow. Independent of the globalMAX_CONCURRENT(8) — a TUI turn is a per-request cold-boot of tmux+claude + up to 120s wallclock, far heavier than a stream-json spawn./healthtuiblock (enabled/entrypointMode/lastEntrypoint/entrypointMismatches/inflight/maxConcurrent) so an operator can poll for the silentsdk-climetered-pool drift (the audit's top risk) instead of grepping journald. Every pre-existing/healthfield is unchanged.ALIGNMENT
Class B. cli.js does not perform this operation (TUI is an OCP-owned subscription-pool bridge).
/healthis a grandfathered B.2 endpoint (ADR 0006); the additivetuiblock is authorized by the ADR 0007 PR-B amendment under ALIGNMENT.md's grandfather provision ("a behaviour-preserving refactor PR or its own ADR") — every pre-existing field is byte-identical, only the newtuikey is appended (behaviour-preserving for existing consumers). No Class A path /.github/workflows/alignment.yml/models.jsontouched.Tests
npm test→ 235 passed, 0 failed (was 224; +11: semaphore bounding/serialize/queue-full/slot-release-on-throw, mismatch-counter semantics incl. null-and-auto-mode handling,/healthtui block shape).Independent review (Iron Rule 10)
Fresh-context reviewer APPROVED: ran
npm test(235/0); diffed old-vs-new/healthto confirm the change is additive/behaviour-preserving; ruled the ADR-0007-amendment authorization valid (not a new-ADR case — no new endpoint or method; the grandfather provision's "its own ADR" path is satisfied, and the additive change independently qualifies as behaviour-preserving); independently probed the semaphore (limit=1 serializes overlapping calls; 5 sequential throws →inflight===0, slot reusable); confirmed no Class A / workflow / models.json touched. Author (dtzp555) did not self-approve.Non-blocking note from review: ADR 0006 is still "Proposed" status (its grandfather text is operative regardless); maintainer may reconcile separately — out of scope for this PR.
🤖 Generated with Claude Code