Skip to content

chore: quality gates — CI, coverage thresholds, UI tests, static analysis, docs - #2

Merged
hutusi merged 15 commits into
mainfrom
chore/quality-gates
Jul 10, 2026
Merged

hutusi merged 15 commits into
mainfrom
chore/quality-gates

Conversation

@hutusi

@hutusi hutusi commented Jul 10, 2026 •

Copy link
Copy Markdown
Owner

Why

v0.1 merged with a solid local verify gate (typecheck + biome + tests) but nothing enforcing it: no CI, the only TUI check lived outside the repo, the CLI package had zero automated coverage, and the workflow/protocol knowledge existed only in heads. This hardens all of that before M2 (Tauri GUI) piles new code on top.

What

  • CI (.github/workflows/ci.yml): verify matrix on ubuntu+macos (typecheck, lint, tests, knip, per-file coverage thresholds), TUI PTY smoke+e2e via expect, compiled-binary smoke on ubuntu (macOS excluded: known Bun unsigned-binary SIGKILL, see DESIGN watchlist), and a live-model smoke that runs only on main pushes with the ANTHROPIC_API_KEY secret — PRs never see the secret and it skips quietly when absent. Plus Dependabot (AI SDK packages grouped — they version in lockstep).
  • Coverage thresholds (bunfig.toml): bun enforces them per file, not on the total (discovered empirically); started at 0.60, ratcheted to 0.80 within this PR after closing gaps. Totals now 96.5% functions / 97.8% lines.
  • CLI testability: argv parsing extracted to a pure parseCliArgs with unit tests; full-stack Ink UI tests via ink-testing-library (real kernel + scripted provider under the components); PTY scripts committed to scripts/ including a genuine pseudo-terminal e2e of a complete conversation with permission approval.
  • Static analysis: exactOptionalPropertyTypes + noImplicitOverride, Biome noExplicitAny at error, knip in the gate (it immediately caught an unlisted binary and a dead export). PermissionBridge became a factory — bun's function-coverage counter never credits field-initializer-only classes.
  • Docs: CONTRIBUTING.md (workflow, gate, test layout), docs/PROTOCOL.md (the wire reference M2 builds against), CHANGELOG.md (0.1.0 + unreleased), README badge/links.

Verification

This PR's own CI run is the deliverable proving itself — all jobs on both OSes, with live-smoke correctly skipped (PR context). Locally: bun run verify green (110 tests), bun test --coverage passes the 0.80 floor, bun run knip clean, both PTY scripts pass.

Summary by CodeRabbit

  • New Features
    • Added clearer CLI help and argument handling, including model selection, session resume, and TUI/ACP commands.
    • Improved permission handling when no interactive handler is available.
  • Documentation
    • Added wire protocol documentation, contribution guidelines, release history, known issues, and consolidated README links.
  • Bug Fixes
    • Improved handling of optional configuration values and tool/provider data.
    • Added safeguards for failed tools, interrupted turns, malformed settings, and replayed session events.
  • Tests
    • Expanded coverage for CLI behavior, interactive sessions, permissions, plans, and end-to-end workflows.

hutusi added 8 commits July 10, 2026 23:20
Until now the verify gate only existed when someone ran it locally.
Four jobs: verify matrix on ubuntu+macos (typecheck, lint, tests, knip,
coverage with bunfig thresholds), TUI PTY smoke+e2e via expect,
compiled-binary smoke on ubuntu only (macOS excluded deliberately — the
Bun unsigned-binary SIGKILL issue from the design watchlist), and a
live-model smoke that runs solely on main pushes with the
ANTHROPIC_API_KEY secret so PRs never see the secret and per-PR token
spend stays zero; it skips quietly when the secret is absent.

Dependabot groups the AI SDK packages (they version in lockstep — see
the @ai-sdk/openai v3/v4 spec mismatch) and dev tooling, weekly, plus
github-actions updates.
Coverage stays opt-in (--coverage) so the local verify loop stays fast;
CI runs the enforcing pass. Bun applies coverageThreshold per file, not
to the total — discovered empirically: totals of 93/96% still fail at
0.80 because three tool files sit at 66.67% function coverage. The
threshold starts at 0.60, just under today's worst file, and ratchets to
0.80 in the coverage-gap commit later in this PR. Documented in the file
so nobody 'fixes' it back to a total-based mental model.
… knip

exactOptionalPropertyTypes and noImplicitOverride are cheap now and
painful to adopt later. The fallout was uniform: internal interfaces
whose optional properties legitimately receive computed
possibly-undefined values gain an explicit `| undefined`, while
external types (Ink props, MCP SDK transport params, AI SDK streamText)
get conditional spreads at the call site so we never pass an explicit
undefined into someone else's optional.

noExplicitAny is promoted from warn to error (already clean), and knip
joins the gate for dead files, exports, and dependencies — the one
static-analysis category nothing else covered in a workspace. It runs
as `bun run knip` in the CI test job.
The argv loop lived as top-level script code interleaved with
process.exit calls — untestable without spawning a process. It is now a
pure function returning run/help/error, unit-tested for every flag,
value-missing and flag-as-value errors, command/flag ordering, and the
usage text; the entrypoint just switches on the result. Behavior is
unchanged (same messages, same exit codes).
The CLI package had zero automated coverage. These render the real App
against a real kernel + scripted provider (nothing mocked below the
terminal): command palette (/help, unknown command), a complete prompt
with streamed text, permission approval and completed tool render,
permission rejection marking the tool failed while the turn continues,
the todo checklist, and the /mode indicator.

Two Ink-under-test quirks are encoded in the helpers rather than
sprinkled as sleeps: a chunk containing \r is treated by TextInput as a
paste (Enter must be its own keystroke), and writes that land before
TextInput's stdin listener attaches — right after first render or after
the input remounts post-turn — are dropped, so type() settles first.
Verified stable across repeated runs.
The render smoke lived outside the repo as a scratch file — the only
check that the terminal UI actually draws was unreproducible. Both
scripts now live in scripts/ and run in CI's tui-smoke job:

smoke-tui.exp starts the real entrypoint under a PTY, asserts banner +
input prompt, and exits via /exit. e2e-tui.exp drives a complete
scripted conversation through the genuine Ink UI — typed prompt,
streamed assistant text, permission approval keystroke, completed tool
output — against the tui-scripted fixture (real kernel + scripted
provider, no network). Complements the ink-testing-library tests: those
exercise components in-process; these prove the same flows through an
actual pseudo-terminal.

knip immediately earned its keep: expect is declared as a system binary
and CliArgs lost an export nothing imported.
Targeted tests for the uncovered branches the report pointed at: every
tool's title() (including malformed-input throws that exercise the
loop's fallback), runtime exec spawn failure, todo_write without its
loop hook, corrupt settings JSON, unknown-tool calls, a tool throwing
mid-execution, provider stream errors producing turn.failed, mid-turn
cancellation synthesizing results for pending tool calls (via a
hand-rolled slow provider), replay's orphan-result/unknown-event/
missing-tool paths, and the store's plan-replacement and mode handling.

PermissionBridge becomes a factory function instead of a class: bun's
function-coverage counter never credits a class whose only members are
field initializers, which held the file at 50% regardless of tests. The
factory reads better anyway.

Totals now 96.5% functions / 97.8% lines with every file at or above
0.80/0.80 — the bunfig threshold ratchets from 0.60 to 0.80 as planned.
CONTRIBUTING.md captures the workflow that was previously tribal
knowledge: the verify gate and what CI enforces beyond it, branch/commit
conventions (conventional commits with why-bodies, no-squash merges),
the test layout, and MINERVA_DATA_DIR isolation for manual runs.

docs/PROTOCOL.md is the wire reference the M2 GUI and any external
frontend will build against: framing, both transports, every ACP-core
method with params/results, all session/update variants, permission
options and outcome semantics (including the cancelled-outcome and
transport-failure rules), the minerva/* extensions, and the versioning
policy for what does and does not bump PROTOCOL_VERSION.

CHANGELOG.md starts at 0.1.0 (Keep a Changelog), with the quality-gates
work under Unreleased. README gains the CI badge and doc links.
@coderabbitai

coderabbitai Bot commented Jul 10, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@hutusi, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 56 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aa0e5736-bd53-40b7-8616-d98df2d276b6

📥 Commits

Reviewing files that changed from the base of the PR and between 8e370d2 and 4538c60.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • .github/workflows/ci.yml
  • CHANGELOG.md
  • packages/cli/package.json
  • packages/cli/test/app.test.tsx
  • scripts/e2e-tui.exp
  • scripts/live-smoke.ts
  • scripts/smoke-tui.exp
📝 Walkthrough

Walkthrough

The pull request adds strict TypeScript settings, CLI parsing, permission-bridge changes, TUI and kernel coverage, CI workflows, smoke tests, dependency automation, protocol documentation, and contribution and release documentation.

Changes

Repository validation and contracts

Layer / File(s) Summary
Strict optional contracts and adapter handling
tsconfig.json, biome.json, bunfig.toml, packages/*/src/*
Enables stricter TypeScript checks, updates optional field types, and omits undefined MCP and AI SDK options.
Kernel and store edge-case coverage
packages/kernel/test/gaps.test.ts, packages/client/test/store.test.ts
Adds coverage for runtime failures, agent-loop behavior, replay tolerance, plan updates, and mode handling.

CLI and automation

Layer / File(s) Summary
CLI parsing and TUI permission flow
packages/cli/src/*, packages/cli/test/*
Adds structured argument parsing, usage generation, factory-based permission handling, and Ink TUI tests.
CI, dependency automation, and smoke execution
.github/*, package.json, knip.json, scripts/*
Adds scheduled dependency updates, CI matrices, release checks, PTY smoke tests, and guarded live-model validation.
Protocol and repository documentation
docs/PROTOCOL.md, CONTRIBUTING.md, README.md, CHANGELOG.md
Documents protocol messages and versioning, contribution rules, repository links, and release history.

Estimated code review effort: 4 (Complex) | ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change set: added quality gates, CI, testing, static analysis, and docs updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/quality-gates

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

hutusi added 2 commits July 10, 2026 23:44
Marking it --external made the compile succeed but deferred resolution
to runtime, where the single-file executable has no node_modules —
'Cannot find package react-devtools-core' on launch (caught by the new
release-smoke job on its very first run). Ink's devtools hook is now a
real devDependency of the CLI so the bundle is self-contained; it only
activates under DEV=true at runtime.
The tui-smoke job failed with 'no banner' after 20 silent seconds — the
expect blocks only handled the match and timeout, so a process that died
instantly was indistinguishable from one that never drew. Both scripts
now handle eof explicitly, and the job first runs the entrypoint
headless (--help) so bun/module resolution failures surface as their own
step with a real error message instead of a PTY timeout.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (2)
.github/workflows/ci.yml (1)

77-92: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Guard against silent skip masking real failures in live-smoke.

The live-smoke job correctly restricts to main-branch pushes, and scripts/live-smoke.ts exits 0 when ANTHROPIC_API_KEY is unset. However, if the secret is never configured, this job always passes without actually testing anything. Consider adding a comment or step that surfaces whether the secret was present, so the team can distinguish "skipped" from "passed" in CI logs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 77 - 92, Add an explicit step in the
live-smoke job before running scripts/live-smoke.ts that reports whether
ANTHROPIC_API_KEY is configured, using a masked presence check without printing
the secret; clearly label the no-secret case as skipped and the configured case
as running, while preserving the existing secure behavior.
packages/cli/test/app.test.tsx (1)

94-98: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider adding a settle delay before permission keystrokes.

The type helper at line 54 waits 50ms before writing to avoid dropped keystrokes when TextInput remounts. However, ui.stdin.write("y") at line 97 (and ui.stdin.write("n") at line 123) write immediately after waitFor confirms the prompt is visible. If the permission prompt's stdin listener isn't attached when the frame renders, the keystroke could be dropped. Adding a short await Bun.sleep(50) before these writes would match the type helper's defensive pattern and reduce flakiness risk.

♻️ Proposed fix
     await waitFor(() => (ui.lastFrame() ?? "").includes("Permission required"), "permission");
     expect(ui.lastFrame()).toContain("echo ui-e2e");
 
+    await Bun.sleep(50);
     ui.stdin.write("y");

Apply the same pattern at line 123:

     await waitFor(() => (ui.lastFrame() ?? "").includes("Permission required"), "permission");
+    await Bun.sleep(50);
     ui.stdin.write("n");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/test/app.test.tsx` around lines 94 - 98, Add a 50ms settle delay
before the direct permission responses in the test: insert await Bun.sleep(50)
after the permission prompt wait and before ui.stdin.write("y"), and apply the
same delay before the corresponding ui.stdin.write("n") in the other permission
test. Match the existing type helper timing pattern.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 52-53: Fix the YAML syntax in the sanity-check step named “Sanity
— entrypoint runs headless in this job context” by changing its run command to a
block scalar (|), preserving the existing command and grep pattern so the
colon-space sequence is parsed safely.

In `@CHANGELOG.md`:
- Around line 10-12: Update the CHANGELOG entry to use the standard “macOS”
capitalization instead of “macos” in the CI platform list.

In `@packages/cli/package.json`:
- Around line 22-24: Update the `ink-testing-library` dependency in
`packages/cli/package.json` to a major version compatible with the project’s
`ink@^6` and React 19 versions, or remove it if no CLI tests use it; verify the
affected CLI test imports and dependency lockfile are consistent.

In `@packages/cli/test/app.test.tsx`:
- Around line 17-30: Update renderTui to retain the MinervaKernel returned by
createKernel, expose or register it for cleanup, and close it from an afterEach
hook so every test disposes the kernel and its transports/MCP connections.

In `@scripts/live-smoke.ts`:
- Around line 20-50: Wrap the live smoke test workflow beginning with client
initialization and ending after the success check in a try/catch. In the catch
block, call clearTimeout(timeout), log the actual error with clear context, and
exit with status 1; retain the existing success-path timer cleanup and
validation. Use the existing timeout, client.initialize, client.newSession, and
client.prompt symbols to locate the changes.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 77-92: Add an explicit step in the live-smoke job before running
scripts/live-smoke.ts that reports whether ANTHROPIC_API_KEY is configured,
using a masked presence check without printing the secret; clearly label the
no-secret case as skipped and the configured case as running, while preserving
the existing secure behavior.

In `@packages/cli/test/app.test.tsx`:
- Around line 94-98: Add a 50ms settle delay before the direct permission
responses in the test: insert await Bun.sleep(50) after the permission prompt
wait and before ui.stdin.write("y"), and apply the same delay before the
corresponding ui.stdin.write("n") in the other permission test. Match the
existing type helper timing pattern.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c99c185a-a20d-49dd-9f6d-8d27b93ac448

📥 Commits

Reviewing files that changed from the base of the PR and between ce189b4 and 8e370d2.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (38)
  • .github/dependabot.yml
  • .github/workflows/ci.yml
  • CHANGELOG.md
  • CONTRIBUTING.md
  • README.md
  • biome.json
  • bunfig.toml
  • docs/PROTOCOL.md
  • knip.json
  • package.json
  • packages/cli/package.json
  • packages/cli/src/app.tsx
  • packages/cli/src/args.ts
  • packages/cli/src/index.tsx
  • packages/cli/src/permission-bridge.ts
  • packages/cli/test/app.test.tsx
  • packages/cli/test/args.test.ts
  • packages/cli/test/fixtures/tui-scripted.tsx
  • packages/cli/test/permission-bridge.test.ts
  • packages/client/src/store.ts
  • packages/client/test/store.test.ts
  • packages/kernel/src/agent-loop.ts
  • packages/kernel/src/events.ts
  • packages/kernel/src/kernel.ts
  • packages/kernel/src/mcp.ts
  • packages/kernel/src/permissions.ts
  • packages/kernel/src/replay.ts
  • packages/kernel/src/runtime.ts
  • packages/kernel/src/settings.ts
  • packages/kernel/src/tools/types.ts
  • packages/kernel/test/gaps.test.ts
  • packages/protocol/src/types.ts
  • packages/providers/src/ai-sdk.ts
  • packages/providers/src/types.ts
  • scripts/e2e-tui.exp
  • scripts/live-smoke.ts
  • scripts/smoke-tui.exp
  • tsconfig.json

Comment thread .github/workflows/ci.yml Outdated
Comment thread CHANGELOG.md Outdated
Comment thread packages/cli/package.json
Comment on lines +22 to +24
"@types/react": "^19",
"ink-testing-library": "^4.0.0",
"react-devtools-core": "^7.0.1"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the ink version declared in the CLI package or root package.json
fd package.json packages/cli --max-depth 1 --exec cat {} \; | jq '.dependencies.ink, .devDependencies.ink'
# Also check root
cat package.json | jq '.dependencies.ink, .devDependencies.ink'

Repository: hutusi/minerva

Length of output: 172


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== packages/cli/package.json =="
cat -n packages/cli/package.json | sed -n '1,120p'

echo
echo "== root package.json =="
cat -n package.json | sed -n '1,220p'

echo
echo "== lockfiles mentioning ink-testing-library / ink / react =="
rg -n '"ink-testing-library"|"ink"|"react"' -g 'pnpm-lock.yaml' -g 'package-lock.json' -g 'yarn.lock' -g 'npm-shrinkwrap.json' .

echo
echo "== package manifests in repo mentioning ink-testing-library =="
rg -n '"ink-testing-library"|ink-testing-library' -g 'package.json' .

Repository: hutusi/minerva

Length of output: 1884


🌐 Web query:

npm ink-testing-library 4.0.0 peerDependencies ink 6 React 19

💡 Result:

The npm package ink-testing-library version 4.0.0 does not officially list ink 6 or React 19 in its peerDependencies [1][2]. While attempts have been made by the community to update ink-testing-library for compatibility with Ink 6 and React 19 [3], these changes were not merged into the 4.0.0 release, which was published on May 22, 2024 [1][4]. Users have reported significant compatibility issues when using ink-testing-library 4.0.0 with newer versions of Ink (such as v5 and v6) [3][5], particularly regarding input handling (useInput) and React 19's synchronous effect execution [3][5]. Consequently, ink-testing-library 4.0.0 is generally considered incompatible with Ink 6 and React 19 in its current state [3][5].

Citations:


Align ink-testing-library with ink@^6 packages/cli/package.json:22-23 pins ink-testing-library@^4.0.0, which doesn’t match the current ink@^6 / React 19 stack. Update it to a compatible major or remove it if the CLI tests no longer need it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/package.json` around lines 22 - 24, Update the
`ink-testing-library` dependency in `packages/cli/package.json` to a major
version compatible with the project’s `ink@^6` and React 19 versions, or remove
it if no CLI tests use it; verify the affected CLI test imports and dependency
lockfile are consistent.

Comment thread packages/cli/test/app.test.tsx
Comment thread scripts/live-smoke.ts Outdated
hutusi added 5 commits July 11, 2026 00:13
The step's unquoted run scalar contained 'Usage: minerva' — the
colon-space made the workflow file unparseable, failing the run in zero
seconds. Block scalar sidesteps YAML's flow-scalar colon rule; the file
is now validated locally before pushing.
The tui-smoke job timed out waiting for the banner while the app was
demonstrably alive (the new eof branch never fired and the headless
sanity step passed). The pattern 'Minerva \u00b7' contains a UTF-8
middle dot; under the runner's C locale Tcl's encoding mangles it and
the match never fires — locally, with a UTF-8 locale, it always did.
Patterns are now plain ASCII and the spawned process gets LANG=C.UTF-8
so the middle dot renders consistently either way.
Root cause of the tui-smoke timeouts: Ink detects CI (ci-info reads CI,
GITHUB_ACTIONS, ...) and disables interactive rendering, drawing only on
unmount — so under expect's PTY the process was alive and intentionally
silent. The whole point of these scripts is that expect IS the terminal,
so they now unset the CI markers for the spawned process. The earlier
eof-vs-timeout diagnostics and headless sanity step are what narrowed
this from 'no banner' to 'alive but not drawing'.
On the Linux runner the spawned PTY reports 0x0; Ink wraps to the
reported width, emitting one character per line, so no multi-character
pattern could ever match (macOS PTYs default to 80x24, which is why the
same scripts passed there and locally). stty_init pins 120x40 for both
platforms.
Four of five findings actioned (the fifth, the YAML block-scalar fix,
had already landed in an earlier commit):

- app.test.tsx now retains each MinervaKernel and closes it in
  afterEach, so transports don't leak across tests.
- ink-testing-library is pinned to exactly 4.0.0 with a comment: it
  predates ink 6 / React 19 and no compatible major exists (upstream
  PRs unmerged), so our passing suite is the compatibility proof and an
  unreviewed 4.x bump must not slip in via install. Upgrading, as the
  review suggested, is not currently possible.
- live-smoke wraps its flow in try/catch for a labeled failure message.
  Note the review's premise was slightly off: Bun exits immediately on
  an unhandled top-level rejection rather than hanging until the timer —
  the wrap improves diagnostics, not liveness.
- CHANGELOG: macOS capitalization.
@hutusi
hutusi merged commit 03f5de9 into main Jul 10, 2026
7 checks passed
@hutusi
hutusi deleted the chore/quality-gates branch July 22, 2026 01:19
Sign up for free to 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.

1 participant