Skip to content

fix: retry version resolution, and stop examples passing on exit 0 (#121, #102) - #134

Merged
mobileskyfi merged 4 commits into
mainfrom
fix/121-102-version-retry-and-example-false-greens
Aug 3, 2026
Merged

fix: retry version resolution, and stop examples passing on exit 0 (#121, #102)#134
mobileskyfi merged 4 commits into
mainfrom
fix/121-102-version-retry-and-example-false-greens

Conversation

@mobileskyfi

@mobileskyfi mobileskyfi commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Closes #121. Closes #102.

Two independent false signals, both already diagnosed in their issues.

#121resolveVersion() had no retry, and the fallback's error hid the real one

fetchResilient() is one attempt per transport: a plain fetch on the system
resolver, then — only on a connection-class error — a public-DNS A record →
IPv4 literal with Host/SNI preserved. If that second attempt also threw, the
error propagated. #116/#119 gave downloadToFile() a retry policy and left
version resolution with a single shot, even though it sits on the critical path
of start() (quickchr.ts:1404, :1523, :1531), the wizard and the CLI. One
refused connection reddened a full integration leg on a run where nothing about
RouterOS, QEMU or the platform was involved.

  • resolveVersion() retries a connection-class failure up to 3× with the same
    backoff shape as downloadToFile, and names the attempt count and host when
    it exhausts them. An HTTP status is still terminal on the first attempt —
    a 404 for a bad channel stays fast (asserted).
  • fetchResilient() no longer lets the fallback's error stand alone. That error
    reports an IP literal and no hostname, which leaves "the system resolver was
    broken", "the host refused us" and "public DNS handed back a bad address"
    indistinguishable — exactly the question a CI log has to answer. Both failures
    now raise a ResilientFetchError naming the URL, each transport's error, the
    address the fallback used and which resolvers produced it, with the direct
    failure kept as cause. isConnectionFailure() recognizes it (it is
    connection-class by construction), so a caller retrying connection failures
    keeps retrying once the fallback fails too.

Deliberately not included: #121's item 3 (trying more than one A record /
preferring the system resolver's answer). The issue asks for hosted-runner
evidence that the balancer is what refused us before touching that — and the
chained error above is what will supply it on the next occurrence.

#102code === 0 + out.length > 0 is not evidence an example worked

The open half of #102. A ParserError in common.ps1 left every helper
undefined, so quickstart.ps1 ran no quickchr command at all — printed one
line, exited 0, and the smoke harness reported (pass) for months. The parse
itself was fixed in #101; this closes the class:

  • Output markers. Each RUNNABLE in examples-smoke.test.ts names
    substrings only a working run produces — always one from the end of the
    script (a mid-run silent failure must not pass) and one the CLI/library
    emitted rather than the example's own echo. The interpolated
    examples-<slug>- machine name is deliberate: the tell in examples: common.ps1 ParserError made every PowerShell example a silent false green #102 was a double
    space where that name should have been. The failure reports the list of
    missing markers, so "wrong result" reads differently from "did nothing".
  • .ps1 arms $ErrorActionPreference = 'Stop' before dot-sourcing,
    duplicating what common.ps1 sets. Load-bearing: if common.ps1 fails to
    load, the preference it sets never takes effect and every undefined helper is
    an ordinary non-terminating CommandNotFound. scripts/validate-examples.ts
    enforces it (cheap gate, no QEMU).
  • examples/mndp/mndp.py had the same bug in Python. Found while choosing
    its markers: the "no MNDP announcement" branch used a bare return, which
    leaves main() through the finally and never reaches the trailing
    sys.exit(rc) — so the failure path exited 0. Now raise SystemExit;
    cleanup still runs.

Grounding (Intel macOS, 2026-08-03)

Claim How
The .ps1 guard converts the false green pwsh 7.4.6 against the real files with #102's $LASTEXITCODE: reinstated: unguarded prints -> starting (stable channel...) (double space) and exits 0; guarded exits 1 at the dot-source, having run nothing
The mndp.py fix identity match forced to fail, real CHR boot: rc=0 before, rc=1 after, cleanup ran in both
Every stdout marker real CHR boots of quickstart.ts, rollback.ts, quickstart.sh and mndp.py — 0 markers missing in each
The .ps1 edits pass their own CI gates ParseFile over all nine .ps1 + PSScriptAnalyzer with the repo settings, run locally: clean (it caught an em dash I had introduced into common.ps1PSUseBOMForUnicodeEncodedFile)
Retry / error-chaining behavior unit tests against a mocked fetch — no test depends on MikroTik's server being flaky

bun run check and bun test (960 pass / 0 fail) are green.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved RouterOS version resolution by retrying connection failures while keeping HTTP and cancellation errors immediate.
    • Network errors now retain details from both direct and fallback connection attempts.
    • MNDP discovery now exits with a failure when no device announcement is found.
  • Validation

    • Example checks now detect silent PowerShell failures and require meaningful output.
    • Updated examples to stop execution when errors occur.
    • Expanded coverage for retries, network failures, and example validation.

, #102)

Two independent false signals, both diagnosed in their issues.

#121 — resolveVersion() had no retry. fetchResilient() is one attempt per
transport (system resolver, then a public-DNS IPv4 fallback); if the fallback
also threw, the error propagated. #116/#119 gave the download path a retry
policy and left version resolution — on the critical path of start(), the
wizard and the CLI — with a single shot, so one refused connection reddened a
whole leg. It now retries a connection-class failure 3x with downloadToFile's
backoff shape and names the attempts and host on exhaustion; an HTTP status
stays terminal on the first attempt so a bad channel still fails fast.

Its second half: the fallback's error was the only one surfaced, so the message
read "https://159.148.147.251/routeros/..." with no hostname — the one thing
that cannot be mapped back to what was attempted. Both failures now raise a
ResilientFetchError naming the URL, each transport's error, the fallback
address and which resolvers produced it, with the direct failure as `cause`.

#102 — the smoke harness asserted `code === 0` and `out.length > 0`, which a
PowerShell example that ran no quickchr command at all satisfied for months.
Closing the class, not the instance:

  - each RUNNABLE names output markers only a working run produces, including
    one from the END of the script and one the CLI itself emitted;
  - each .ps1 sets $ErrorActionPreference = 'Stop' BEFORE dot-sourcing
    common.ps1, so a common.ps1 that fails to load can no longer let the
    example run past it (validate-examples enforces this);
  - mndp.py's "no announcement" branch raised the same false green in Python —
    a bare return leaves main() through the finally and never reaches
    sys.exit(rc). It raises SystemExit now.

Grounded locally, Intel macOS 2026-08-03. pwsh 7.4.6 against the real files
with #102's ParserError reinstated: rc=0 unguarded (reproducing "-> starting
 (stable channel...)", double space and all), rc=1 guarded. mndp.py with the
identity match forced to fail: rc=0 before, rc=1 after. Every stdout marker
verified against a real CHR boot (quickstart.ts, rollback.ts, quickstart.sh,
mndp.py). PSScriptAnalyzer + the ParseFile gate run clean on all nine .ps1.

Not included: #121's item 3 (trying more than one A record / preferring the
system resolver's answer). It needs evidence from a hosted runner that the
balancer is what refused us; the chained error above is what will supply it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 3, 2026 21:23
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 30ec915a-0ecd-4f13-9dc6-49e5b50a95fa

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds retry handling and combined transport errors to RouterOS version resolution. It also strengthens example validation with output markers, PowerShell terminating errors, and non-zero MNDP failure exits. Tests and changelog entries cover these behaviors.

Changes

Network resilience

Layer / File(s) Summary
Transport error aggregation
src/lib/net.ts, test/unit/net.test.ts, CHANGELOG.md
fetchResilient now preserves direct and IPv4 fallback failures in ResilientFetchError. Tests verify error details and connection classification.
Version resolution retry policy
src/lib/versions.ts, test/unit/versions.test.ts, CHANGELOG.md
resolveVersion retries connection failures with configurable attempts and linear backoff. HTTP errors and aborts remain terminal.

Example failure detection

Layer / File(s) Summary
Example validation and PowerShell guards
examples/common.ps1, examples/*/*.ps1, scripts/validate-examples.ts, .github/instructions/examples.instructions.md, CHANGELOG.md
PowerShell examples set terminating error handling before loading helpers. Validation checks this ordering for dot-sourced scripts.
Smoke-test success markers
test/integration/examples-smoke.test.ts, .github/instructions/examples.instructions.md, CHANGELOG.md
Curated examples now require specific stdout markers. The smoke test reports all missing markers.
MNDP failure exit behavior
examples/mndp/mndp.py, CHANGELOG.md
Subprocess failures include captured output, and missing MNDP announcements now produce a non-zero exit status.

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

Possibly related PRs

  • tikoci/quickchr#1: Both changes modify resilient network fetching and version resolution behavior.
  • tikoci/quickchr#2: Both changes extend fetchResilient fallback error handling.
  • tikoci/quickchr#4: Both changes modify RouterOS version resolution and its public options or behavior.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% 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 clearly summarizes the retry and example false-green fixes addressed by the pull request.
Linked Issues check ✅ Passed The changes satisfy #121 retry and error-chaining requirements and #102 output-marker, PowerShell, and MNDP failure requirements.
Out of Scope Changes check ✅ Passed The documentation, changelog, diagnostics, implementation, and tests directly support the linked issue objectives.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/121-102-version-retry-and-example-false-greens

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.

Copilot AI 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.

Pull request overview

This PR improves robustness of RouterOS version resolution (network/transient failures) and tightens the examples smoke harness so “exit 0 + any output” can’t silently pass broken examples.

Changes:

  • Add connection-failure retry behavior to resolveVersion() and propagate retry options through resolveAllVersions().
  • Improve fetchResilient() error reporting by chaining direct + IPv4-fallback failures into a structured ResilientFetchError.
  • Make examples smoke tests assert per-example output markers; enforce PowerShell guard rails ($ErrorActionPreference = 'Stop' before dot-sourcing) and fix a Python example’s “return → exit 0” failure path.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/lib/versions.ts Adds retry policy for version resolution and plumbs options through resolveAllVersions().
src/lib/net.ts Introduces ResilientFetchError and preserves both transport failures when IPv4 fallback fails.
test/unit/versions.test.ts Adds unit coverage for retry/no-retry behaviors in version resolution.
test/unit/net.test.ts Adds unit coverage for ResilientFetchError and updated isConnectionFailure() behavior.
test/integration/examples-smoke.test.ts Replaces “non-empty output” with required stdout markers per runnable example.
scripts/validate-examples.ts Enforces PowerShell $ErrorActionPreference guard before dot-sourcing common.ps1.
examples/common.ps1 Documents the load-bearing duplication of $ErrorActionPreference guarding.
examples/quickstart/quickstart.ps1 Arms $ErrorActionPreference = 'Stop' before dot-sourcing shared helpers.
examples/rollback/rollback.ps1 Arms $ErrorActionPreference = 'Stop' before dot-sourcing shared helpers.
examples/device-mode/device-mode.ps1 Arms $ErrorActionPreference = 'Stop' before dot-sourcing shared helpers.
examples/dude/dude.ps1 Arms $ErrorActionPreference = 'Stop' before dot-sourcing shared helpers.
examples/grounding/grounding.ps1 Arms $ErrorActionPreference = 'Stop' before dot-sourcing shared helpers.
examples/harness/harness.ps1 Arms $ErrorActionPreference = 'Stop' before dot-sourcing shared helpers.
examples/service-forward/service-forward.ps1 Arms $ErrorActionPreference = 'Stop' before dot-sourcing shared helpers.
examples/trial-license/trial-license.ps1 Arms $ErrorActionPreference = 'Stop' before dot-sourcing shared helpers (manual example).
examples/version-matrix/version-matrix.ps1 Arms $ErrorActionPreference = 'Stop' before dot-sourcing shared helpers.
examples/mndp/mndp.py Fixes failure path to exit non-zero via SystemExit instead of bare return.
.github/instructions/examples.instructions.md Documents “Exit 0 is not evidence” and the enforced PowerShell guard.
CHANGELOG.md Records the user-facing reliability fixes (version retry/error reporting, examples false-green closure).

Comment thread src/lib/versions.ts Outdated
…135)

`run_quickchr()` used subprocess.run(check=True), whose CalledProcessError
names the command and the exit code and DISCARDS both streams -- and those
streams are the only place quickchr says what went wrong. Run 30852139131
(linux/aarch64, currently red on main) failed on the first post-boot exec and
recorded nothing but "returned non-zero exit status 1", which is not enough to
diagnose it. Same family as the rest of this branch: a failure that cannot say
what it was.

Instrumentation, not a fix for #135 -- that stays open until the aarch64
failure is understood.

Verified locally by pointing the exec at a machine that does not exist: rc=1,
and the CLI's own "Machine ... not found. Available: ..." now reaches stderr.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mobileskyfi

Copy link
Copy Markdown
Contributor Author

CI status: 8/9 green; the one red check is not this PR

Check Result
Repo Checks, Unit Tests & Coverage, Unit Tests (windows-latest), Lint PowerShell examples, CodeQL ×3, CodeRabbit pass
Integration freshness failmain integration is RED (failure) at fc92d4b

That gate reads main's last integration run, not this branch. main went red at run 30852139131 (pushed ~30 min before this branch existed) on Examples smoke (linux/aarch64 · stable) — the first post-boot exec returned 1. Filed as #135; every other leg in that run passed.

One follow-on commit here

The aarch64 failure recorded nothing but the exit code: run_quickchr() used subprocess.run(check=True), and CalledProcessError names the command and discards both streams — the only place quickchr says what went wrong. The smoke artifact holds no more.

That is the same defect class as the rest of this branch, in the file this branch already fixes, so 4d4bd76 makes run_quickchr() print the child's stdout/stderr on failure. Verified locally by pointing the exec at a nonexistent machine: rc=1, and Machine "…" not found. Available: … now reaches stderr.

It is instrumentation, not a fix#135 stays open until the aarch64 failure is understood. Merging this PR still needs main green first; the freshness gate is doing its job.

@mobileskyfi

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 5

🤖 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 `@examples/common.ps1`:
- Around line 9-13: Update the explanatory comment in common.ps1 around the
caller’s $ErrorActionPreference to state that the caller must set it to 'Stop'
before dot-sourcing common.ps1; describe the false-green behavior when that
preference is missing, while preserving the existing load-order and helper-call
context.

In `@examples/mndp/mndp.py`:
- Around line 45-56: Update the subprocess invocation in the mndp wrapper to
pass check=False explicitly, then forward proc.stdout and proc.stderr for
successful calls before returning. Preserve the existing nonzero-return handling
and diagnostic SystemExit message, and ensure output is not duplicated on
failures.

In `@examples/version-matrix/version-matrix.ps1`:
- Around line 5-6: Configure each Start-Job script block in the version-matrix
flow to set $PSNativeCommandUseErrorActionPreference = $true and
$ErrorActionPreference = 'Stop' before invoking quickchr, or explicitly validate
$LASTEXITCODE immediately afterward and fail the job on nonzero results.

In `@scripts/validate-examples.ts`:
- Line 14: Update scripts/validate-examples.ts to replace every readFileSync
usage, including the require("node:fs").readFileSync call, with
Bun.file(...).text() and await the resulting promises where needed; then remove
readFileSync from the node:fs import while preserving the existing validation
flow.

In `@src/lib/versions.ts`:
- Around line 103-134: Update the retry loop around fetchResilient so response
reading and version parsing occur within the try/catch that handles retryable
connection failures. Preserve HTTP status failures and invalid-version results
as terminal QuickCHRError outcomes, while allowing retry-eligible body-read
failures to update lastError and retry or surface after maxAttempts.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8bf30c1f-b0ee-4fba-b9bf-1bfc52c77b73

📥 Commits

Reviewing files that changed from the base of the PR and between fc92d4b and 4d4bd76.

📒 Files selected for processing (19)
  • .github/instructions/examples.instructions.md
  • CHANGELOG.md
  • examples/common.ps1
  • examples/device-mode/device-mode.ps1
  • examples/dude/dude.ps1
  • examples/grounding/grounding.ps1
  • examples/harness/harness.ps1
  • examples/mndp/mndp.py
  • examples/quickstart/quickstart.ps1
  • examples/rollback/rollback.ps1
  • examples/service-forward/service-forward.ps1
  • examples/trial-license/trial-license.ps1
  • examples/version-matrix/version-matrix.ps1
  • scripts/validate-examples.ts
  • src/lib/net.ts
  • src/lib/versions.ts
  • test/integration/examples-smoke.test.ts
  • test/unit/net.test.ts
  • test/unit/versions.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

In Bun-based TypeScript code, use Bun.spawn(), Bun.write(), Bun.sleep(), bun:test, and ESM imports with .ts extensions.

Files:

  • test/unit/versions.test.ts
  • scripts/validate-examples.ts
  • test/integration/examples-smoke.test.ts
  • src/lib/versions.ts
  • src/lib/net.ts
  • test/unit/net.test.ts
test/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not turn a red integration test green by broadening timeouts, skipping it, or platform-gating it before reproducing and root-causing the failure.

Files:

  • test/unit/versions.test.ts
  • test/integration/examples-smoke.test.ts
  • test/unit/net.test.ts
**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.ts: Use Bun APIs and tooling rather than Node.js equivalents: Bun.spawn(), Bun.write(), Bun.sleep(), bun test, and bun:test. Use ESM with .ts extensions in imports; do not use CommonJS.
For ARM64 virt machines, never use if=virtio for drives; use an explicit -device virtio-blk-pci,drive=drive0 configuration.
When using HVF acceleration, use -cpu host, not cortex-a710.
For arm64 guests on macOS, automatically select TCG with -cpu cortex-a710; HVF cannot run the CHR image's 32-bit ARM userspace on Apple Silicon. --accel and QUICKCHR_ACCEL must override this selection for testing.
UEFI pflash code and vars units must be identical in size.
QGA is x86-only; do not assume the guest agent starts for arm64 CHR.
Use tabs for indentation.
Do not add unnecessary comments to obvious code.
Errors must be thrown as QuickCHRError(code, message, installHint?).
Preserve the documented public API types and behavior: QuickCHR.start(opts) returns ChrInstance; ChrInstance provides stop(), remove(), rest(), monitor(), serial(), and qga(); and MachineState represents persisted machine.json state.

Files:

  • test/unit/versions.test.ts
  • scripts/validate-examples.ts
  • test/integration/examples-smoke.test.ts
  • src/lib/versions.ts
  • src/lib/net.ts
  • test/unit/net.test.ts
test/unit/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Unit tests must be fast and must not require QEMU.

Files:

  • test/unit/versions.test.ts
  • test/unit/net.test.ts
test/integration/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Integration tests may require QEMU and must be guarded by QUICKCHR_INTEGRATION=1.

Files:

  • test/integration/examples-smoke.test.ts
src/**

📄 CodeRabbit inference engine (CLAUDE.md)

Follow the rules in general.instructions.md for files under src/**, including layer boundaries, error-code usage, port layout, and the RouterOS “expired admin” caveat.

Files:

  • src/lib/versions.ts
  • src/lib/net.ts
src/lib/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Keep src/lib/ as pure library code: do not import from src/cli/ and do not call process.exit() there.

Keep src/lib/ as pure library modules: do not add CLI dependencies or call process.exit().

Files:

  • src/lib/versions.ts
  • src/lib/net.ts
🧠 Learnings (2)
📚 Learning: 2026-07-31T11:56:40.455Z
Learnt from: mobileskyfi
Repo: tikoci/quickchr PR: 117
File: scripts/ci-cache-key.ts:68-68
Timestamp: 2026-07-31T11:56:40.455Z
Learning: In Bun TypeScript files, do not flag use of `node:fs.appendFileSync` when append semantics are required and `Bun.write()` cannot provide them. This applies to cases such as appending multiple entries to `$GITHUB_OUTPUT` or writing to the boot log; use append-capable file operations rather than overwriting existing content.

Applied to files:

  • test/unit/versions.test.ts
  • scripts/validate-examples.ts
  • test/integration/examples-smoke.test.ts
  • src/lib/versions.ts
  • src/lib/net.ts
  • test/unit/net.test.ts
📚 Learning: 2026-07-29T22:35:22.878Z
Learnt from: mobileskyfi
Repo: tikoci/quickchr PR: 108
File: test/lab/www-abort-damage/www-abort-damage.test.ts:0-0
Timestamp: 2026-07-29T22:35:22.878Z
Learning: In this Bun TypeScript repo, QUICKCHR_INTEGRATION is a presence/opt-in flag for integration/lab tests. Follow the existing convention that gates execution using the presence check form `!process.env.QUICKCHR_INTEGRATION` (e.g., in describe/test skip conditions or equivalent gating). Any defined value (including `QUICKCHR_INTEGRATION=0`) should be treated as “present” and thus enables the tests per the repo’s convention. Do not add explicit value parsing/comparisons inside individual test files (e.g., `=== "0"`, `=== "1"`, or number/boolean conversions); only change this behavior via a repo-wide convention update.

Applied to files:

  • test/integration/examples-smoke.test.ts
🪛 LanguageTool
CHANGELOG.md

[style] ~117-~117: Consider an alternative for the overused word “exactly”.
Context: ...ad address" indistinguishable, which is exactly the question the log has to answer. T...

(EXACTLY_PRECISELY)

🪛 Ruff (0.16.0)
examples/mndp/mndp.py

[error] 45-45: subprocess call: check for execution of untrusted input

(S603)


[warning] 45-45: subprocess.run without explicit check argument

Add explicit check=False

(PLW1510)


[warning] 52-56: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 252-252: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (18)
src/lib/net.ts (2)

22-24: LGTM!

Also applies to: 37-56, 184-203


57-117: 🗄️ Data Integrity & Integration

No action needed.

test/unit/net.test.ts (1)

3-8: LGTM!

Also applies to: 43-54, 136-167

src/lib/versions.ts (1)

7-7: LGTM!

Also applies to: 68-83, 146-154

test/unit/versions.test.ts (1)

264-335: LGTM!

CHANGELOG.md (1)

102-122: LGTM!

examples/common.ps1 (1)

4-4: LGTM!

.github/instructions/examples.instructions.md (1)

17-17: LGTM!

Also applies to: 53-83

examples/device-mode/device-mode.ps1 (1)

2-2: LGTM!

examples/dude/dude.ps1 (1)

3-3: LGTM!

test/integration/examples-smoke.test.ts (1)

50-68: LGTM!

Also applies to: 77-113, 212-216

examples/mndp/mndp.py (1)

249-252: LGTM!

examples/grounding/grounding.ps1 (1)

3-4: LGTM!

examples/harness/harness.ps1 (1)

4-5: LGTM!

examples/quickstart/quickstart.ps1 (1)

3-4: LGTM!

examples/rollback/rollback.ps1 (1)

2-3: LGTM!

examples/service-forward/service-forward.ps1 (1)

2-3: LGTM!

examples/trial-license/trial-license.ps1 (1)

3-4: LGTM!

Comment thread examples/common.ps1 Outdated
Comment thread examples/mndp/mndp.py Outdated
Comment thread examples/version-matrix/version-matrix.ps1
Comment thread scripts/validate-examples.ts
Comment thread src/lib/versions.ts
mobileskyfi and others added 2 commits August 3, 2026 15:02
…tart-Job

Six review findings; four applied, two declined with reasons.

versions.ts, from CodeRabbit: `response.text()` sat OUTSIDE the retry-eligible
try, so a body that failed after the headers arrived (a reset mid-response)
escaped the loop unwrapped — no retry, no terminal QuickCHRError. The read now
happens inside the try; an HTTP status is thrown as a QuickCHRError so the catch
can tell a server verdict from a transport failure and let it straight out, and
parsing stays terminal after the loop. Anchored with a stream that errors after
a 200.

versions.ts, from Copilot: `Math.max(1, NaN)` is NaN, so a caller passing NaN
skipped the loop entirely and got "all NaN attempts: undefined" — a message
describing nothing. maxAttempts/retryDelayMs are public options and are now
sanitized to a whole count >= 1 and a finite non-negative delay. Anchored.

version-matrix.ps1, from CodeRabbit: Start-Job runs in a separate process and
inherits no preference variables, so neither the caller's nor common.ps1's copy
reaches the job block. Grounded with pwsh 7.4.6 rather than taken on faith:

  - silent `exit 3` inside a job, preferences unset -> parent continues, rc=0
  - same, with both set inside the block   -> NativeCommandExitException, rc=1
  - a job whose command writes to stderr propagates either way, but via the
    stderr-to-error-record mapping, not the exit code — not something to rely on

That is #102's exact class (a failed start booting nothing, reported green), so
the rule is recorded in examples.instructions.md alongside the others.

common.ps1: reworded so the false-green condition is stated as the conditional
it is — the helper calls are non-terminating only when the CALLER has not armed
the preference, which is why the caller's copy exists.

mndp.py: explicit check=False, naming why the wrapper handles returncode itself.

Declined:
  - forwarding child stdout/stderr on SUCCESSFUL run_quickchr calls — the
    example curates its own narrative output, and echoing every CLI invocation
    would bury it and the smoke markers in noise. The gap was diagnosing
    FAILURES, which is fixed.
  - converting validate-examples.ts to Bun.file().text() — the file is uniformly
    sync node:fs (readdirSync/statSync/existsSync), so a lone async read would
    read worse, not better. Worth doing as a whole-file cleanup, not here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Hit while byte-compiling mndp.py to check the review edits: any local
`python3 -m py_compile` or import of a .py example creates
examples/<name>/__pycache__, and the dir scan flagged it as an unexpected
subdir — turning `bun run check` red for a build artifact. It is gitignored, so
it never reaches CI and can never be a real violation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mobileskyfi
mobileskyfi merged commit 5136c22 into main Aug 3, 2026
10 checks passed
@mobileskyfi
mobileskyfi deleted the fix/121-102-version-retry-and-example-false-greens branch August 3, 2026 22:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants