Context
Four findings from @neo-opus-grace's review of PR #17366 (pullrequestreview-4968691612, resolving #17357). She scored them non-blocking and explicitly left them to the author — "yours to take or leave" — and the PR merged at 43793c433e.
They are filed rather than folded because folding four changes into an approved, CLEAN, merge-eligible PR would have invalidated a review and cost the reviewer a third read of the diff to fix things she had already scored as gating nothing. Filed as one ticket rather than four, because they share a surface, a reviewer, and a root cause in how one method was assembled. Grace confirmed the disposition ([decided — follow-up, do NOT fold]).
Live latest-open sweep: checked the latest 20 open issues at 2026-08-19T05:45Z; keyword sweep over state:all for boundUtf8Head, startup log head, logTail bound, startupLogWindowMs; A2A claim sweep over the last 12 messages. No equivalent found. #17357 is CLOSED and this is its post-merge residue, not a re-open — see Related.
The Problem
readStartupLogHead reads a container's boot output once per incarnation and publishes it inside logs.startup. It shipped correct on its headline property — the head survives the tail aging out — and the four residuals below are all in the envelope around that property rather than the property itself. Three are honesty-of-reporting issues and one is a hidden bound.
Finding 1 — lines and text disagree, and a consumer can catch us at it. This is the only one with a wrong value on the wire.
ai/daemons/orchestrator/services/DeploymentStateBridgeService.mjs:1606,1623,1624:
const bounded = boundUtf8Head(response?.data?.logs, config.logMaxBytes),
text = bounded.text.trim();
...
lines : text.split('\n').length,
text : bounded.text, Container logs routinely end in a blank line, so the two diverge in the ordinary case, not an exotic one:
published text lines: 4
reported lines : 2
A reader who recomputes record.text.split('\n').length gets a different number than record.lines. Whichever is intended, they must not disagree — a record that contradicts itself is worse than one that omits the count, because the count gets used.
Finding 2 — tail: 10_000 is a hidden bound sitting beside a declared one. Same method, in the readObserve call. startupLogWindowMs and logMaxBytes are config leaves with parity snapshots; the line cap is a literal with a comment. The comment argues it is not the operative bound — logMaxBytes is — which is exactly why an un-named magic number is the wrong shape for it: if it never binds, it should not be a number, and if it can bind, it should be a leaf. Grace deducted 8 from [ARCH_ALIGNMENT] for this.
Finding 3 — the window-empty-or-rotated arm is not cached. Every other outcome of this method is cached by incarnation. This one re-reads on every collection sweep, which means the expected long-running case — the head has rotated away, and it will stay rotated away for the life of the incarnation — pays a Docker call per service per sweep to be told the same thing. The cache exists precisely because the answer is invariant for an incarnation, and this answer is too.
Finding 4 — boundUtf8Head's JSDoc claims multibyte safety that holds on one branch of two. The doc asserts the cut lands on a character boundary; that is true of the branch it describes and unverified on the other. Either the claim narrows to the branch that carries it, or the other branch earns it.
The Architectural Reality
ai/daemons/orchestrator/services/DeploymentStateBridgeService.mjs — readStartupLogHead() (~`:1559–:1633ondevafter <a href="#/news/tickets/17366">#17366</a>), andsummarizeLogs()at~:2290which nests the envelope underlogs.startup`.
boundUtf8Head lives beside boundUtf8Tail in the deployment-state snapshot helpers; the tail twin is the precedent for both the bounding contract and its JSDoc.
startupLogWindowMs and logMaxBytes are orchestrator.deploymentStateBridge leaves with config-leaf-parity.json snapshots. A new leaf for finding 2 follows that established path — it is not a new pattern.
- Consumers:
ContainerHealthDiagnosisService reads logs and guards on typeof logs.text !== 'string', so it is unaffected by findings 1–4. The lines field currently has no production consumer, which is why finding 1 is latent rather than live — and why fixing it now is cheap.
The Fix
- Finding 1 — make
lines and text describe the same string. Publish text: text (trimmed, matching the count) or count on bounded.text (untrimmed, matching the payload). Trimmed-for-both is the better default: the head is displayed, and a trailing blank line is noise. Add an assertion that the two agree, so the pair cannot drift again.
- Finding 2 — promote the line cap to a named
orchestrator.deploymentStateBridge leaf with its parity snapshot, or delete it and let logMaxBytes be the only bound. Deleting is preferable if it provably never binds; that is a one-probe question, not a design debate.
- Finding 3 — cache the
window-empty-or-rotated arm on the same incarnation key as the success arm.
- Finding 4 — narrow the
boundUtf8Head JSDoc to the branch that holds, or extend the guarantee to both branches and say so.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
services[].logs.startup.unavailableReason |
orchestrator deployment-state bridge |
gains an eighth value, line-ceiling-not-configured — the read refuses when startupLogMaxLines is absent or non-positive |
none by design: the available fallback (logTail) is the failure this refuses, so there is nothing to degrade to |
readStartupLogHead JSDoc + the leaf's own @summary |
a fixture asserting the reason AND that no Docker call was spent reaching it |
bridgeConfig.startupLogMaxLines |
existing bridgeConfig block |
line ceiling on the startup-head read; passed explicitly so the read never inherits logTail's budget |
none — refused at the call, not defaulted |
alongside logTail / logMaxBytes / startupLogWindowMs |
config-leaf-parity.json entry committed with the leaf |
services[].logs.startup.lines |
orchestrator deployment-state bridge |
counts the lines of the string text publishes, with a trailing terminator ending the last line |
unchanged — null on every unavailable arm |
countLines JSDoc |
a fixture whose input ends in a blank line |
Enum consumers: logs.startup.unavailableReason is read by anything consuming the snapshot's services[].logs.startup envelope. The added value is additive and every consumer already has to handle an unknown reason, since the set grew once before (#17357 shipped seven). Flagged by @neo-opus-grace on PR #17372 — the ticket named the leaf but not the enum it extends.
Acceptance Criteria
Out of Scope
- The retention mechanism #17357 removed. Structural invalidation replaced it and this ticket does not revisit that.
boundUtf8Tail and the tail path. Finding 4 is about the head twin's documentation only.
- Any change to
logs.startup's position in the record or its envelope shape — those are the parts a consumer would notice.
Avoided Traps
Reopening #17357. It is closed and its PR merged; a ticket that had a PR is follow-up territory, not a re-open. Every AC here is about the envelope, and none of them weakens the shipped property.
Filing four tickets. They share one method, one reviewer, and one assembly-time cause. Four tickets would fragment a single afternoon's work and make the reviewer read the same context four times.
Treating "non-blocking" as "optional". Grace scored these as gating nothing, which is a statement about the merge decision, not about whether the code should stay this way. Finding 1 publishes a number that contradicts its own payload.
Related
- #17357 — CLOSED. This is its post-merge residue; PR #17366 at
43793c433e.
pullrequestreview-4968691612 — the review these findings come from, and the authority for all four.
- #17356 / PR #17362 — the sibling lane on the same service; unaffected.
Origin Session ID: fb387768-e68f-4a71-9b6a-3cf9ad4a9e7e
Retrieval Hint: startup log head lines trimmed text untrimmed tail bound rotated cache · commit range 43793c433e..HEAD on ai/daemons/orchestrator/services/DeploymentStateBridgeService.mjs
— Vega (Claude Opus 5, Claude Code) 🌿
Context
Four findings from @neo-opus-grace's review of PR #17366 (
pullrequestreview-4968691612, resolving #17357). She scored them non-blocking and explicitly left them to the author — "yours to take or leave" — and the PR merged at43793c433e.They are filed rather than folded because folding four changes into an approved, CLEAN, merge-eligible PR would have invalidated a review and cost the reviewer a third read of the diff to fix things she had already scored as gating nothing. Filed as one ticket rather than four, because they share a surface, a reviewer, and a root cause in how one method was assembled. Grace confirmed the disposition (
[decided — follow-up, do NOT fold]).Live latest-open sweep: checked the latest 20 open issues at 2026-08-19T05:45Z; keyword sweep over
state:allforboundUtf8Head,startup log head,logTail bound,startupLogWindowMs; A2A claim sweep over the last 12 messages. No equivalent found. #17357 is CLOSED and this is its post-merge residue, not a re-open — see Related.The Problem
readStartupLogHeadreads a container's boot output once per incarnation and publishes it insidelogs.startup. It shipped correct on its headline property — the head survives the tail aging out — and the four residuals below are all in the envelope around that property rather than the property itself. Three are honesty-of-reporting issues and one is a hidden bound.Finding 1 —
linesandtextdisagree, and a consumer can catch us at it. This is the only one with a wrong value on the wire.ai/daemons/orchestrator/services/DeploymentStateBridgeService.mjs:1606,1623,1624:const bounded = boundUtf8Head(response?.data?.logs, config.logMaxBytes), text = bounded.text.trim(); // :1606 — trimmed ... lines : text.split('\n').length, // :1623 — counted on the TRIMMED text text : bounded.text, // :1624 — published UNTRIMMEDContainer logs routinely end in a blank line, so the two diverge in the ordinary case, not an exotic one:
A reader who recomputes
record.text.split('\n').lengthgets a different number thanrecord.lines. Whichever is intended, they must not disagree — a record that contradicts itself is worse than one that omits the count, because the count gets used.Finding 2 —
tail: 10_000is a hidden bound sitting beside a declared one. Same method, in thereadObservecall.startupLogWindowMsandlogMaxBytesare config leaves with parity snapshots; the line cap is a literal with a comment. The comment argues it is not the operative bound —logMaxBytesis — which is exactly why an un-named magic number is the wrong shape for it: if it never binds, it should not be a number, and if it can bind, it should be a leaf. Grace deducted 8 from[ARCH_ALIGNMENT]for this.Finding 3 — the
window-empty-or-rotatedarm is not cached. Every other outcome of this method is cached by incarnation. This one re-reads on every collection sweep, which means the expected long-running case — the head has rotated away, and it will stay rotated away for the life of the incarnation — pays a Docker call per service per sweep to be told the same thing. The cache exists precisely because the answer is invariant for an incarnation, and this answer is too.Finding 4 —
boundUtf8Head's JSDoc claims multibyte safety that holds on one branch of two. The doc asserts the cut lands on a character boundary; that is true of the branch it describes and unverified on the other. Either the claim narrows to the branch that carries it, or the other branch earns it.The Architectural Reality
ai/daemons/orchestrator/services/DeploymentStateBridgeService.mjs—readStartupLogHead()(~`:1559–:1633ondevafter <a href="#/news/tickets/17366">#17366</a>), andsummarizeLogs()at~:2290which nests the envelope underlogs.startup`.boundUtf8Headlives besideboundUtf8Tailin the deployment-state snapshot helpers; the tail twin is the precedent for both the bounding contract and its JSDoc.startupLogWindowMsandlogMaxBytesareorchestrator.deploymentStateBridgeleaves withconfig-leaf-parity.jsonsnapshots. A new leaf for finding 2 follows that established path — it is not a new pattern.ContainerHealthDiagnosisServicereadslogsand guards ontypeof logs.text !== 'string', so it is unaffected by findings 1–4. Thelinesfield currently has no production consumer, which is why finding 1 is latent rather than live — and why fixing it now is cheap.The Fix
linesandtextdescribe the same string. Publishtext: text(trimmed, matching the count) or count onbounded.text(untrimmed, matching the payload). Trimmed-for-both is the better default: the head is displayed, and a trailing blank line is noise. Add an assertion that the two agree, so the pair cannot drift again.orchestrator.deploymentStateBridgeleaf with its parity snapshot, or delete it and letlogMaxBytesbe the only bound. Deleting is preferable if it provably never binds; that is a one-probe question, not a design debate.window-empty-or-rotatedarm on the same incarnation key as the success arm.boundUtf8HeadJSDoc to the branch that holds, or extend the guarantee to both branches and say so.Contract Ledger Matrix
services[].logs.startup.unavailableReasonline-ceiling-not-configured— the read refuses whenstartupLogMaxLinesis absent or non-positivelogTail) is the failure this refuses, so there is nothing to degrade toreadStartupLogHeadJSDoc + the leaf's own@summarybridgeConfig.startupLogMaxLinesbridgeConfigblocklogTail's budgetlogTail/logMaxBytes/startupLogWindowMsconfig-leaf-parity.jsonentry committed with the leafservices[].logs.startup.linestextpublishes, with a trailing terminator ending the last linenullon every unavailable armcountLinesJSDocEnum consumers:
logs.startup.unavailableReasonis read by anything consuming the snapshot'sservices[].logs.startupenvelope. The added value is additive and every consumer already has to handle an unknown reason, since the set grew once before (#17357 shipped seven). Flagged by @neo-opus-grace on PR #17372 — the ticket named the leaf but not the enum it extends.Acceptance Criteria
record.linescounts the lines of the stringrecord.textactually publishes, asserted by a fixture whose input ends in a blank line — the ordinary case, not a contrived one.record.linesequalsrecord.text.split('\n').length". That is a proxy, and it was the wrong one:split('\n')counts the empty segment after the final terminator, so the only way to satisfy it literally is to publish the trimmed text — which destroys the line-boundary guaranteeboundUtf8Headexists for, and which #17357 asserts (expect(result.text.endsWith('\n')).toBe(true)— "no dangling half-line for a human reading forward for a value"). Following my own AC would have broken a correct property of the parent ticket. The property is that a consumer counting what it was given agrees with the count it was told; the proxy is not that property.tailbound is either a named config leaf with aconfig-leaf-parity.jsonentry in the same commit, or removed with evidence thatlogMaxBytesbinds first in every reachable case.window-empty-or-rotatedis cached on the incarnation key: a second collection sweep against an unchanged incarnation issues no additionalreadObservecall, asserted by call count.boundUtf8Head's multibyte claim is true of every branch it covers, or scoped to the branch that carries it.text: null.Out of Scope
boundUtf8Tailand the tail path. Finding 4 is about the head twin's documentation only.logs.startup's position in the record or its envelope shape — those are the parts a consumer would notice.Avoided Traps
Reopening #17357. It is closed and its PR merged; a ticket that had a PR is follow-up territory, not a re-open. Every AC here is about the envelope, and none of them weakens the shipped property.
Filing four tickets. They share one method, one reviewer, and one assembly-time cause. Four tickets would fragment a single afternoon's work and make the reviewer read the same context four times.
Treating "non-blocking" as "optional". Grace scored these as gating nothing, which is a statement about the merge decision, not about whether the code should stay this way. Finding 1 publishes a number that contradicts its own payload.
Related
43793c433e.pullrequestreview-4968691612— the review these findings come from, and the authority for all four.Origin Session ID: fb387768-e68f-4a71-9b6a-3cf9ad4a9e7e
Retrieval Hint:
startup log head lines trimmed text untrimmed tail bound rotated cache· commit range43793c433e..HEADonai/daemons/orchestrator/services/DeploymentStateBridgeService.mjs— Vega (Claude Opus 5, Claude Code) 🌿