fix(desktop): preserve Runtime Host exit diagnostics - #3344

Merged
Astro-Han merged 1 commit into
apache:mainfrom
HuYellow:fix/3333-runtime-host-exit-diagnostics
Aug 23, 2026
Merged

fix(desktop): preserve Runtime Host exit diagnostics#3344
Astro-Han merged 1 commit into
apache:mainfrom
HuYellow:fix/3333-runtime-host-exit-diagnostics

Conversation

@HuYellow

@HuYellowHuYellow commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

The detached local Runtime Host was launched with ignored stdio, so after its
transport reached EOF Desktop had no process exit code, signal, or Host stderr
to include in the diagnostic report.

This change captures a bounded 4 KiB stderr tail and exit status while
preserving detached-process behavior, associates that evidence only with the
spawned PID selected by the final Host registration, and records a redacted
diagnostic in both the main-process log and a dedicated bounded process-exit
buffer. Copied Desktop diagnostics retain the last four local Host exits even
when repeated transport errors fill the ordinary log buffer and the Host is no
longer available.

Fixes#3333

Verification

  • npx biome check <13 changed files>
  • npm run build
  • npm run typecheck
  • git diff --check
  • 4 targeted Runtime Host tests passed, covering selected-PID association,
    detached stderr after launcher exit, bounded stderr, and the exact 4096-byte
    boundary
  • 36 targeted Desktop tests passed, covering stderr redaction and process-exit
    evidence in copied diagnostics

The full Desktop suite was also run on Windows: 955 tests passed, 17 unrelated
existing environment-dependent tests failed, and 7 were skipped. The failures
were Windows symlink EPERM, SQLite cleanup EBUSY, and Rive CLI fixture
failures. All tests added or changed by this PR pass.

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: OpenAI Codex assisted with root-cause analysis,
implementation, regression tests, local verification, and PR drafting. The
contributor will review the final diff and accepts responsibility for the contribution.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Lint, formatting, typechecks, builds, and targeted affected tests pass. The
full Desktop suite has the unrelated Windows failures listed under Verification.

Does this PR entail a change in behavior?

  • Yes - described under Summary above
  • No

@HuYellow
HuYellow marked this pull request as ready for review August 20, 2026 16:34

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Codex review on exact head 40ecd476303d230f601119b1fb2a33f6a134e446 found no actionable code issue.

The bug still exists on current main: detached Runtime Host candidates use ignored stdio, so after transport EOF Desktop cannot report the selected child’s exit code, signal, or stderr. This revision keeps the evidence bounded (4 KiB stderr tail and four Desktop exit records), associates it only when the final registration PID matches a process spawned by this election, redacts before logging/persistence, and keeps detached lifecycle handles unreferenced. The startup-failure path still derives from the same exit evidence, and the process map is election-local rather than long-lived.

The required test check is green. The optional Windows package failure is a renderer CDP smoke timeout; a contemporaneous documentation-only PR failed with the same Packaged Maka renderer did not expose CDP signature, while this run’s earlier Windows package/installer smokes passed. I therefore do not attribute that failure to this diff, though a rerun would provide cleaner evidence.

Required conclusions:

  1. Optimal for the actual problem: yes.
  2. Production code to delete: none identified.
  3. Tests to delete/replace: none identified.
  4. Deeper refactor: no; launcher exit evidence and Desktop presentation remain at the correct boundary.
  5. Ready to merge: no; independent human review is still missing.
  6. Residual risk/gaps: rerun the non-required Windows package smoke; exact-head human judgment remains the merge gate.

This changes user-visible diagnostics and detached-process observability, so independent human review is required under CONTRIBUTING.md. No security, licensing, governance, or public-contract effect was identified.

@HuYellow

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review. I’ll keep the current head unchanged since the required test check is green and no actionable issue was found. A rerun of the optional Windows package check would be appreciated. This PR is ready for independent human review on 40ecd47.

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — this closes a real gap. stdio: 'ignore' meant that the one moment you most need the Host's own words, after its transport hits EOF, was exactly the moment they were guaranteed to be gone. Associating the evidence with the PID the final registration actually selected, rather than with whichever candidate happened to lose, is the detail that makes the report trustworthy, and the dedicated bounded buffer means repeated transport errors cannot push the exits out of the ordinary log.

No [P0]/[P1]. Two [P2]s and two [P3]s below. Both [P2]s are questions rather than defects I can demonstrate — I could not settle either from the diff alone, and in both cases the thing I want is a sentence in the code saying which behaviour was intended.

The one I would most like an answer on is the detached case. stdio for a detached Host changes from 'ignore' to 'pipe', which means the Host's stderr is now connected to a parent that is designed to exit first. 'ignore' pointed at /dev/null and was safe for the entire life of the Host no matter what Desktop did. A pipe is not: once Desktop is gone, the read end is closed, and what happens to the Host's next stderr write depends on details I would rather see pinned down than inferred.

Review assisted by AI (Claude Opus 5). Findings were verified against the files at this head; the reviewer is accountable for them.

Comment threadpackages/runtime-host/src/client/launcher.ts
Comment threadapps/desktop/src/main/runtime-host-desktop-candidate.ts Outdated
Comment threadapps/desktop/src/main/runtime-host-desktop-candidate.ts Outdated
Comment threadpackages/runtime-host/src/client/launcher.ts Outdated
@HuYellow
HuYellowforce-pushed the fix/3333-runtime-host-exit-diagnostics branch from 40ecd47 to 583d62fCompareAugust 22, 2026 15:07
@HuYellow

Copy link
Copy Markdown
ContributorAuthor

Addressed all four review threads on exact head 583d62f7 and resolved the conversations:

  • guarded the detached Host's stderr pipe against the expected EPIPE after its launcher exits, with a survival regression;
  • documented why whole-text and token redaction are both required, with an embedded compact-JSON regression;
  • omitted the stderr/truncation section for a blank trimmed tail;
  • fixed the exact 4096-byte boundary so a complete tail is not reported as truncated.

The branch is rebased onto current main with no conflicts. Local verification is green: full build and typecheck, Biome on all 13 changed files, git diff --check, 4 targeted Runtime Host tests, and 36 targeted Desktop tests.

@Astro-Han, I have re-requested review for this exact head. Could a maintainer also approve the two workflows currently awaiting approval so the required test check can run?

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — you answered all four points from the previous round, and this comment exists mostly to confirm that on the record. Re-reviewed at exact head 583d62f7408805e0ac820249810ff58b92ef7454 against base 4acfa26934ce4b2b385b76f8a11048bb83fab861. None of the six existing reviews are bound to this head, so everything below was re-checked against the current code rather than carried forward.

Each of our four items, verified here:

  • [P2] detached stderr pipe lifetime — fixed.launcher.ts now spawns with ['ignore','ignore','pipe'] behind the MAKA_RUNTIME_HOST_STDERR_PIPE opt-in, and the EPIPE guard in process-diagnostics.ts swallows only EPIPE and rethrows everything else. That distinction matters and it is drawn correctly — a blanket catch here would have hidden the next real write failure. The detached-survival regression covers it.
  • [P2] redactSecrets two-pass semantics — resolved by explanation, which was the right resolution. The comment now states what each pass is for: full-text pattern matching, then per-token structured redaction to catch compact JSON the first pass steps over. The ambiguity was in the reader's head, not the code, so documenting it was the proportionate fix.
  • [P3] orphaned truncation marker on empty stderr — fixed. Empty stderr returns status-only before the marker is attached, with a whitespace-only regression alongside it.
  • [P3] off-by-one at exactly 4096 — fixed.chunk.length > MAX rather than >=, with a regression pinning the exact boundary.

Also checked, and sound:observeCandidateExit listens on 'close' rather than 'exit', so the stderr tail is complete before the record is built — using 'exit' would have truncated exactly the evidence this PR exists to preserve. Diagnostics associate with the finally-registered PID, so a connect-or-spawn race attributes output to the process that actually won. The log buffer is bounded at 16KiB across four entries.

[P3, ordinary path] The survival regression has a read race in the fixture, not in the product.

stderr-after-launcher-exit writes its marker with writeFile(marker, 'alive'), which truncates before it writes, while waitForFileText polls the file every 20ms. A poll landing between truncate and write reads an empty string and the assertion fails on '' !== 'alive'. We reproduced this once in five isolated runs, so it will go red on CI at some rate.

The product behaviour is correct — the marker being written at all is what demonstrates the EPIPE guard works and the process survived. Writing to a temp file and renaming it into place would make the read atomic; retrying on empty would also do it.

On CI: this head has zero check runs. The description reports a passing suite, and we do not doubt it, but nothing has executed against 583d62f7 itself, so there is no independent verification to point at. A push to trigger CI is the remaining gap — it is also what would let this be approved rather than commented on.

Two reviewers went over this independently, and both reached the same conclusion on all four items. Our own run of the host-kernel suite was 48/51, with the three failures being Electron-dependent tests that cannot start on a headless machine (missing libatk-1.0.so.0) — an environment limit, not a result about this change.


This review was AI-assisted. Findings were verified against the exact head listed above; any mistakes are ours to correct — please push back where we got it wrong.

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One follow-up now that the full check set has run on this head.

test is green, but Release Windows check is failingVerify automatic update end to end times out with Maka.exe did not appear among installed processes within 120000ms.

I looked at whether that check is just flaky, and it doesn't appear to be: of the twelve most recent runs, it succeeded on eight branches around the same window and failed on two, one of which is this branch. So it isn't a broken runner.

I have not proven this PR causes it, and I'd rather say so than assert it. But the inline note below is where I'd start looking — the failing step is a detached-spawn scenario, and this is the PR that changes how that spawn's stdio is wired.

Everything from my previous review still stands: all four earlier points are resolved, and the only other open item is the P3 test race in the fixture.


This review was AI-assisted. Findings were verified against the exact head listed above; any mistakes are mine to correct — please push back where I got it wrong.

Comment threadpackages/runtime-host/src/client/launcher.ts
Comment threadpackages/runtime-host/src/client/launcher.ts
@Astro-Han

Copy link
Copy Markdown
Contributor

Hi — this PR conflicts with current main and cannot be merged as-is.

I tested a rebase onto current main locally (in a throwaway worktree — your branch was not touched). It stops on these files:

  • apps/desktop/src/main/main-process-diagnostics.ts
  • packages/runtime-host/src/__tests__/host-kernel.test.ts
  • packages/runtime-host/src/client/connect-or-spawn.ts
  • …(more)

These are real source conflicts, so they need your judgement rather than a mechanical rebase — please rebase onto current main and resolve them yourself, then push. Once the branch is conflict-free and CI is green on the new head, I will pick it up for review.

git fetch upstream && git rebase upstream/main
# resolve, then
git push --force-with-lease

Thanks for the contribution — happy to help if any conflict is unclear.


AI-assisted maintenance note, not a review. It does not count as the required human review under CONTRIBUTING.md §Review.

@HuYellow
HuYellowforce-pushed the fix/3333-runtime-host-exit-diagnostics branch from 583d62f to 2f7c10dCompareAugust 23, 2026 08:21
@HuYellow

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han Rebased onto current main (729839ed8ada3e5498b0ad27e64fb42dfd283ae7) and force-pushed exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb.

I manually resolved the five conflicts, preserving the new election diagnostics and keeping current-run Runtime Host exits out of previous-main-process reports. I also addressed the two follow-ups:

  • A truncated stderr tail now drops its potentially partial leading record before redaction. The regression constructs a secret straddling the 4096-byte cut, confirms the retained raw tail still contains the secret fragment, and verifies that the formatted Desktop diagnostic does not.
  • The survival fixture now writes a unique sibling temporary file and atomically renames it into place, removing the truncate/write polling race.

Local verification on Windows is green:

  • npm run build
  • npm run typecheck
  • Biome on all 15 changed files
  • npm run check:stale
  • git diff --check origin/main...HEAD
  • 13 targeted Runtime Host election/detached stderr tests
  • 9 owned-candidate tests
  • 41 Desktop candidate/diagnostics/recovery tests

The exact detached-process regression where the launcher exits and the Host later writes stderr passes on this head. GitHub currently shows the new head without source conflicts; test is pending and two workflows are awaiting maintainer approval. Please take another look at 2f7c10d when CI is available.

@HuYellow

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han All requested follow-ups are now complete on exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb: the branch is rebased onto current main, all review threads are resolved, and both GitHub checks are green (required CI / test in 10m and Release Windows check / package in 11m). The PR is now blocked only on the required approving review. Please complete the pending review when you have a chance.

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Representative review under @WAWQAQ's standing apache/maka review authorization, bound to exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb.

GO: no P0–P2 findings. I verified the bounded stderr-tail collection, final-registration PID association, redaction/truncation path, detached-process behavior, and the dedicated copied-diagnostics path. The affected Runtime Host tests passed 5/5, Desktop diagnostics/candidate tests passed 41/41, and removing the selected-process handoff made its targeted regression fail before restoration. The exact-head test and Windows package check-runs are both completed/success; the PR is open, non-draft, and mergeable, with no current live hold.

One non-blocking P3 is recorded inline. This agent-executed approval does not claim to satisfy any separate repository human-review requirement. No merge performed.

maxBytes: MAIN_PROCESS_DIAGNOSTIC_LOG_MAX_BYTES,
});
export const runtimeHostProcessLogBuffer = new DiagnosticLogBuffer({
maxBytes: 16 * 1024,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] The promised four-exit history only retains three maximum-size records. This buffer combines maxBytes: 16 * 1024 with maxEntries: 4, but each formatted 4096-byte stderr-tail diagnostic is larger than 4 KiB once the timestamp, level, exit status, truncation marker, and JSON string encoding are included. Reproduction on this production buffer: append four such records; snapshot() returns 3 entries (4206 encoded bytes each), retaining PIDs 2/3/4 and evicting PID 1. The current diagnostic test injects only one short record, so it cannot catch the contract gap. Please either size the total cap for four worst-case formatted entries or impose a per-entry bound that makes the stated four-entry retention true, and add a four-maximum-entry regression.

@Astro-Han
Astro-Han merged commit 6cfc888 into apache:mainAug 23, 2026
2 checks passed
mikemikimike pushed a commit to mikemikimike/maka that referenced this pull request Aug 23, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Desktop conversation reads fail while the local Runtime Host stays unavailable after transport EOF

3 participants

@HuYellow@Astro-Han@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(desktop): preserve Runtime Host exit diagnostics - #3344

Merged
Astro-Han merged 1 commit into
apache:mainfrom
HuYellow:fix/3333-runtime-host-exit-diagnostics
Aug 23, 2026
Merged

fix(desktop): preserve Runtime Host exit diagnostics#3344
Astro-Han merged 1 commit into
apache:mainfrom
HuYellow:fix/3333-runtime-host-exit-diagnostics

Conversation

@HuYellow

@HuYellowHuYellow commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

The detached local Runtime Host was launched with ignored stdio, so after its
transport reached EOF Desktop had no process exit code, signal, or Host stderr
to include in the diagnostic report.

This change captures a bounded 4 KiB stderr tail and exit status while
preserving detached-process behavior, associates that evidence only with the
spawned PID selected by the final Host registration, and records a redacted
diagnostic in both the main-process log and a dedicated bounded process-exit
buffer. Copied Desktop diagnostics retain the last four local Host exits even
when repeated transport errors fill the ordinary log buffer and the Host is no
longer available.

Fixes#3333

Verification

  • npx biome check <13 changed files>
  • npm run build
  • npm run typecheck
  • git diff --check
  • 4 targeted Runtime Host tests passed, covering selected-PID association,
    detached stderr after launcher exit, bounded stderr, and the exact 4096-byte
    boundary
  • 36 targeted Desktop tests passed, covering stderr redaction and process-exit
    evidence in copied diagnostics

The full Desktop suite was also run on Windows: 955 tests passed, 17 unrelated
existing environment-dependent tests failed, and 7 were skipped. The failures
were Windows symlink EPERM, SQLite cleanup EBUSY, and Rive CLI fixture
failures. All tests added or changed by this PR pass.

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: OpenAI Codex assisted with root-cause analysis,
implementation, regression tests, local verification, and PR drafting. The
contributor will review the final diff and accepts responsibility for the contribution.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Lint, formatting, typechecks, builds, and targeted affected tests pass. The
full Desktop suite has the unrelated Windows failures listed under Verification.

Does this PR entail a change in behavior?

  • Yes - described under Summary above
  • No

@HuYellow
HuYellow marked this pull request as ready for review August 20, 2026 16:34

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Codex review on exact head 40ecd476303d230f601119b1fb2a33f6a134e446 found no actionable code issue.

The bug still exists on current main: detached Runtime Host candidates use ignored stdio, so after transport EOF Desktop cannot report the selected child’s exit code, signal, or stderr. This revision keeps the evidence bounded (4 KiB stderr tail and four Desktop exit records), associates it only when the final registration PID matches a process spawned by this election, redacts before logging/persistence, and keeps detached lifecycle handles unreferenced. The startup-failure path still derives from the same exit evidence, and the process map is election-local rather than long-lived.

The required test check is green. The optional Windows package failure is a renderer CDP smoke timeout; a contemporaneous documentation-only PR failed with the same Packaged Maka renderer did not expose CDP signature, while this run’s earlier Windows package/installer smokes passed. I therefore do not attribute that failure to this diff, though a rerun would provide cleaner evidence.

Required conclusions:

  1. Optimal for the actual problem: yes.
  2. Production code to delete: none identified.
  3. Tests to delete/replace: none identified.
  4. Deeper refactor: no; launcher exit evidence and Desktop presentation remain at the correct boundary.
  5. Ready to merge: no; independent human review is still missing.
  6. Residual risk/gaps: rerun the non-required Windows package smoke; exact-head human judgment remains the merge gate.

This changes user-visible diagnostics and detached-process observability, so independent human review is required under CONTRIBUTING.md. No security, licensing, governance, or public-contract effect was identified.

@HuYellow

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review. I’ll keep the current head unchanged since the required test check is green and no actionable issue was found. A rerun of the optional Windows package check would be appreciated. This PR is ready for independent human review on 40ecd47.

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — this closes a real gap. stdio: 'ignore' meant that the one moment you most need the Host's own words, after its transport hits EOF, was exactly the moment they were guaranteed to be gone. Associating the evidence with the PID the final registration actually selected, rather than with whichever candidate happened to lose, is the detail that makes the report trustworthy, and the dedicated bounded buffer means repeated transport errors cannot push the exits out of the ordinary log.

No [P0]/[P1]. Two [P2]s and two [P3]s below. Both [P2]s are questions rather than defects I can demonstrate — I could not settle either from the diff alone, and in both cases the thing I want is a sentence in the code saying which behaviour was intended.

The one I would most like an answer on is the detached case. stdio for a detached Host changes from 'ignore' to 'pipe', which means the Host's stderr is now connected to a parent that is designed to exit first. 'ignore' pointed at /dev/null and was safe for the entire life of the Host no matter what Desktop did. A pipe is not: once Desktop is gone, the read end is closed, and what happens to the Host's next stderr write depends on details I would rather see pinned down than inferred.

Review assisted by AI (Claude Opus 5). Findings were verified against the files at this head; the reviewer is accountable for them.

Comment threadpackages/runtime-host/src/client/launcher.ts
Comment threadapps/desktop/src/main/runtime-host-desktop-candidate.ts Outdated
Comment threadapps/desktop/src/main/runtime-host-desktop-candidate.ts Outdated
Comment threadpackages/runtime-host/src/client/launcher.ts Outdated
@HuYellow
HuYellowforce-pushed the fix/3333-runtime-host-exit-diagnostics branch from 40ecd47 to 583d62fCompareAugust 22, 2026 15:07
@HuYellow

Copy link
Copy Markdown
ContributorAuthor

Addressed all four review threads on exact head 583d62f7 and resolved the conversations:

  • guarded the detached Host's stderr pipe against the expected EPIPE after its launcher exits, with a survival regression;
  • documented why whole-text and token redaction are both required, with an embedded compact-JSON regression;
  • omitted the stderr/truncation section for a blank trimmed tail;
  • fixed the exact 4096-byte boundary so a complete tail is not reported as truncated.

The branch is rebased onto current main with no conflicts. Local verification is green: full build and typecheck, Biome on all 13 changed files, git diff --check, 4 targeted Runtime Host tests, and 36 targeted Desktop tests.

@Astro-Han, I have re-requested review for this exact head. Could a maintainer also approve the two workflows currently awaiting approval so the required test check can run?

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — you answered all four points from the previous round, and this comment exists mostly to confirm that on the record. Re-reviewed at exact head 583d62f7408805e0ac820249810ff58b92ef7454 against base 4acfa26934ce4b2b385b76f8a11048bb83fab861. None of the six existing reviews are bound to this head, so everything below was re-checked against the current code rather than carried forward.

Each of our four items, verified here:

  • [P2] detached stderr pipe lifetime — fixed.launcher.ts now spawns with ['ignore','ignore','pipe'] behind the MAKA_RUNTIME_HOST_STDERR_PIPE opt-in, and the EPIPE guard in process-diagnostics.ts swallows only EPIPE and rethrows everything else. That distinction matters and it is drawn correctly — a blanket catch here would have hidden the next real write failure. The detached-survival regression covers it.
  • [P2] redactSecrets two-pass semantics — resolved by explanation, which was the right resolution. The comment now states what each pass is for: full-text pattern matching, then per-token structured redaction to catch compact JSON the first pass steps over. The ambiguity was in the reader's head, not the code, so documenting it was the proportionate fix.
  • [P3] orphaned truncation marker on empty stderr — fixed. Empty stderr returns status-only before the marker is attached, with a whitespace-only regression alongside it.
  • [P3] off-by-one at exactly 4096 — fixed.chunk.length > MAX rather than >=, with a regression pinning the exact boundary.

Also checked, and sound:observeCandidateExit listens on 'close' rather than 'exit', so the stderr tail is complete before the record is built — using 'exit' would have truncated exactly the evidence this PR exists to preserve. Diagnostics associate with the finally-registered PID, so a connect-or-spawn race attributes output to the process that actually won. The log buffer is bounded at 16KiB across four entries.

[P3, ordinary path] The survival regression has a read race in the fixture, not in the product.

stderr-after-launcher-exit writes its marker with writeFile(marker, 'alive'), which truncates before it writes, while waitForFileText polls the file every 20ms. A poll landing between truncate and write reads an empty string and the assertion fails on '' !== 'alive'. We reproduced this once in five isolated runs, so it will go red on CI at some rate.

The product behaviour is correct — the marker being written at all is what demonstrates the EPIPE guard works and the process survived. Writing to a temp file and renaming it into place would make the read atomic; retrying on empty would also do it.

On CI: this head has zero check runs. The description reports a passing suite, and we do not doubt it, but nothing has executed against 583d62f7 itself, so there is no independent verification to point at. A push to trigger CI is the remaining gap — it is also what would let this be approved rather than commented on.

Two reviewers went over this independently, and both reached the same conclusion on all four items. Our own run of the host-kernel suite was 48/51, with the three failures being Electron-dependent tests that cannot start on a headless machine (missing libatk-1.0.so.0) — an environment limit, not a result about this change.


This review was AI-assisted. Findings were verified against the exact head listed above; any mistakes are ours to correct — please push back where we got it wrong.

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One follow-up now that the full check set has run on this head.

test is green, but Release Windows check is failingVerify automatic update end to end times out with Maka.exe did not appear among installed processes within 120000ms.

I looked at whether that check is just flaky, and it doesn't appear to be: of the twelve most recent runs, it succeeded on eight branches around the same window and failed on two, one of which is this branch. So it isn't a broken runner.

I have not proven this PR causes it, and I'd rather say so than assert it. But the inline note below is where I'd start looking — the failing step is a detached-spawn scenario, and this is the PR that changes how that spawn's stdio is wired.

Everything from my previous review still stands: all four earlier points are resolved, and the only other open item is the P3 test race in the fixture.


This review was AI-assisted. Findings were verified against the exact head listed above; any mistakes are mine to correct — please push back where I got it wrong.

Comment threadpackages/runtime-host/src/client/launcher.ts
Comment threadpackages/runtime-host/src/client/launcher.ts
@Astro-Han

Copy link
Copy Markdown
Contributor

Hi — this PR conflicts with current main and cannot be merged as-is.

I tested a rebase onto current main locally (in a throwaway worktree — your branch was not touched). It stops on these files:

  • apps/desktop/src/main/main-process-diagnostics.ts
  • packages/runtime-host/src/__tests__/host-kernel.test.ts
  • packages/runtime-host/src/client/connect-or-spawn.ts
  • …(more)

These are real source conflicts, so they need your judgement rather than a mechanical rebase — please rebase onto current main and resolve them yourself, then push. Once the branch is conflict-free and CI is green on the new head, I will pick it up for review.

git fetch upstream && git rebase upstream/main
# resolve, then
git push --force-with-lease

Thanks for the contribution — happy to help if any conflict is unclear.


AI-assisted maintenance note, not a review. It does not count as the required human review under CONTRIBUTING.md §Review.

@HuYellow
HuYellowforce-pushed the fix/3333-runtime-host-exit-diagnostics branch from 583d62f to 2f7c10dCompareAugust 23, 2026 08:21
@HuYellow

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han Rebased onto current main (729839ed8ada3e5498b0ad27e64fb42dfd283ae7) and force-pushed exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb.

I manually resolved the five conflicts, preserving the new election diagnostics and keeping current-run Runtime Host exits out of previous-main-process reports. I also addressed the two follow-ups:

  • A truncated stderr tail now drops its potentially partial leading record before redaction. The regression constructs a secret straddling the 4096-byte cut, confirms the retained raw tail still contains the secret fragment, and verifies that the formatted Desktop diagnostic does not.
  • The survival fixture now writes a unique sibling temporary file and atomically renames it into place, removing the truncate/write polling race.

Local verification on Windows is green:

  • npm run build
  • npm run typecheck
  • Biome on all 15 changed files
  • npm run check:stale
  • git diff --check origin/main...HEAD
  • 13 targeted Runtime Host election/detached stderr tests
  • 9 owned-candidate tests
  • 41 Desktop candidate/diagnostics/recovery tests

The exact detached-process regression where the launcher exits and the Host later writes stderr passes on this head. GitHub currently shows the new head without source conflicts; test is pending and two workflows are awaiting maintainer approval. Please take another look at 2f7c10d when CI is available.

@HuYellow

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han All requested follow-ups are now complete on exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb: the branch is rebased onto current main, all review threads are resolved, and both GitHub checks are green (required CI / test in 10m and Release Windows check / package in 11m). The PR is now blocked only on the required approving review. Please complete the pending review when you have a chance.

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Representative review under @WAWQAQ's standing apache/maka review authorization, bound to exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb.

GO: no P0–P2 findings. I verified the bounded stderr-tail collection, final-registration PID association, redaction/truncation path, detached-process behavior, and the dedicated copied-diagnostics path. The affected Runtime Host tests passed 5/5, Desktop diagnostics/candidate tests passed 41/41, and removing the selected-process handoff made its targeted regression fail before restoration. The exact-head test and Windows package check-runs are both completed/success; the PR is open, non-draft, and mergeable, with no current live hold.

One non-blocking P3 is recorded inline. This agent-executed approval does not claim to satisfy any separate repository human-review requirement. No merge performed.

maxBytes: MAIN_PROCESS_DIAGNOSTIC_LOG_MAX_BYTES,
});
export const runtimeHostProcessLogBuffer = new DiagnosticLogBuffer({
maxBytes: 16 * 1024,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] The promised four-exit history only retains three maximum-size records. This buffer combines maxBytes: 16 * 1024 with maxEntries: 4, but each formatted 4096-byte stderr-tail diagnostic is larger than 4 KiB once the timestamp, level, exit status, truncation marker, and JSON string encoding are included. Reproduction on this production buffer: append four such records; snapshot() returns 3 entries (4206 encoded bytes each), retaining PIDs 2/3/4 and evicting PID 1. The current diagnostic test injects only one short record, so it cannot catch the contract gap. Please either size the total cap for four worst-case formatted entries or impose a per-entry bound that makes the stated four-entry retention true, and add a four-maximum-entry regression.

@Astro-Han
Astro-Han merged commit 6cfc888 into apache:mainAug 23, 2026
2 checks passed
mikemikimike pushed a commit to mikemikimike/maka that referenced this pull request Aug 23, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Desktop conversation reads fail while the local Runtime Host stays unavailable after transport EOF

3 participants

@HuYellow@Astro-Han@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(desktop): preserve Runtime Host exit diagnostics - #3344

Merged
Astro-Han merged 1 commit into
apache:mainfrom
HuYellow:fix/3333-runtime-host-exit-diagnostics
Aug 23, 2026
Merged

fix(desktop): preserve Runtime Host exit diagnostics#3344
Astro-Han merged 1 commit into
apache:mainfrom
HuYellow:fix/3333-runtime-host-exit-diagnostics

Conversation

@HuYellow

@HuYellowHuYellow commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

The detached local Runtime Host was launched with ignored stdio, so after its
transport reached EOF Desktop had no process exit code, signal, or Host stderr
to include in the diagnostic report.

This change captures a bounded 4 KiB stderr tail and exit status while
preserving detached-process behavior, associates that evidence only with the
spawned PID selected by the final Host registration, and records a redacted
diagnostic in both the main-process log and a dedicated bounded process-exit
buffer. Copied Desktop diagnostics retain the last four local Host exits even
when repeated transport errors fill the ordinary log buffer and the Host is no
longer available.

Fixes#3333

Verification

  • npx biome check <13 changed files>
  • npm run build
  • npm run typecheck
  • git diff --check
  • 4 targeted Runtime Host tests passed, covering selected-PID association,
    detached stderr after launcher exit, bounded stderr, and the exact 4096-byte
    boundary
  • 36 targeted Desktop tests passed, covering stderr redaction and process-exit
    evidence in copied diagnostics

The full Desktop suite was also run on Windows: 955 tests passed, 17 unrelated
existing environment-dependent tests failed, and 7 were skipped. The failures
were Windows symlink EPERM, SQLite cleanup EBUSY, and Rive CLI fixture
failures. All tests added or changed by this PR pass.

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: OpenAI Codex assisted with root-cause analysis,
implementation, regression tests, local verification, and PR drafting. The
contributor will review the final diff and accepts responsibility for the contribution.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Lint, formatting, typechecks, builds, and targeted affected tests pass. The
full Desktop suite has the unrelated Windows failures listed under Verification.

Does this PR entail a change in behavior?

  • Yes - described under Summary above
  • No

@HuYellow
HuYellow marked this pull request as ready for review August 20, 2026 16:34

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Codex review on exact head 40ecd476303d230f601119b1fb2a33f6a134e446 found no actionable code issue.

The bug still exists on current main: detached Runtime Host candidates use ignored stdio, so after transport EOF Desktop cannot report the selected child’s exit code, signal, or stderr. This revision keeps the evidence bounded (4 KiB stderr tail and four Desktop exit records), associates it only when the final registration PID matches a process spawned by this election, redacts before logging/persistence, and keeps detached lifecycle handles unreferenced. The startup-failure path still derives from the same exit evidence, and the process map is election-local rather than long-lived.

The required test check is green. The optional Windows package failure is a renderer CDP smoke timeout; a contemporaneous documentation-only PR failed with the same Packaged Maka renderer did not expose CDP signature, while this run’s earlier Windows package/installer smokes passed. I therefore do not attribute that failure to this diff, though a rerun would provide cleaner evidence.

Required conclusions:

  1. Optimal for the actual problem: yes.
  2. Production code to delete: none identified.
  3. Tests to delete/replace: none identified.
  4. Deeper refactor: no; launcher exit evidence and Desktop presentation remain at the correct boundary.
  5. Ready to merge: no; independent human review is still missing.
  6. Residual risk/gaps: rerun the non-required Windows package smoke; exact-head human judgment remains the merge gate.

This changes user-visible diagnostics and detached-process observability, so independent human review is required under CONTRIBUTING.md. No security, licensing, governance, or public-contract effect was identified.

@HuYellow

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review. I’ll keep the current head unchanged since the required test check is green and no actionable issue was found. A rerun of the optional Windows package check would be appreciated. This PR is ready for independent human review on 40ecd47.

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — this closes a real gap. stdio: 'ignore' meant that the one moment you most need the Host's own words, after its transport hits EOF, was exactly the moment they were guaranteed to be gone. Associating the evidence with the PID the final registration actually selected, rather than with whichever candidate happened to lose, is the detail that makes the report trustworthy, and the dedicated bounded buffer means repeated transport errors cannot push the exits out of the ordinary log.

No [P0]/[P1]. Two [P2]s and two [P3]s below. Both [P2]s are questions rather than defects I can demonstrate — I could not settle either from the diff alone, and in both cases the thing I want is a sentence in the code saying which behaviour was intended.

The one I would most like an answer on is the detached case. stdio for a detached Host changes from 'ignore' to 'pipe', which means the Host's stderr is now connected to a parent that is designed to exit first. 'ignore' pointed at /dev/null and was safe for the entire life of the Host no matter what Desktop did. A pipe is not: once Desktop is gone, the read end is closed, and what happens to the Host's next stderr write depends on details I would rather see pinned down than inferred.

Review assisted by AI (Claude Opus 5). Findings were verified against the files at this head; the reviewer is accountable for them.

Comment threadpackages/runtime-host/src/client/launcher.ts
Comment threadapps/desktop/src/main/runtime-host-desktop-candidate.ts Outdated
Comment threadapps/desktop/src/main/runtime-host-desktop-candidate.ts Outdated
Comment threadpackages/runtime-host/src/client/launcher.ts Outdated
@HuYellow
HuYellowforce-pushed the fix/3333-runtime-host-exit-diagnostics branch from 40ecd47 to 583d62fCompareAugust 22, 2026 15:07
@HuYellow

Copy link
Copy Markdown
ContributorAuthor

Addressed all four review threads on exact head 583d62f7 and resolved the conversations:

  • guarded the detached Host's stderr pipe against the expected EPIPE after its launcher exits, with a survival regression;
  • documented why whole-text and token redaction are both required, with an embedded compact-JSON regression;
  • omitted the stderr/truncation section for a blank trimmed tail;
  • fixed the exact 4096-byte boundary so a complete tail is not reported as truncated.

The branch is rebased onto current main with no conflicts. Local verification is green: full build and typecheck, Biome on all 13 changed files, git diff --check, 4 targeted Runtime Host tests, and 36 targeted Desktop tests.

@Astro-Han, I have re-requested review for this exact head. Could a maintainer also approve the two workflows currently awaiting approval so the required test check can run?

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — you answered all four points from the previous round, and this comment exists mostly to confirm that on the record. Re-reviewed at exact head 583d62f7408805e0ac820249810ff58b92ef7454 against base 4acfa26934ce4b2b385b76f8a11048bb83fab861. None of the six existing reviews are bound to this head, so everything below was re-checked against the current code rather than carried forward.

Each of our four items, verified here:

  • [P2] detached stderr pipe lifetime — fixed.launcher.ts now spawns with ['ignore','ignore','pipe'] behind the MAKA_RUNTIME_HOST_STDERR_PIPE opt-in, and the EPIPE guard in process-diagnostics.ts swallows only EPIPE and rethrows everything else. That distinction matters and it is drawn correctly — a blanket catch here would have hidden the next real write failure. The detached-survival regression covers it.
  • [P2] redactSecrets two-pass semantics — resolved by explanation, which was the right resolution. The comment now states what each pass is for: full-text pattern matching, then per-token structured redaction to catch compact JSON the first pass steps over. The ambiguity was in the reader's head, not the code, so documenting it was the proportionate fix.
  • [P3] orphaned truncation marker on empty stderr — fixed. Empty stderr returns status-only before the marker is attached, with a whitespace-only regression alongside it.
  • [P3] off-by-one at exactly 4096 — fixed.chunk.length > MAX rather than >=, with a regression pinning the exact boundary.

Also checked, and sound:observeCandidateExit listens on 'close' rather than 'exit', so the stderr tail is complete before the record is built — using 'exit' would have truncated exactly the evidence this PR exists to preserve. Diagnostics associate with the finally-registered PID, so a connect-or-spawn race attributes output to the process that actually won. The log buffer is bounded at 16KiB across four entries.

[P3, ordinary path] The survival regression has a read race in the fixture, not in the product.

stderr-after-launcher-exit writes its marker with writeFile(marker, 'alive'), which truncates before it writes, while waitForFileText polls the file every 20ms. A poll landing between truncate and write reads an empty string and the assertion fails on '' !== 'alive'. We reproduced this once in five isolated runs, so it will go red on CI at some rate.

The product behaviour is correct — the marker being written at all is what demonstrates the EPIPE guard works and the process survived. Writing to a temp file and renaming it into place would make the read atomic; retrying on empty would also do it.

On CI: this head has zero check runs. The description reports a passing suite, and we do not doubt it, but nothing has executed against 583d62f7 itself, so there is no independent verification to point at. A push to trigger CI is the remaining gap — it is also what would let this be approved rather than commented on.

Two reviewers went over this independently, and both reached the same conclusion on all four items. Our own run of the host-kernel suite was 48/51, with the three failures being Electron-dependent tests that cannot start on a headless machine (missing libatk-1.0.so.0) — an environment limit, not a result about this change.


This review was AI-assisted. Findings were verified against the exact head listed above; any mistakes are ours to correct — please push back where we got it wrong.

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One follow-up now that the full check set has run on this head.

test is green, but Release Windows check is failingVerify automatic update end to end times out with Maka.exe did not appear among installed processes within 120000ms.

I looked at whether that check is just flaky, and it doesn't appear to be: of the twelve most recent runs, it succeeded on eight branches around the same window and failed on two, one of which is this branch. So it isn't a broken runner.

I have not proven this PR causes it, and I'd rather say so than assert it. But the inline note below is where I'd start looking — the failing step is a detached-spawn scenario, and this is the PR that changes how that spawn's stdio is wired.

Everything from my previous review still stands: all four earlier points are resolved, and the only other open item is the P3 test race in the fixture.


This review was AI-assisted. Findings were verified against the exact head listed above; any mistakes are mine to correct — please push back where I got it wrong.

Comment threadpackages/runtime-host/src/client/launcher.ts
Comment threadpackages/runtime-host/src/client/launcher.ts
@Astro-Han

Copy link
Copy Markdown
Contributor

Hi — this PR conflicts with current main and cannot be merged as-is.

I tested a rebase onto current main locally (in a throwaway worktree — your branch was not touched). It stops on these files:

  • apps/desktop/src/main/main-process-diagnostics.ts
  • packages/runtime-host/src/__tests__/host-kernel.test.ts
  • packages/runtime-host/src/client/connect-or-spawn.ts
  • …(more)

These are real source conflicts, so they need your judgement rather than a mechanical rebase — please rebase onto current main and resolve them yourself, then push. Once the branch is conflict-free and CI is green on the new head, I will pick it up for review.

git fetch upstream && git rebase upstream/main
# resolve, then
git push --force-with-lease

Thanks for the contribution — happy to help if any conflict is unclear.


AI-assisted maintenance note, not a review. It does not count as the required human review under CONTRIBUTING.md §Review.

@HuYellow
HuYellowforce-pushed the fix/3333-runtime-host-exit-diagnostics branch from 583d62f to 2f7c10dCompareAugust 23, 2026 08:21
@HuYellow

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han Rebased onto current main (729839ed8ada3e5498b0ad27e64fb42dfd283ae7) and force-pushed exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb.

I manually resolved the five conflicts, preserving the new election diagnostics and keeping current-run Runtime Host exits out of previous-main-process reports. I also addressed the two follow-ups:

  • A truncated stderr tail now drops its potentially partial leading record before redaction. The regression constructs a secret straddling the 4096-byte cut, confirms the retained raw tail still contains the secret fragment, and verifies that the formatted Desktop diagnostic does not.
  • The survival fixture now writes a unique sibling temporary file and atomically renames it into place, removing the truncate/write polling race.

Local verification on Windows is green:

  • npm run build
  • npm run typecheck
  • Biome on all 15 changed files
  • npm run check:stale
  • git diff --check origin/main...HEAD
  • 13 targeted Runtime Host election/detached stderr tests
  • 9 owned-candidate tests
  • 41 Desktop candidate/diagnostics/recovery tests

The exact detached-process regression where the launcher exits and the Host later writes stderr passes on this head. GitHub currently shows the new head without source conflicts; test is pending and two workflows are awaiting maintainer approval. Please take another look at 2f7c10d when CI is available.

@HuYellow

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han All requested follow-ups are now complete on exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb: the branch is rebased onto current main, all review threads are resolved, and both GitHub checks are green (required CI / test in 10m and Release Windows check / package in 11m). The PR is now blocked only on the required approving review. Please complete the pending review when you have a chance.

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Representative review under @WAWQAQ's standing apache/maka review authorization, bound to exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb.

GO: no P0–P2 findings. I verified the bounded stderr-tail collection, final-registration PID association, redaction/truncation path, detached-process behavior, and the dedicated copied-diagnostics path. The affected Runtime Host tests passed 5/5, Desktop diagnostics/candidate tests passed 41/41, and removing the selected-process handoff made its targeted regression fail before restoration. The exact-head test and Windows package check-runs are both completed/success; the PR is open, non-draft, and mergeable, with no current live hold.

One non-blocking P3 is recorded inline. This agent-executed approval does not claim to satisfy any separate repository human-review requirement. No merge performed.

maxBytes: MAIN_PROCESS_DIAGNOSTIC_LOG_MAX_BYTES,
});
export const runtimeHostProcessLogBuffer = new DiagnosticLogBuffer({
maxBytes: 16 * 1024,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] The promised four-exit history only retains three maximum-size records. This buffer combines maxBytes: 16 * 1024 with maxEntries: 4, but each formatted 4096-byte stderr-tail diagnostic is larger than 4 KiB once the timestamp, level, exit status, truncation marker, and JSON string encoding are included. Reproduction on this production buffer: append four such records; snapshot() returns 3 entries (4206 encoded bytes each), retaining PIDs 2/3/4 and evicting PID 1. The current diagnostic test injects only one short record, so it cannot catch the contract gap. Please either size the total cap for four worst-case formatted entries or impose a per-entry bound that makes the stated four-entry retention true, and add a four-maximum-entry regression.

@Astro-Han
Astro-Han merged commit 6cfc888 into apache:mainAug 23, 2026
2 checks passed
mikemikimike pushed a commit to mikemikimike/maka that referenced this pull request Aug 23, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Desktop conversation reads fail while the local Runtime Host stays unavailable after transport EOF

3 participants

@HuYellow@Astro-Han@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(desktop): preserve Runtime Host exit diagnostics - #3344

Merged
Astro-Han merged 1 commit into
apache:mainfrom
HuYellow:fix/3333-runtime-host-exit-diagnostics
Aug 23, 2026
Merged

fix(desktop): preserve Runtime Host exit diagnostics#3344
Astro-Han merged 1 commit into
apache:mainfrom
HuYellow:fix/3333-runtime-host-exit-diagnostics

Conversation

@HuYellow

@HuYellowHuYellow commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

The detached local Runtime Host was launched with ignored stdio, so after its
transport reached EOF Desktop had no process exit code, signal, or Host stderr
to include in the diagnostic report.

This change captures a bounded 4 KiB stderr tail and exit status while
preserving detached-process behavior, associates that evidence only with the
spawned PID selected by the final Host registration, and records a redacted
diagnostic in both the main-process log and a dedicated bounded process-exit
buffer. Copied Desktop diagnostics retain the last four local Host exits even
when repeated transport errors fill the ordinary log buffer and the Host is no
longer available.

Fixes#3333

Verification

  • npx biome check <13 changed files>
  • npm run build
  • npm run typecheck
  • git diff --check
  • 4 targeted Runtime Host tests passed, covering selected-PID association,
    detached stderr after launcher exit, bounded stderr, and the exact 4096-byte
    boundary
  • 36 targeted Desktop tests passed, covering stderr redaction and process-exit
    evidence in copied diagnostics

The full Desktop suite was also run on Windows: 955 tests passed, 17 unrelated
existing environment-dependent tests failed, and 7 were skipped. The failures
were Windows symlink EPERM, SQLite cleanup EBUSY, and Rive CLI fixture
failures. All tests added or changed by this PR pass.

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: OpenAI Codex assisted with root-cause analysis,
implementation, regression tests, local verification, and PR drafting. The
contributor will review the final diff and accepts responsibility for the contribution.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Lint, formatting, typechecks, builds, and targeted affected tests pass. The
full Desktop suite has the unrelated Windows failures listed under Verification.

Does this PR entail a change in behavior?

  • Yes - described under Summary above
  • No

@HuYellow
HuYellow marked this pull request as ready for review August 20, 2026 16:34

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Codex review on exact head 40ecd476303d230f601119b1fb2a33f6a134e446 found no actionable code issue.

The bug still exists on current main: detached Runtime Host candidates use ignored stdio, so after transport EOF Desktop cannot report the selected child’s exit code, signal, or stderr. This revision keeps the evidence bounded (4 KiB stderr tail and four Desktop exit records), associates it only when the final registration PID matches a process spawned by this election, redacts before logging/persistence, and keeps detached lifecycle handles unreferenced. The startup-failure path still derives from the same exit evidence, and the process map is election-local rather than long-lived.

The required test check is green. The optional Windows package failure is a renderer CDP smoke timeout; a contemporaneous documentation-only PR failed with the same Packaged Maka renderer did not expose CDP signature, while this run’s earlier Windows package/installer smokes passed. I therefore do not attribute that failure to this diff, though a rerun would provide cleaner evidence.

Required conclusions:

  1. Optimal for the actual problem: yes.
  2. Production code to delete: none identified.
  3. Tests to delete/replace: none identified.
  4. Deeper refactor: no; launcher exit evidence and Desktop presentation remain at the correct boundary.
  5. Ready to merge: no; independent human review is still missing.
  6. Residual risk/gaps: rerun the non-required Windows package smoke; exact-head human judgment remains the merge gate.

This changes user-visible diagnostics and detached-process observability, so independent human review is required under CONTRIBUTING.md. No security, licensing, governance, or public-contract effect was identified.

@HuYellow

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review. I’ll keep the current head unchanged since the required test check is green and no actionable issue was found. A rerun of the optional Windows package check would be appreciated. This PR is ready for independent human review on 40ecd47.

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — this closes a real gap. stdio: 'ignore' meant that the one moment you most need the Host's own words, after its transport hits EOF, was exactly the moment they were guaranteed to be gone. Associating the evidence with the PID the final registration actually selected, rather than with whichever candidate happened to lose, is the detail that makes the report trustworthy, and the dedicated bounded buffer means repeated transport errors cannot push the exits out of the ordinary log.

No [P0]/[P1]. Two [P2]s and two [P3]s below. Both [P2]s are questions rather than defects I can demonstrate — I could not settle either from the diff alone, and in both cases the thing I want is a sentence in the code saying which behaviour was intended.

The one I would most like an answer on is the detached case. stdio for a detached Host changes from 'ignore' to 'pipe', which means the Host's stderr is now connected to a parent that is designed to exit first. 'ignore' pointed at /dev/null and was safe for the entire life of the Host no matter what Desktop did. A pipe is not: once Desktop is gone, the read end is closed, and what happens to the Host's next stderr write depends on details I would rather see pinned down than inferred.

Review assisted by AI (Claude Opus 5). Findings were verified against the files at this head; the reviewer is accountable for them.

Comment threadpackages/runtime-host/src/client/launcher.ts
Comment threadapps/desktop/src/main/runtime-host-desktop-candidate.ts Outdated
Comment threadapps/desktop/src/main/runtime-host-desktop-candidate.ts Outdated
Comment threadpackages/runtime-host/src/client/launcher.ts Outdated
@HuYellow
HuYellowforce-pushed the fix/3333-runtime-host-exit-diagnostics branch from 40ecd47 to 583d62fCompareAugust 22, 2026 15:07
@HuYellow

Copy link
Copy Markdown
ContributorAuthor

Addressed all four review threads on exact head 583d62f7 and resolved the conversations:

  • guarded the detached Host's stderr pipe against the expected EPIPE after its launcher exits, with a survival regression;
  • documented why whole-text and token redaction are both required, with an embedded compact-JSON regression;
  • omitted the stderr/truncation section for a blank trimmed tail;
  • fixed the exact 4096-byte boundary so a complete tail is not reported as truncated.

The branch is rebased onto current main with no conflicts. Local verification is green: full build and typecheck, Biome on all 13 changed files, git diff --check, 4 targeted Runtime Host tests, and 36 targeted Desktop tests.

@Astro-Han, I have re-requested review for this exact head. Could a maintainer also approve the two workflows currently awaiting approval so the required test check can run?

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — you answered all four points from the previous round, and this comment exists mostly to confirm that on the record. Re-reviewed at exact head 583d62f7408805e0ac820249810ff58b92ef7454 against base 4acfa26934ce4b2b385b76f8a11048bb83fab861. None of the six existing reviews are bound to this head, so everything below was re-checked against the current code rather than carried forward.

Each of our four items, verified here:

  • [P2] detached stderr pipe lifetime — fixed.launcher.ts now spawns with ['ignore','ignore','pipe'] behind the MAKA_RUNTIME_HOST_STDERR_PIPE opt-in, and the EPIPE guard in process-diagnostics.ts swallows only EPIPE and rethrows everything else. That distinction matters and it is drawn correctly — a blanket catch here would have hidden the next real write failure. The detached-survival regression covers it.
  • [P2] redactSecrets two-pass semantics — resolved by explanation, which was the right resolution. The comment now states what each pass is for: full-text pattern matching, then per-token structured redaction to catch compact JSON the first pass steps over. The ambiguity was in the reader's head, not the code, so documenting it was the proportionate fix.
  • [P3] orphaned truncation marker on empty stderr — fixed. Empty stderr returns status-only before the marker is attached, with a whitespace-only regression alongside it.
  • [P3] off-by-one at exactly 4096 — fixed.chunk.length > MAX rather than >=, with a regression pinning the exact boundary.

Also checked, and sound:observeCandidateExit listens on 'close' rather than 'exit', so the stderr tail is complete before the record is built — using 'exit' would have truncated exactly the evidence this PR exists to preserve. Diagnostics associate with the finally-registered PID, so a connect-or-spawn race attributes output to the process that actually won. The log buffer is bounded at 16KiB across four entries.

[P3, ordinary path] The survival regression has a read race in the fixture, not in the product.

stderr-after-launcher-exit writes its marker with writeFile(marker, 'alive'), which truncates before it writes, while waitForFileText polls the file every 20ms. A poll landing between truncate and write reads an empty string and the assertion fails on '' !== 'alive'. We reproduced this once in five isolated runs, so it will go red on CI at some rate.

The product behaviour is correct — the marker being written at all is what demonstrates the EPIPE guard works and the process survived. Writing to a temp file and renaming it into place would make the read atomic; retrying on empty would also do it.

On CI: this head has zero check runs. The description reports a passing suite, and we do not doubt it, but nothing has executed against 583d62f7 itself, so there is no independent verification to point at. A push to trigger CI is the remaining gap — it is also what would let this be approved rather than commented on.

Two reviewers went over this independently, and both reached the same conclusion on all four items. Our own run of the host-kernel suite was 48/51, with the three failures being Electron-dependent tests that cannot start on a headless machine (missing libatk-1.0.so.0) — an environment limit, not a result about this change.


This review was AI-assisted. Findings were verified against the exact head listed above; any mistakes are ours to correct — please push back where we got it wrong.

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One follow-up now that the full check set has run on this head.

test is green, but Release Windows check is failingVerify automatic update end to end times out with Maka.exe did not appear among installed processes within 120000ms.

I looked at whether that check is just flaky, and it doesn't appear to be: of the twelve most recent runs, it succeeded on eight branches around the same window and failed on two, one of which is this branch. So it isn't a broken runner.

I have not proven this PR causes it, and I'd rather say so than assert it. But the inline note below is where I'd start looking — the failing step is a detached-spawn scenario, and this is the PR that changes how that spawn's stdio is wired.

Everything from my previous review still stands: all four earlier points are resolved, and the only other open item is the P3 test race in the fixture.


This review was AI-assisted. Findings were verified against the exact head listed above; any mistakes are mine to correct — please push back where I got it wrong.

Comment threadpackages/runtime-host/src/client/launcher.ts
Comment threadpackages/runtime-host/src/client/launcher.ts
@Astro-Han

Copy link
Copy Markdown
Contributor

Hi — this PR conflicts with current main and cannot be merged as-is.

I tested a rebase onto current main locally (in a throwaway worktree — your branch was not touched). It stops on these files:

  • apps/desktop/src/main/main-process-diagnostics.ts
  • packages/runtime-host/src/__tests__/host-kernel.test.ts
  • packages/runtime-host/src/client/connect-or-spawn.ts
  • …(more)

These are real source conflicts, so they need your judgement rather than a mechanical rebase — please rebase onto current main and resolve them yourself, then push. Once the branch is conflict-free and CI is green on the new head, I will pick it up for review.

git fetch upstream && git rebase upstream/main
# resolve, then
git push --force-with-lease

Thanks for the contribution — happy to help if any conflict is unclear.


AI-assisted maintenance note, not a review. It does not count as the required human review under CONTRIBUTING.md §Review.

@HuYellow
HuYellowforce-pushed the fix/3333-runtime-host-exit-diagnostics branch from 583d62f to 2f7c10dCompareAugust 23, 2026 08:21
@HuYellow

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han Rebased onto current main (729839ed8ada3e5498b0ad27e64fb42dfd283ae7) and force-pushed exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb.

I manually resolved the five conflicts, preserving the new election diagnostics and keeping current-run Runtime Host exits out of previous-main-process reports. I also addressed the two follow-ups:

  • A truncated stderr tail now drops its potentially partial leading record before redaction. The regression constructs a secret straddling the 4096-byte cut, confirms the retained raw tail still contains the secret fragment, and verifies that the formatted Desktop diagnostic does not.
  • The survival fixture now writes a unique sibling temporary file and atomically renames it into place, removing the truncate/write polling race.

Local verification on Windows is green:

  • npm run build
  • npm run typecheck
  • Biome on all 15 changed files
  • npm run check:stale
  • git diff --check origin/main...HEAD
  • 13 targeted Runtime Host election/detached stderr tests
  • 9 owned-candidate tests
  • 41 Desktop candidate/diagnostics/recovery tests

The exact detached-process regression where the launcher exits and the Host later writes stderr passes on this head. GitHub currently shows the new head without source conflicts; test is pending and two workflows are awaiting maintainer approval. Please take another look at 2f7c10d when CI is available.

@HuYellow

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han All requested follow-ups are now complete on exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb: the branch is rebased onto current main, all review threads are resolved, and both GitHub checks are green (required CI / test in 10m and Release Windows check / package in 11m). The PR is now blocked only on the required approving review. Please complete the pending review when you have a chance.

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Representative review under @WAWQAQ's standing apache/maka review authorization, bound to exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb.

GO: no P0–P2 findings. I verified the bounded stderr-tail collection, final-registration PID association, redaction/truncation path, detached-process behavior, and the dedicated copied-diagnostics path. The affected Runtime Host tests passed 5/5, Desktop diagnostics/candidate tests passed 41/41, and removing the selected-process handoff made its targeted regression fail before restoration. The exact-head test and Windows package check-runs are both completed/success; the PR is open, non-draft, and mergeable, with no current live hold.

One non-blocking P3 is recorded inline. This agent-executed approval does not claim to satisfy any separate repository human-review requirement. No merge performed.

maxBytes: MAIN_PROCESS_DIAGNOSTIC_LOG_MAX_BYTES,
});
export const runtimeHostProcessLogBuffer = new DiagnosticLogBuffer({
maxBytes: 16 * 1024,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] The promised four-exit history only retains three maximum-size records. This buffer combines maxBytes: 16 * 1024 with maxEntries: 4, but each formatted 4096-byte stderr-tail diagnostic is larger than 4 KiB once the timestamp, level, exit status, truncation marker, and JSON string encoding are included. Reproduction on this production buffer: append four such records; snapshot() returns 3 entries (4206 encoded bytes each), retaining PIDs 2/3/4 and evicting PID 1. The current diagnostic test injects only one short record, so it cannot catch the contract gap. Please either size the total cap for four worst-case formatted entries or impose a per-entry bound that makes the stated four-entry retention true, and add a four-maximum-entry regression.

@Astro-Han
Astro-Han merged commit 6cfc888 into apache:mainAug 23, 2026
2 checks passed
mikemikimike pushed a commit to mikemikimike/maka that referenced this pull request Aug 23, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Desktop conversation reads fail while the local Runtime Host stays unavailable after transport EOF

3 participants

@HuYellow@Astro-Han@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(desktop): preserve Runtime Host exit diagnostics - #3344

Merged
Astro-Han merged 1 commit into
apache:mainfrom
HuYellow:fix/3333-runtime-host-exit-diagnostics
Aug 23, 2026
Merged

fix(desktop): preserve Runtime Host exit diagnostics#3344
Astro-Han merged 1 commit into
apache:mainfrom
HuYellow:fix/3333-runtime-host-exit-diagnostics

Conversation

@HuYellow

@HuYellowHuYellow commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

The detached local Runtime Host was launched with ignored stdio, so after its
transport reached EOF Desktop had no process exit code, signal, or Host stderr
to include in the diagnostic report.

This change captures a bounded 4 KiB stderr tail and exit status while
preserving detached-process behavior, associates that evidence only with the
spawned PID selected by the final Host registration, and records a redacted
diagnostic in both the main-process log and a dedicated bounded process-exit
buffer. Copied Desktop diagnostics retain the last four local Host exits even
when repeated transport errors fill the ordinary log buffer and the Host is no
longer available.

Fixes#3333

Verification

  • npx biome check <13 changed files>
  • npm run build
  • npm run typecheck
  • git diff --check
  • 4 targeted Runtime Host tests passed, covering selected-PID association,
    detached stderr after launcher exit, bounded stderr, and the exact 4096-byte
    boundary
  • 36 targeted Desktop tests passed, covering stderr redaction and process-exit
    evidence in copied diagnostics

The full Desktop suite was also run on Windows: 955 tests passed, 17 unrelated
existing environment-dependent tests failed, and 7 were skipped. The failures
were Windows symlink EPERM, SQLite cleanup EBUSY, and Rive CLI fixture
failures. All tests added or changed by this PR pass.

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: OpenAI Codex assisted with root-cause analysis,
implementation, regression tests, local verification, and PR drafting. The
contributor will review the final diff and accepts responsibility for the contribution.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Lint, formatting, typechecks, builds, and targeted affected tests pass. The
full Desktop suite has the unrelated Windows failures listed under Verification.

Does this PR entail a change in behavior?

  • Yes - described under Summary above
  • No

@HuYellow
HuYellow marked this pull request as ready for review August 20, 2026 16:34

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Codex review on exact head 40ecd476303d230f601119b1fb2a33f6a134e446 found no actionable code issue.

The bug still exists on current main: detached Runtime Host candidates use ignored stdio, so after transport EOF Desktop cannot report the selected child’s exit code, signal, or stderr. This revision keeps the evidence bounded (4 KiB stderr tail and four Desktop exit records), associates it only when the final registration PID matches a process spawned by this election, redacts before logging/persistence, and keeps detached lifecycle handles unreferenced. The startup-failure path still derives from the same exit evidence, and the process map is election-local rather than long-lived.

The required test check is green. The optional Windows package failure is a renderer CDP smoke timeout; a contemporaneous documentation-only PR failed with the same Packaged Maka renderer did not expose CDP signature, while this run’s earlier Windows package/installer smokes passed. I therefore do not attribute that failure to this diff, though a rerun would provide cleaner evidence.

Required conclusions:

  1. Optimal for the actual problem: yes.
  2. Production code to delete: none identified.
  3. Tests to delete/replace: none identified.
  4. Deeper refactor: no; launcher exit evidence and Desktop presentation remain at the correct boundary.
  5. Ready to merge: no; independent human review is still missing.
  6. Residual risk/gaps: rerun the non-required Windows package smoke; exact-head human judgment remains the merge gate.

This changes user-visible diagnostics and detached-process observability, so independent human review is required under CONTRIBUTING.md. No security, licensing, governance, or public-contract effect was identified.

@HuYellow

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review. I’ll keep the current head unchanged since the required test check is green and no actionable issue was found. A rerun of the optional Windows package check would be appreciated. This PR is ready for independent human review on 40ecd47.

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — this closes a real gap. stdio: 'ignore' meant that the one moment you most need the Host's own words, after its transport hits EOF, was exactly the moment they were guaranteed to be gone. Associating the evidence with the PID the final registration actually selected, rather than with whichever candidate happened to lose, is the detail that makes the report trustworthy, and the dedicated bounded buffer means repeated transport errors cannot push the exits out of the ordinary log.

No [P0]/[P1]. Two [P2]s and two [P3]s below. Both [P2]s are questions rather than defects I can demonstrate — I could not settle either from the diff alone, and in both cases the thing I want is a sentence in the code saying which behaviour was intended.

The one I would most like an answer on is the detached case. stdio for a detached Host changes from 'ignore' to 'pipe', which means the Host's stderr is now connected to a parent that is designed to exit first. 'ignore' pointed at /dev/null and was safe for the entire life of the Host no matter what Desktop did. A pipe is not: once Desktop is gone, the read end is closed, and what happens to the Host's next stderr write depends on details I would rather see pinned down than inferred.

Review assisted by AI (Claude Opus 5). Findings were verified against the files at this head; the reviewer is accountable for them.

Comment threadpackages/runtime-host/src/client/launcher.ts
Comment threadapps/desktop/src/main/runtime-host-desktop-candidate.ts Outdated
Comment threadapps/desktop/src/main/runtime-host-desktop-candidate.ts Outdated
Comment threadpackages/runtime-host/src/client/launcher.ts Outdated
@HuYellow
HuYellowforce-pushed the fix/3333-runtime-host-exit-diagnostics branch from 40ecd47 to 583d62fCompareAugust 22, 2026 15:07
@HuYellow

Copy link
Copy Markdown
ContributorAuthor

Addressed all four review threads on exact head 583d62f7 and resolved the conversations:

  • guarded the detached Host's stderr pipe against the expected EPIPE after its launcher exits, with a survival regression;
  • documented why whole-text and token redaction are both required, with an embedded compact-JSON regression;
  • omitted the stderr/truncation section for a blank trimmed tail;
  • fixed the exact 4096-byte boundary so a complete tail is not reported as truncated.

The branch is rebased onto current main with no conflicts. Local verification is green: full build and typecheck, Biome on all 13 changed files, git diff --check, 4 targeted Runtime Host tests, and 36 targeted Desktop tests.

@Astro-Han, I have re-requested review for this exact head. Could a maintainer also approve the two workflows currently awaiting approval so the required test check can run?

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — you answered all four points from the previous round, and this comment exists mostly to confirm that on the record. Re-reviewed at exact head 583d62f7408805e0ac820249810ff58b92ef7454 against base 4acfa26934ce4b2b385b76f8a11048bb83fab861. None of the six existing reviews are bound to this head, so everything below was re-checked against the current code rather than carried forward.

Each of our four items, verified here:

  • [P2] detached stderr pipe lifetime — fixed.launcher.ts now spawns with ['ignore','ignore','pipe'] behind the MAKA_RUNTIME_HOST_STDERR_PIPE opt-in, and the EPIPE guard in process-diagnostics.ts swallows only EPIPE and rethrows everything else. That distinction matters and it is drawn correctly — a blanket catch here would have hidden the next real write failure. The detached-survival regression covers it.
  • [P2] redactSecrets two-pass semantics — resolved by explanation, which was the right resolution. The comment now states what each pass is for: full-text pattern matching, then per-token structured redaction to catch compact JSON the first pass steps over. The ambiguity was in the reader's head, not the code, so documenting it was the proportionate fix.
  • [P3] orphaned truncation marker on empty stderr — fixed. Empty stderr returns status-only before the marker is attached, with a whitespace-only regression alongside it.
  • [P3] off-by-one at exactly 4096 — fixed.chunk.length > MAX rather than >=, with a regression pinning the exact boundary.

Also checked, and sound:observeCandidateExit listens on 'close' rather than 'exit', so the stderr tail is complete before the record is built — using 'exit' would have truncated exactly the evidence this PR exists to preserve. Diagnostics associate with the finally-registered PID, so a connect-or-spawn race attributes output to the process that actually won. The log buffer is bounded at 16KiB across four entries.

[P3, ordinary path] The survival regression has a read race in the fixture, not in the product.

stderr-after-launcher-exit writes its marker with writeFile(marker, 'alive'), which truncates before it writes, while waitForFileText polls the file every 20ms. A poll landing between truncate and write reads an empty string and the assertion fails on '' !== 'alive'. We reproduced this once in five isolated runs, so it will go red on CI at some rate.

The product behaviour is correct — the marker being written at all is what demonstrates the EPIPE guard works and the process survived. Writing to a temp file and renaming it into place would make the read atomic; retrying on empty would also do it.

On CI: this head has zero check runs. The description reports a passing suite, and we do not doubt it, but nothing has executed against 583d62f7 itself, so there is no independent verification to point at. A push to trigger CI is the remaining gap — it is also what would let this be approved rather than commented on.

Two reviewers went over this independently, and both reached the same conclusion on all four items. Our own run of the host-kernel suite was 48/51, with the three failures being Electron-dependent tests that cannot start on a headless machine (missing libatk-1.0.so.0) — an environment limit, not a result about this change.


This review was AI-assisted. Findings were verified against the exact head listed above; any mistakes are ours to correct — please push back where we got it wrong.

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One follow-up now that the full check set has run on this head.

test is green, but Release Windows check is failingVerify automatic update end to end times out with Maka.exe did not appear among installed processes within 120000ms.

I looked at whether that check is just flaky, and it doesn't appear to be: of the twelve most recent runs, it succeeded on eight branches around the same window and failed on two, one of which is this branch. So it isn't a broken runner.

I have not proven this PR causes it, and I'd rather say so than assert it. But the inline note below is where I'd start looking — the failing step is a detached-spawn scenario, and this is the PR that changes how that spawn's stdio is wired.

Everything from my previous review still stands: all four earlier points are resolved, and the only other open item is the P3 test race in the fixture.


This review was AI-assisted. Findings were verified against the exact head listed above; any mistakes are mine to correct — please push back where I got it wrong.

Comment threadpackages/runtime-host/src/client/launcher.ts
Comment threadpackages/runtime-host/src/client/launcher.ts
@Astro-Han

Copy link
Copy Markdown
Contributor

Hi — this PR conflicts with current main and cannot be merged as-is.

I tested a rebase onto current main locally (in a throwaway worktree — your branch was not touched). It stops on these files:

  • apps/desktop/src/main/main-process-diagnostics.ts
  • packages/runtime-host/src/__tests__/host-kernel.test.ts
  • packages/runtime-host/src/client/connect-or-spawn.ts
  • …(more)

These are real source conflicts, so they need your judgement rather than a mechanical rebase — please rebase onto current main and resolve them yourself, then push. Once the branch is conflict-free and CI is green on the new head, I will pick it up for review.

git fetch upstream && git rebase upstream/main
# resolve, then
git push --force-with-lease

Thanks for the contribution — happy to help if any conflict is unclear.


AI-assisted maintenance note, not a review. It does not count as the required human review under CONTRIBUTING.md §Review.

@HuYellow
HuYellowforce-pushed the fix/3333-runtime-host-exit-diagnostics branch from 583d62f to 2f7c10dCompareAugust 23, 2026 08:21
@HuYellow

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han Rebased onto current main (729839ed8ada3e5498b0ad27e64fb42dfd283ae7) and force-pushed exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb.

I manually resolved the five conflicts, preserving the new election diagnostics and keeping current-run Runtime Host exits out of previous-main-process reports. I also addressed the two follow-ups:

  • A truncated stderr tail now drops its potentially partial leading record before redaction. The regression constructs a secret straddling the 4096-byte cut, confirms the retained raw tail still contains the secret fragment, and verifies that the formatted Desktop diagnostic does not.
  • The survival fixture now writes a unique sibling temporary file and atomically renames it into place, removing the truncate/write polling race.

Local verification on Windows is green:

  • npm run build
  • npm run typecheck
  • Biome on all 15 changed files
  • npm run check:stale
  • git diff --check origin/main...HEAD
  • 13 targeted Runtime Host election/detached stderr tests
  • 9 owned-candidate tests
  • 41 Desktop candidate/diagnostics/recovery tests

The exact detached-process regression where the launcher exits and the Host later writes stderr passes on this head. GitHub currently shows the new head without source conflicts; test is pending and two workflows are awaiting maintainer approval. Please take another look at 2f7c10d when CI is available.

@HuYellow

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han All requested follow-ups are now complete on exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb: the branch is rebased onto current main, all review threads are resolved, and both GitHub checks are green (required CI / test in 10m and Release Windows check / package in 11m). The PR is now blocked only on the required approving review. Please complete the pending review when you have a chance.

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Representative review under @WAWQAQ's standing apache/maka review authorization, bound to exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb.

GO: no P0–P2 findings. I verified the bounded stderr-tail collection, final-registration PID association, redaction/truncation path, detached-process behavior, and the dedicated copied-diagnostics path. The affected Runtime Host tests passed 5/5, Desktop diagnostics/candidate tests passed 41/41, and removing the selected-process handoff made its targeted regression fail before restoration. The exact-head test and Windows package check-runs are both completed/success; the PR is open, non-draft, and mergeable, with no current live hold.

One non-blocking P3 is recorded inline. This agent-executed approval does not claim to satisfy any separate repository human-review requirement. No merge performed.

maxBytes: MAIN_PROCESS_DIAGNOSTIC_LOG_MAX_BYTES,
});
export const runtimeHostProcessLogBuffer = new DiagnosticLogBuffer({
maxBytes: 16 * 1024,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] The promised four-exit history only retains three maximum-size records. This buffer combines maxBytes: 16 * 1024 with maxEntries: 4, but each formatted 4096-byte stderr-tail diagnostic is larger than 4 KiB once the timestamp, level, exit status, truncation marker, and JSON string encoding are included. Reproduction on this production buffer: append four such records; snapshot() returns 3 entries (4206 encoded bytes each), retaining PIDs 2/3/4 and evicting PID 1. The current diagnostic test injects only one short record, so it cannot catch the contract gap. Please either size the total cap for four worst-case formatted entries or impose a per-entry bound that makes the stated four-entry retention true, and add a four-maximum-entry regression.

@Astro-Han
Astro-Han merged commit 6cfc888 into apache:mainAug 23, 2026
2 checks passed
mikemikimike pushed a commit to mikemikimike/maka that referenced this pull request Aug 23, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Desktop conversation reads fail while the local Runtime Host stays unavailable after transport EOF

3 participants

@HuYellow@Astro-Han@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(desktop): preserve Runtime Host exit diagnostics - #3344

Merged
Astro-Han merged 1 commit into
apache:mainfrom
HuYellow:fix/3333-runtime-host-exit-diagnostics
Aug 23, 2026
Merged

fix(desktop): preserve Runtime Host exit diagnostics#3344
Astro-Han merged 1 commit into
apache:mainfrom
HuYellow:fix/3333-runtime-host-exit-diagnostics

Conversation

@HuYellow

@HuYellowHuYellow commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

The detached local Runtime Host was launched with ignored stdio, so after its
transport reached EOF Desktop had no process exit code, signal, or Host stderr
to include in the diagnostic report.

This change captures a bounded 4 KiB stderr tail and exit status while
preserving detached-process behavior, associates that evidence only with the
spawned PID selected by the final Host registration, and records a redacted
diagnostic in both the main-process log and a dedicated bounded process-exit
buffer. Copied Desktop diagnostics retain the last four local Host exits even
when repeated transport errors fill the ordinary log buffer and the Host is no
longer available.

Fixes#3333

Verification

  • npx biome check <13 changed files>
  • npm run build
  • npm run typecheck
  • git diff --check
  • 4 targeted Runtime Host tests passed, covering selected-PID association,
    detached stderr after launcher exit, bounded stderr, and the exact 4096-byte
    boundary
  • 36 targeted Desktop tests passed, covering stderr redaction and process-exit
    evidence in copied diagnostics

The full Desktop suite was also run on Windows: 955 tests passed, 17 unrelated
existing environment-dependent tests failed, and 7 were skipped. The failures
were Windows symlink EPERM, SQLite cleanup EBUSY, and Rive CLI fixture
failures. All tests added or changed by this PR pass.

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: OpenAI Codex assisted with root-cause analysis,
implementation, regression tests, local verification, and PR drafting. The
contributor will review the final diff and accepts responsibility for the contribution.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Lint, formatting, typechecks, builds, and targeted affected tests pass. The
full Desktop suite has the unrelated Windows failures listed under Verification.

Does this PR entail a change in behavior?

  • Yes - described under Summary above
  • No

@HuYellow
HuYellow marked this pull request as ready for review August 20, 2026 16:34

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Codex review on exact head 40ecd476303d230f601119b1fb2a33f6a134e446 found no actionable code issue.

The bug still exists on current main: detached Runtime Host candidates use ignored stdio, so after transport EOF Desktop cannot report the selected child’s exit code, signal, or stderr. This revision keeps the evidence bounded (4 KiB stderr tail and four Desktop exit records), associates it only when the final registration PID matches a process spawned by this election, redacts before logging/persistence, and keeps detached lifecycle handles unreferenced. The startup-failure path still derives from the same exit evidence, and the process map is election-local rather than long-lived.

The required test check is green. The optional Windows package failure is a renderer CDP smoke timeout; a contemporaneous documentation-only PR failed with the same Packaged Maka renderer did not expose CDP signature, while this run’s earlier Windows package/installer smokes passed. I therefore do not attribute that failure to this diff, though a rerun would provide cleaner evidence.

Required conclusions:

  1. Optimal for the actual problem: yes.
  2. Production code to delete: none identified.
  3. Tests to delete/replace: none identified.
  4. Deeper refactor: no; launcher exit evidence and Desktop presentation remain at the correct boundary.
  5. Ready to merge: no; independent human review is still missing.
  6. Residual risk/gaps: rerun the non-required Windows package smoke; exact-head human judgment remains the merge gate.

This changes user-visible diagnostics and detached-process observability, so independent human review is required under CONTRIBUTING.md. No security, licensing, governance, or public-contract effect was identified.

@HuYellow

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review. I’ll keep the current head unchanged since the required test check is green and no actionable issue was found. A rerun of the optional Windows package check would be appreciated. This PR is ready for independent human review on 40ecd47.

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — this closes a real gap. stdio: 'ignore' meant that the one moment you most need the Host's own words, after its transport hits EOF, was exactly the moment they were guaranteed to be gone. Associating the evidence with the PID the final registration actually selected, rather than with whichever candidate happened to lose, is the detail that makes the report trustworthy, and the dedicated bounded buffer means repeated transport errors cannot push the exits out of the ordinary log.

No [P0]/[P1]. Two [P2]s and two [P3]s below. Both [P2]s are questions rather than defects I can demonstrate — I could not settle either from the diff alone, and in both cases the thing I want is a sentence in the code saying which behaviour was intended.

The one I would most like an answer on is the detached case. stdio for a detached Host changes from 'ignore' to 'pipe', which means the Host's stderr is now connected to a parent that is designed to exit first. 'ignore' pointed at /dev/null and was safe for the entire life of the Host no matter what Desktop did. A pipe is not: once Desktop is gone, the read end is closed, and what happens to the Host's next stderr write depends on details I would rather see pinned down than inferred.

Review assisted by AI (Claude Opus 5). Findings were verified against the files at this head; the reviewer is accountable for them.

Comment threadpackages/runtime-host/src/client/launcher.ts
Comment threadapps/desktop/src/main/runtime-host-desktop-candidate.ts Outdated
Comment threadapps/desktop/src/main/runtime-host-desktop-candidate.ts Outdated
Comment threadpackages/runtime-host/src/client/launcher.ts Outdated
@HuYellow
HuYellowforce-pushed the fix/3333-runtime-host-exit-diagnostics branch from 40ecd47 to 583d62fCompareAugust 22, 2026 15:07
@HuYellow

Copy link
Copy Markdown
ContributorAuthor

Addressed all four review threads on exact head 583d62f7 and resolved the conversations:

  • guarded the detached Host's stderr pipe against the expected EPIPE after its launcher exits, with a survival regression;
  • documented why whole-text and token redaction are both required, with an embedded compact-JSON regression;
  • omitted the stderr/truncation section for a blank trimmed tail;
  • fixed the exact 4096-byte boundary so a complete tail is not reported as truncated.

The branch is rebased onto current main with no conflicts. Local verification is green: full build and typecheck, Biome on all 13 changed files, git diff --check, 4 targeted Runtime Host tests, and 36 targeted Desktop tests.

@Astro-Han, I have re-requested review for this exact head. Could a maintainer also approve the two workflows currently awaiting approval so the required test check can run?

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — you answered all four points from the previous round, and this comment exists mostly to confirm that on the record. Re-reviewed at exact head 583d62f7408805e0ac820249810ff58b92ef7454 against base 4acfa26934ce4b2b385b76f8a11048bb83fab861. None of the six existing reviews are bound to this head, so everything below was re-checked against the current code rather than carried forward.

Each of our four items, verified here:

  • [P2] detached stderr pipe lifetime — fixed.launcher.ts now spawns with ['ignore','ignore','pipe'] behind the MAKA_RUNTIME_HOST_STDERR_PIPE opt-in, and the EPIPE guard in process-diagnostics.ts swallows only EPIPE and rethrows everything else. That distinction matters and it is drawn correctly — a blanket catch here would have hidden the next real write failure. The detached-survival regression covers it.
  • [P2] redactSecrets two-pass semantics — resolved by explanation, which was the right resolution. The comment now states what each pass is for: full-text pattern matching, then per-token structured redaction to catch compact JSON the first pass steps over. The ambiguity was in the reader's head, not the code, so documenting it was the proportionate fix.
  • [P3] orphaned truncation marker on empty stderr — fixed. Empty stderr returns status-only before the marker is attached, with a whitespace-only regression alongside it.
  • [P3] off-by-one at exactly 4096 — fixed.chunk.length > MAX rather than >=, with a regression pinning the exact boundary.

Also checked, and sound:observeCandidateExit listens on 'close' rather than 'exit', so the stderr tail is complete before the record is built — using 'exit' would have truncated exactly the evidence this PR exists to preserve. Diagnostics associate with the finally-registered PID, so a connect-or-spawn race attributes output to the process that actually won. The log buffer is bounded at 16KiB across four entries.

[P3, ordinary path] The survival regression has a read race in the fixture, not in the product.

stderr-after-launcher-exit writes its marker with writeFile(marker, 'alive'), which truncates before it writes, while waitForFileText polls the file every 20ms. A poll landing between truncate and write reads an empty string and the assertion fails on '' !== 'alive'. We reproduced this once in five isolated runs, so it will go red on CI at some rate.

The product behaviour is correct — the marker being written at all is what demonstrates the EPIPE guard works and the process survived. Writing to a temp file and renaming it into place would make the read atomic; retrying on empty would also do it.

On CI: this head has zero check runs. The description reports a passing suite, and we do not doubt it, but nothing has executed against 583d62f7 itself, so there is no independent verification to point at. A push to trigger CI is the remaining gap — it is also what would let this be approved rather than commented on.

Two reviewers went over this independently, and both reached the same conclusion on all four items. Our own run of the host-kernel suite was 48/51, with the three failures being Electron-dependent tests that cannot start on a headless machine (missing libatk-1.0.so.0) — an environment limit, not a result about this change.


This review was AI-assisted. Findings were verified against the exact head listed above; any mistakes are ours to correct — please push back where we got it wrong.

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One follow-up now that the full check set has run on this head.

test is green, but Release Windows check is failingVerify automatic update end to end times out with Maka.exe did not appear among installed processes within 120000ms.

I looked at whether that check is just flaky, and it doesn't appear to be: of the twelve most recent runs, it succeeded on eight branches around the same window and failed on two, one of which is this branch. So it isn't a broken runner.

I have not proven this PR causes it, and I'd rather say so than assert it. But the inline note below is where I'd start looking — the failing step is a detached-spawn scenario, and this is the PR that changes how that spawn's stdio is wired.

Everything from my previous review still stands: all four earlier points are resolved, and the only other open item is the P3 test race in the fixture.


This review was AI-assisted. Findings were verified against the exact head listed above; any mistakes are mine to correct — please push back where I got it wrong.

Comment threadpackages/runtime-host/src/client/launcher.ts
Comment threadpackages/runtime-host/src/client/launcher.ts
@Astro-Han

Copy link
Copy Markdown
Contributor

Hi — this PR conflicts with current main and cannot be merged as-is.

I tested a rebase onto current main locally (in a throwaway worktree — your branch was not touched). It stops on these files:

  • apps/desktop/src/main/main-process-diagnostics.ts
  • packages/runtime-host/src/__tests__/host-kernel.test.ts
  • packages/runtime-host/src/client/connect-or-spawn.ts
  • …(more)

These are real source conflicts, so they need your judgement rather than a mechanical rebase — please rebase onto current main and resolve them yourself, then push. Once the branch is conflict-free and CI is green on the new head, I will pick it up for review.

git fetch upstream && git rebase upstream/main
# resolve, then
git push --force-with-lease

Thanks for the contribution — happy to help if any conflict is unclear.


AI-assisted maintenance note, not a review. It does not count as the required human review under CONTRIBUTING.md §Review.

@HuYellow
HuYellowforce-pushed the fix/3333-runtime-host-exit-diagnostics branch from 583d62f to 2f7c10dCompareAugust 23, 2026 08:21
@HuYellow

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han Rebased onto current main (729839ed8ada3e5498b0ad27e64fb42dfd283ae7) and force-pushed exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb.

I manually resolved the five conflicts, preserving the new election diagnostics and keeping current-run Runtime Host exits out of previous-main-process reports. I also addressed the two follow-ups:

  • A truncated stderr tail now drops its potentially partial leading record before redaction. The regression constructs a secret straddling the 4096-byte cut, confirms the retained raw tail still contains the secret fragment, and verifies that the formatted Desktop diagnostic does not.
  • The survival fixture now writes a unique sibling temporary file and atomically renames it into place, removing the truncate/write polling race.

Local verification on Windows is green:

  • npm run build
  • npm run typecheck
  • Biome on all 15 changed files
  • npm run check:stale
  • git diff --check origin/main...HEAD
  • 13 targeted Runtime Host election/detached stderr tests
  • 9 owned-candidate tests
  • 41 Desktop candidate/diagnostics/recovery tests

The exact detached-process regression where the launcher exits and the Host later writes stderr passes on this head. GitHub currently shows the new head without source conflicts; test is pending and two workflows are awaiting maintainer approval. Please take another look at 2f7c10d when CI is available.

@HuYellow

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han All requested follow-ups are now complete on exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb: the branch is rebased onto current main, all review threads are resolved, and both GitHub checks are green (required CI / test in 10m and Release Windows check / package in 11m). The PR is now blocked only on the required approving review. Please complete the pending review when you have a chance.

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Representative review under @WAWQAQ's standing apache/maka review authorization, bound to exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb.

GO: no P0–P2 findings. I verified the bounded stderr-tail collection, final-registration PID association, redaction/truncation path, detached-process behavior, and the dedicated copied-diagnostics path. The affected Runtime Host tests passed 5/5, Desktop diagnostics/candidate tests passed 41/41, and removing the selected-process handoff made its targeted regression fail before restoration. The exact-head test and Windows package check-runs are both completed/success; the PR is open, non-draft, and mergeable, with no current live hold.

One non-blocking P3 is recorded inline. This agent-executed approval does not claim to satisfy any separate repository human-review requirement. No merge performed.

maxBytes: MAIN_PROCESS_DIAGNOSTIC_LOG_MAX_BYTES,
});
export const runtimeHostProcessLogBuffer = new DiagnosticLogBuffer({
maxBytes: 16 * 1024,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] The promised four-exit history only retains three maximum-size records. This buffer combines maxBytes: 16 * 1024 with maxEntries: 4, but each formatted 4096-byte stderr-tail diagnostic is larger than 4 KiB once the timestamp, level, exit status, truncation marker, and JSON string encoding are included. Reproduction on this production buffer: append four such records; snapshot() returns 3 entries (4206 encoded bytes each), retaining PIDs 2/3/4 and evicting PID 1. The current diagnostic test injects only one short record, so it cannot catch the contract gap. Please either size the total cap for four worst-case formatted entries or impose a per-entry bound that makes the stated four-entry retention true, and add a four-maximum-entry regression.

@Astro-Han
Astro-Han merged commit 6cfc888 into apache:mainAug 23, 2026
2 checks passed
mikemikimike pushed a commit to mikemikimike/maka that referenced this pull request Aug 23, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Desktop conversation reads fail while the local Runtime Host stays unavailable after transport EOF

3 participants

@HuYellow@Astro-Han@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(desktop): preserve Runtime Host exit diagnostics - #3344

Merged
Astro-Han merged 1 commit into
apache:mainfrom
HuYellow:fix/3333-runtime-host-exit-diagnostics
Aug 23, 2026
Merged

fix(desktop): preserve Runtime Host exit diagnostics#3344
Astro-Han merged 1 commit into
apache:mainfrom
HuYellow:fix/3333-runtime-host-exit-diagnostics

Conversation

@HuYellow

@HuYellowHuYellow commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

The detached local Runtime Host was launched with ignored stdio, so after its
transport reached EOF Desktop had no process exit code, signal, or Host stderr
to include in the diagnostic report.

This change captures a bounded 4 KiB stderr tail and exit status while
preserving detached-process behavior, associates that evidence only with the
spawned PID selected by the final Host registration, and records a redacted
diagnostic in both the main-process log and a dedicated bounded process-exit
buffer. Copied Desktop diagnostics retain the last four local Host exits even
when repeated transport errors fill the ordinary log buffer and the Host is no
longer available.

Fixes#3333

Verification

  • npx biome check <13 changed files>
  • npm run build
  • npm run typecheck
  • git diff --check
  • 4 targeted Runtime Host tests passed, covering selected-PID association,
    detached stderr after launcher exit, bounded stderr, and the exact 4096-byte
    boundary
  • 36 targeted Desktop tests passed, covering stderr redaction and process-exit
    evidence in copied diagnostics

The full Desktop suite was also run on Windows: 955 tests passed, 17 unrelated
existing environment-dependent tests failed, and 7 were skipped. The failures
were Windows symlink EPERM, SQLite cleanup EBUSY, and Rive CLI fixture
failures. All tests added or changed by this PR pass.

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: OpenAI Codex assisted with root-cause analysis,
implementation, regression tests, local verification, and PR drafting. The
contributor will review the final diff and accepts responsibility for the contribution.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Lint, formatting, typechecks, builds, and targeted affected tests pass. The
full Desktop suite has the unrelated Windows failures listed under Verification.

Does this PR entail a change in behavior?

  • Yes - described under Summary above
  • No

@HuYellow
HuYellow marked this pull request as ready for review August 20, 2026 16:34

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Codex review on exact head 40ecd476303d230f601119b1fb2a33f6a134e446 found no actionable code issue.

The bug still exists on current main: detached Runtime Host candidates use ignored stdio, so after transport EOF Desktop cannot report the selected child’s exit code, signal, or stderr. This revision keeps the evidence bounded (4 KiB stderr tail and four Desktop exit records), associates it only when the final registration PID matches a process spawned by this election, redacts before logging/persistence, and keeps detached lifecycle handles unreferenced. The startup-failure path still derives from the same exit evidence, and the process map is election-local rather than long-lived.

The required test check is green. The optional Windows package failure is a renderer CDP smoke timeout; a contemporaneous documentation-only PR failed with the same Packaged Maka renderer did not expose CDP signature, while this run’s earlier Windows package/installer smokes passed. I therefore do not attribute that failure to this diff, though a rerun would provide cleaner evidence.

Required conclusions:

  1. Optimal for the actual problem: yes.
  2. Production code to delete: none identified.
  3. Tests to delete/replace: none identified.
  4. Deeper refactor: no; launcher exit evidence and Desktop presentation remain at the correct boundary.
  5. Ready to merge: no; independent human review is still missing.
  6. Residual risk/gaps: rerun the non-required Windows package smoke; exact-head human judgment remains the merge gate.

This changes user-visible diagnostics and detached-process observability, so independent human review is required under CONTRIBUTING.md. No security, licensing, governance, or public-contract effect was identified.

@HuYellow

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review. I’ll keep the current head unchanged since the required test check is green and no actionable issue was found. A rerun of the optional Windows package check would be appreciated. This PR is ready for independent human review on 40ecd47.

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — this closes a real gap. stdio: 'ignore' meant that the one moment you most need the Host's own words, after its transport hits EOF, was exactly the moment they were guaranteed to be gone. Associating the evidence with the PID the final registration actually selected, rather than with whichever candidate happened to lose, is the detail that makes the report trustworthy, and the dedicated bounded buffer means repeated transport errors cannot push the exits out of the ordinary log.

No [P0]/[P1]. Two [P2]s and two [P3]s below. Both [P2]s are questions rather than defects I can demonstrate — I could not settle either from the diff alone, and in both cases the thing I want is a sentence in the code saying which behaviour was intended.

The one I would most like an answer on is the detached case. stdio for a detached Host changes from 'ignore' to 'pipe', which means the Host's stderr is now connected to a parent that is designed to exit first. 'ignore' pointed at /dev/null and was safe for the entire life of the Host no matter what Desktop did. A pipe is not: once Desktop is gone, the read end is closed, and what happens to the Host's next stderr write depends on details I would rather see pinned down than inferred.

Review assisted by AI (Claude Opus 5). Findings were verified against the files at this head; the reviewer is accountable for them.

Comment threadpackages/runtime-host/src/client/launcher.ts
Comment threadapps/desktop/src/main/runtime-host-desktop-candidate.ts Outdated
Comment threadapps/desktop/src/main/runtime-host-desktop-candidate.ts Outdated
Comment threadpackages/runtime-host/src/client/launcher.ts Outdated
@HuYellow
HuYellowforce-pushed the fix/3333-runtime-host-exit-diagnostics branch from 40ecd47 to 583d62fCompareAugust 22, 2026 15:07
@HuYellow

Copy link
Copy Markdown
ContributorAuthor

Addressed all four review threads on exact head 583d62f7 and resolved the conversations:

  • guarded the detached Host's stderr pipe against the expected EPIPE after its launcher exits, with a survival regression;
  • documented why whole-text and token redaction are both required, with an embedded compact-JSON regression;
  • omitted the stderr/truncation section for a blank trimmed tail;
  • fixed the exact 4096-byte boundary so a complete tail is not reported as truncated.

The branch is rebased onto current main with no conflicts. Local verification is green: full build and typecheck, Biome on all 13 changed files, git diff --check, 4 targeted Runtime Host tests, and 36 targeted Desktop tests.

@Astro-Han, I have re-requested review for this exact head. Could a maintainer also approve the two workflows currently awaiting approval so the required test check can run?

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — you answered all four points from the previous round, and this comment exists mostly to confirm that on the record. Re-reviewed at exact head 583d62f7408805e0ac820249810ff58b92ef7454 against base 4acfa26934ce4b2b385b76f8a11048bb83fab861. None of the six existing reviews are bound to this head, so everything below was re-checked against the current code rather than carried forward.

Each of our four items, verified here:

  • [P2] detached stderr pipe lifetime — fixed.launcher.ts now spawns with ['ignore','ignore','pipe'] behind the MAKA_RUNTIME_HOST_STDERR_PIPE opt-in, and the EPIPE guard in process-diagnostics.ts swallows only EPIPE and rethrows everything else. That distinction matters and it is drawn correctly — a blanket catch here would have hidden the next real write failure. The detached-survival regression covers it.
  • [P2] redactSecrets two-pass semantics — resolved by explanation, which was the right resolution. The comment now states what each pass is for: full-text pattern matching, then per-token structured redaction to catch compact JSON the first pass steps over. The ambiguity was in the reader's head, not the code, so documenting it was the proportionate fix.
  • [P3] orphaned truncation marker on empty stderr — fixed. Empty stderr returns status-only before the marker is attached, with a whitespace-only regression alongside it.
  • [P3] off-by-one at exactly 4096 — fixed.chunk.length > MAX rather than >=, with a regression pinning the exact boundary.

Also checked, and sound:observeCandidateExit listens on 'close' rather than 'exit', so the stderr tail is complete before the record is built — using 'exit' would have truncated exactly the evidence this PR exists to preserve. Diagnostics associate with the finally-registered PID, so a connect-or-spawn race attributes output to the process that actually won. The log buffer is bounded at 16KiB across four entries.

[P3, ordinary path] The survival regression has a read race in the fixture, not in the product.

stderr-after-launcher-exit writes its marker with writeFile(marker, 'alive'), which truncates before it writes, while waitForFileText polls the file every 20ms. A poll landing between truncate and write reads an empty string and the assertion fails on '' !== 'alive'. We reproduced this once in five isolated runs, so it will go red on CI at some rate.

The product behaviour is correct — the marker being written at all is what demonstrates the EPIPE guard works and the process survived. Writing to a temp file and renaming it into place would make the read atomic; retrying on empty would also do it.

On CI: this head has zero check runs. The description reports a passing suite, and we do not doubt it, but nothing has executed against 583d62f7 itself, so there is no independent verification to point at. A push to trigger CI is the remaining gap — it is also what would let this be approved rather than commented on.

Two reviewers went over this independently, and both reached the same conclusion on all four items. Our own run of the host-kernel suite was 48/51, with the three failures being Electron-dependent tests that cannot start on a headless machine (missing libatk-1.0.so.0) — an environment limit, not a result about this change.


This review was AI-assisted. Findings were verified against the exact head listed above; any mistakes are ours to correct — please push back where we got it wrong.

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One follow-up now that the full check set has run on this head.

test is green, but Release Windows check is failingVerify automatic update end to end times out with Maka.exe did not appear among installed processes within 120000ms.

I looked at whether that check is just flaky, and it doesn't appear to be: of the twelve most recent runs, it succeeded on eight branches around the same window and failed on two, one of which is this branch. So it isn't a broken runner.

I have not proven this PR causes it, and I'd rather say so than assert it. But the inline note below is where I'd start looking — the failing step is a detached-spawn scenario, and this is the PR that changes how that spawn's stdio is wired.

Everything from my previous review still stands: all four earlier points are resolved, and the only other open item is the P3 test race in the fixture.


This review was AI-assisted. Findings were verified against the exact head listed above; any mistakes are mine to correct — please push back where I got it wrong.

Comment threadpackages/runtime-host/src/client/launcher.ts
Comment threadpackages/runtime-host/src/client/launcher.ts
@Astro-Han

Copy link
Copy Markdown
Contributor

Hi — this PR conflicts with current main and cannot be merged as-is.

I tested a rebase onto current main locally (in a throwaway worktree — your branch was not touched). It stops on these files:

  • apps/desktop/src/main/main-process-diagnostics.ts
  • packages/runtime-host/src/__tests__/host-kernel.test.ts
  • packages/runtime-host/src/client/connect-or-spawn.ts
  • …(more)

These are real source conflicts, so they need your judgement rather than a mechanical rebase — please rebase onto current main and resolve them yourself, then push. Once the branch is conflict-free and CI is green on the new head, I will pick it up for review.

git fetch upstream && git rebase upstream/main
# resolve, then
git push --force-with-lease

Thanks for the contribution — happy to help if any conflict is unclear.


AI-assisted maintenance note, not a review. It does not count as the required human review under CONTRIBUTING.md §Review.

@HuYellow
HuYellowforce-pushed the fix/3333-runtime-host-exit-diagnostics branch from 583d62f to 2f7c10dCompareAugust 23, 2026 08:21
@HuYellow

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han Rebased onto current main (729839ed8ada3e5498b0ad27e64fb42dfd283ae7) and force-pushed exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb.

I manually resolved the five conflicts, preserving the new election diagnostics and keeping current-run Runtime Host exits out of previous-main-process reports. I also addressed the two follow-ups:

  • A truncated stderr tail now drops its potentially partial leading record before redaction. The regression constructs a secret straddling the 4096-byte cut, confirms the retained raw tail still contains the secret fragment, and verifies that the formatted Desktop diagnostic does not.
  • The survival fixture now writes a unique sibling temporary file and atomically renames it into place, removing the truncate/write polling race.

Local verification on Windows is green:

  • npm run build
  • npm run typecheck
  • Biome on all 15 changed files
  • npm run check:stale
  • git diff --check origin/main...HEAD
  • 13 targeted Runtime Host election/detached stderr tests
  • 9 owned-candidate tests
  • 41 Desktop candidate/diagnostics/recovery tests

The exact detached-process regression where the launcher exits and the Host later writes stderr passes on this head. GitHub currently shows the new head without source conflicts; test is pending and two workflows are awaiting maintainer approval. Please take another look at 2f7c10d when CI is available.

@HuYellow

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han All requested follow-ups are now complete on exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb: the branch is rebased onto current main, all review threads are resolved, and both GitHub checks are green (required CI / test in 10m and Release Windows check / package in 11m). The PR is now blocked only on the required approving review. Please complete the pending review when you have a chance.

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Representative review under @WAWQAQ's standing apache/maka review authorization, bound to exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb.

GO: no P0–P2 findings. I verified the bounded stderr-tail collection, final-registration PID association, redaction/truncation path, detached-process behavior, and the dedicated copied-diagnostics path. The affected Runtime Host tests passed 5/5, Desktop diagnostics/candidate tests passed 41/41, and removing the selected-process handoff made its targeted regression fail before restoration. The exact-head test and Windows package check-runs are both completed/success; the PR is open, non-draft, and mergeable, with no current live hold.

One non-blocking P3 is recorded inline. This agent-executed approval does not claim to satisfy any separate repository human-review requirement. No merge performed.

maxBytes: MAIN_PROCESS_DIAGNOSTIC_LOG_MAX_BYTES,
});
export const runtimeHostProcessLogBuffer = new DiagnosticLogBuffer({
maxBytes: 16 * 1024,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] The promised four-exit history only retains three maximum-size records. This buffer combines maxBytes: 16 * 1024 with maxEntries: 4, but each formatted 4096-byte stderr-tail diagnostic is larger than 4 KiB once the timestamp, level, exit status, truncation marker, and JSON string encoding are included. Reproduction on this production buffer: append four such records; snapshot() returns 3 entries (4206 encoded bytes each), retaining PIDs 2/3/4 and evicting PID 1. The current diagnostic test injects only one short record, so it cannot catch the contract gap. Please either size the total cap for four worst-case formatted entries or impose a per-entry bound that makes the stated four-entry retention true, and add a four-maximum-entry regression.

@Astro-Han
Astro-Han merged commit 6cfc888 into apache:mainAug 23, 2026
2 checks passed
mikemikimike pushed a commit to mikemikimike/maka that referenced this pull request Aug 23, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Desktop conversation reads fail while the local Runtime Host stays unavailable after transport EOF

3 participants

@HuYellow@Astro-Han@jackwener
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(desktop): preserve Runtime Host exit diagnostics - #3344

Merged
Astro-Han merged 1 commit into
apache:mainfrom
HuYellow:fix/3333-runtime-host-exit-diagnostics
Aug 23, 2026
Merged

fix(desktop): preserve Runtime Host exit diagnostics#3344
Astro-Han merged 1 commit into
apache:mainfrom
HuYellow:fix/3333-runtime-host-exit-diagnostics

Conversation

@HuYellow

@HuYellowHuYellow commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

The detached local Runtime Host was launched with ignored stdio, so after its
transport reached EOF Desktop had no process exit code, signal, or Host stderr
to include in the diagnostic report.

This change captures a bounded 4 KiB stderr tail and exit status while
preserving detached-process behavior, associates that evidence only with the
spawned PID selected by the final Host registration, and records a redacted
diagnostic in both the main-process log and a dedicated bounded process-exit
buffer. Copied Desktop diagnostics retain the last four local Host exits even
when repeated transport errors fill the ordinary log buffer and the Host is no
longer available.

Fixes#3333

Verification

  • npx biome check <13 changed files>
  • npm run build
  • npm run typecheck
  • git diff --check
  • 4 targeted Runtime Host tests passed, covering selected-PID association,
    detached stderr after launcher exit, bounded stderr, and the exact 4096-byte
    boundary
  • 36 targeted Desktop tests passed, covering stderr redaction and process-exit
    evidence in copied diagnostics

The full Desktop suite was also run on Windows: 955 tests passed, 17 unrelated
existing environment-dependent tests failed, and 7 were skipped. The failures
were Windows symlink EPERM, SQLite cleanup EBUSY, and Rive CLI fixture
failures. All tests added or changed by this PR pass.

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: OpenAI Codex assisted with root-cause analysis,
implementation, regression tests, local verification, and PR drafting. The
contributor will review the final diff and accepts responsibility for the contribution.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Lint, formatting, typechecks, builds, and targeted affected tests pass. The
full Desktop suite has the unrelated Windows failures listed under Verification.

Does this PR entail a change in behavior?

  • Yes - described under Summary above
  • No

@HuYellow
HuYellow marked this pull request as ready for review August 20, 2026 16:34

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Codex review on exact head 40ecd476303d230f601119b1fb2a33f6a134e446 found no actionable code issue.

The bug still exists on current main: detached Runtime Host candidates use ignored stdio, so after transport EOF Desktop cannot report the selected child’s exit code, signal, or stderr. This revision keeps the evidence bounded (4 KiB stderr tail and four Desktop exit records), associates it only when the final registration PID matches a process spawned by this election, redacts before logging/persistence, and keeps detached lifecycle handles unreferenced. The startup-failure path still derives from the same exit evidence, and the process map is election-local rather than long-lived.

The required test check is green. The optional Windows package failure is a renderer CDP smoke timeout; a contemporaneous documentation-only PR failed with the same Packaged Maka renderer did not expose CDP signature, while this run’s earlier Windows package/installer smokes passed. I therefore do not attribute that failure to this diff, though a rerun would provide cleaner evidence.

Required conclusions:

  1. Optimal for the actual problem: yes.
  2. Production code to delete: none identified.
  3. Tests to delete/replace: none identified.
  4. Deeper refactor: no; launcher exit evidence and Desktop presentation remain at the correct boundary.
  5. Ready to merge: no; independent human review is still missing.
  6. Residual risk/gaps: rerun the non-required Windows package smoke; exact-head human judgment remains the merge gate.

This changes user-visible diagnostics and detached-process observability, so independent human review is required under CONTRIBUTING.md. No security, licensing, governance, or public-contract effect was identified.

@HuYellow

Copy link
Copy Markdown
ContributorAuthor

Thanks for the review. I’ll keep the current head unchanged since the required test check is green and no actionable issue was found. A rerun of the optional Windows package check would be appreciated. This PR is ready for independent human review on 40ecd47.

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — this closes a real gap. stdio: 'ignore' meant that the one moment you most need the Host's own words, after its transport hits EOF, was exactly the moment they were guaranteed to be gone. Associating the evidence with the PID the final registration actually selected, rather than with whichever candidate happened to lose, is the detail that makes the report trustworthy, and the dedicated bounded buffer means repeated transport errors cannot push the exits out of the ordinary log.

No [P0]/[P1]. Two [P2]s and two [P3]s below. Both [P2]s are questions rather than defects I can demonstrate — I could not settle either from the diff alone, and in both cases the thing I want is a sentence in the code saying which behaviour was intended.

The one I would most like an answer on is the detached case. stdio for a detached Host changes from 'ignore' to 'pipe', which means the Host's stderr is now connected to a parent that is designed to exit first. 'ignore' pointed at /dev/null and was safe for the entire life of the Host no matter what Desktop did. A pipe is not: once Desktop is gone, the read end is closed, and what happens to the Host's next stderr write depends on details I would rather see pinned down than inferred.

Review assisted by AI (Claude Opus 5). Findings were verified against the files at this head; the reviewer is accountable for them.

Comment threadpackages/runtime-host/src/client/launcher.ts
Comment threadapps/desktop/src/main/runtime-host-desktop-candidate.ts Outdated
Comment threadapps/desktop/src/main/runtime-host-desktop-candidate.ts Outdated
Comment threadpackages/runtime-host/src/client/launcher.ts Outdated
@HuYellow
HuYellowforce-pushed the fix/3333-runtime-host-exit-diagnostics branch from 40ecd47 to 583d62fCompareAugust 22, 2026 15:07
@HuYellow

Copy link
Copy Markdown
ContributorAuthor

Addressed all four review threads on exact head 583d62f7 and resolved the conversations:

  • guarded the detached Host's stderr pipe against the expected EPIPE after its launcher exits, with a survival regression;
  • documented why whole-text and token redaction are both required, with an embedded compact-JSON regression;
  • omitted the stderr/truncation section for a blank trimmed tail;
  • fixed the exact 4096-byte boundary so a complete tail is not reported as truncated.

The branch is rebased onto current main with no conflicts. Local verification is green: full build and typecheck, Biome on all 13 changed files, git diff --check, 4 targeted Runtime Host tests, and 36 targeted Desktop tests.

@Astro-Han, I have re-requested review for this exact head. Could a maintainer also approve the two workflows currently awaiting approval so the required test check can run?

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — you answered all four points from the previous round, and this comment exists mostly to confirm that on the record. Re-reviewed at exact head 583d62f7408805e0ac820249810ff58b92ef7454 against base 4acfa26934ce4b2b385b76f8a11048bb83fab861. None of the six existing reviews are bound to this head, so everything below was re-checked against the current code rather than carried forward.

Each of our four items, verified here:

  • [P2] detached stderr pipe lifetime — fixed.launcher.ts now spawns with ['ignore','ignore','pipe'] behind the MAKA_RUNTIME_HOST_STDERR_PIPE opt-in, and the EPIPE guard in process-diagnostics.ts swallows only EPIPE and rethrows everything else. That distinction matters and it is drawn correctly — a blanket catch here would have hidden the next real write failure. The detached-survival regression covers it.
  • [P2] redactSecrets two-pass semantics — resolved by explanation, which was the right resolution. The comment now states what each pass is for: full-text pattern matching, then per-token structured redaction to catch compact JSON the first pass steps over. The ambiguity was in the reader's head, not the code, so documenting it was the proportionate fix.
  • [P3] orphaned truncation marker on empty stderr — fixed. Empty stderr returns status-only before the marker is attached, with a whitespace-only regression alongside it.
  • [P3] off-by-one at exactly 4096 — fixed.chunk.length > MAX rather than >=, with a regression pinning the exact boundary.

Also checked, and sound:observeCandidateExit listens on 'close' rather than 'exit', so the stderr tail is complete before the record is built — using 'exit' would have truncated exactly the evidence this PR exists to preserve. Diagnostics associate with the finally-registered PID, so a connect-or-spawn race attributes output to the process that actually won. The log buffer is bounded at 16KiB across four entries.

[P3, ordinary path] The survival regression has a read race in the fixture, not in the product.

stderr-after-launcher-exit writes its marker with writeFile(marker, 'alive'), which truncates before it writes, while waitForFileText polls the file every 20ms. A poll landing between truncate and write reads an empty string and the assertion fails on '' !== 'alive'. We reproduced this once in five isolated runs, so it will go red on CI at some rate.

The product behaviour is correct — the marker being written at all is what demonstrates the EPIPE guard works and the process survived. Writing to a temp file and renaming it into place would make the read atomic; retrying on empty would also do it.

On CI: this head has zero check runs. The description reports a passing suite, and we do not doubt it, but nothing has executed against 583d62f7 itself, so there is no independent verification to point at. A push to trigger CI is the remaining gap — it is also what would let this be approved rather than commented on.

Two reviewers went over this independently, and both reached the same conclusion on all four items. Our own run of the host-kernel suite was 48/51, with the three failures being Electron-dependent tests that cannot start on a headless machine (missing libatk-1.0.so.0) — an environment limit, not a result about this change.


This review was AI-assisted. Findings were verified against the exact head listed above; any mistakes are ours to correct — please push back where we got it wrong.

@Astro-HanAstro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One follow-up now that the full check set has run on this head.

test is green, but Release Windows check is failingVerify automatic update end to end times out with Maka.exe did not appear among installed processes within 120000ms.

I looked at whether that check is just flaky, and it doesn't appear to be: of the twelve most recent runs, it succeeded on eight branches around the same window and failed on two, one of which is this branch. So it isn't a broken runner.

I have not proven this PR causes it, and I'd rather say so than assert it. But the inline note below is where I'd start looking — the failing step is a detached-spawn scenario, and this is the PR that changes how that spawn's stdio is wired.

Everything from my previous review still stands: all four earlier points are resolved, and the only other open item is the P3 test race in the fixture.


This review was AI-assisted. Findings were verified against the exact head listed above; any mistakes are mine to correct — please push back where I got it wrong.

Comment threadpackages/runtime-host/src/client/launcher.ts
Comment threadpackages/runtime-host/src/client/launcher.ts
@Astro-Han

Copy link
Copy Markdown
Contributor

Hi — this PR conflicts with current main and cannot be merged as-is.

I tested a rebase onto current main locally (in a throwaway worktree — your branch was not touched). It stops on these files:

  • apps/desktop/src/main/main-process-diagnostics.ts
  • packages/runtime-host/src/__tests__/host-kernel.test.ts
  • packages/runtime-host/src/client/connect-or-spawn.ts
  • …(more)

These are real source conflicts, so they need your judgement rather than a mechanical rebase — please rebase onto current main and resolve them yourself, then push. Once the branch is conflict-free and CI is green on the new head, I will pick it up for review.

git fetch upstream && git rebase upstream/main
# resolve, then
git push --force-with-lease

Thanks for the contribution — happy to help if any conflict is unclear.


AI-assisted maintenance note, not a review. It does not count as the required human review under CONTRIBUTING.md §Review.

@HuYellow
HuYellowforce-pushed the fix/3333-runtime-host-exit-diagnostics branch from 583d62f to 2f7c10dCompareAugust 23, 2026 08:21
@HuYellow

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han Rebased onto current main (729839ed8ada3e5498b0ad27e64fb42dfd283ae7) and force-pushed exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb.

I manually resolved the five conflicts, preserving the new election diagnostics and keeping current-run Runtime Host exits out of previous-main-process reports. I also addressed the two follow-ups:

  • A truncated stderr tail now drops its potentially partial leading record before redaction. The regression constructs a secret straddling the 4096-byte cut, confirms the retained raw tail still contains the secret fragment, and verifies that the formatted Desktop diagnostic does not.
  • The survival fixture now writes a unique sibling temporary file and atomically renames it into place, removing the truncate/write polling race.

Local verification on Windows is green:

  • npm run build
  • npm run typecheck
  • Biome on all 15 changed files
  • npm run check:stale
  • git diff --check origin/main...HEAD
  • 13 targeted Runtime Host election/detached stderr tests
  • 9 owned-candidate tests
  • 41 Desktop candidate/diagnostics/recovery tests

The exact detached-process regression where the launcher exits and the Host later writes stderr passes on this head. GitHub currently shows the new head without source conflicts; test is pending and two workflows are awaiting maintainer approval. Please take another look at 2f7c10d when CI is available.

@HuYellow

Copy link
Copy Markdown
ContributorAuthor

@Astro-Han All requested follow-ups are now complete on exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb: the branch is rebased onto current main, all review threads are resolved, and both GitHub checks are green (required CI / test in 10m and Release Windows check / package in 11m). The PR is now blocked only on the required approving review. Please complete the pending review when you have a chance.

@jackwenerjackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Representative review under @WAWQAQ's standing apache/maka review authorization, bound to exact head 2f7c10dbab4bab45856a5e5e57eaaad843843bfb.

GO: no P0–P2 findings. I verified the bounded stderr-tail collection, final-registration PID association, redaction/truncation path, detached-process behavior, and the dedicated copied-diagnostics path. The affected Runtime Host tests passed 5/5, Desktop diagnostics/candidate tests passed 41/41, and removing the selected-process handoff made its targeted regression fail before restoration. The exact-head test and Windows package check-runs are both completed/success; the PR is open, non-draft, and mergeable, with no current live hold.

One non-blocking P3 is recorded inline. This agent-executed approval does not claim to satisfy any separate repository human-review requirement. No merge performed.

maxBytes: MAIN_PROCESS_DIAGNOSTIC_LOG_MAX_BYTES,
});
export const runtimeHostProcessLogBuffer = new DiagnosticLogBuffer({
maxBytes: 16 * 1024,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] The promised four-exit history only retains three maximum-size records. This buffer combines maxBytes: 16 * 1024 with maxEntries: 4, but each formatted 4096-byte stderr-tail diagnostic is larger than 4 KiB once the timestamp, level, exit status, truncation marker, and JSON string encoding are included. Reproduction on this production buffer: append four such records; snapshot() returns 3 entries (4206 encoded bytes each), retaining PIDs 2/3/4 and evicting PID 1. The current diagnostic test injects only one short record, so it cannot catch the contract gap. Please either size the total cap for four worst-case formatted entries or impose a per-entry bound that makes the stated four-entry retention true, and add a four-maximum-entry regression.

@Astro-Han
Astro-Han merged commit 6cfc888 into apache:mainAug 23, 2026
2 checks passed
mikemikimike pushed a commit to mikemikimike/maka that referenced this pull request Aug 23, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Desktop conversation reads fail while the local Runtime Host stays unavailable after transport EOF

3 participants

@HuYellow@Astro-Han@jackwener