Skip to content

test(ci): pin #767 Bun preflight behavior - #770

Merged
vansin merged 2 commits into
mainfrom
agent/test-766-bunx-preflight
Aug 12, 2026
Merged

test(ci): pin #767 Bun preflight behavior#770
vansin merged 2 commits into
mainfrom
agent/test-766-bunx-preflight

Conversation

@vansin

@vansin vansin commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Scope

Test/CI-only behavior gate for the anet hub start preflight selected by #767.

  • bun without bunx: precise fail-fast diagnosis and symlink remediation
  • neither executable: generic Bun installation guidance
  • valid bunx: exact package argv and healthy CLI banner
  • two named witnessed-red mutations
  • active L1 registration in scripts/qa.sh

This PR does not modify product code, publish packages, deploy anything, close #766, or decide whether bun-only installations should later auto-fallback to bun x.

Frozen coordinates

  • base: 3b049160b2db542d06a32988a530f0b21f773c96
  • source: 885a74d7c5418d0ee20de46c728396a367ec41cf
  • report-only head: 69015033143a91fef7ba5d8230cd08a87e27738e
  • focused image: sha256:60994c0f0a088167e7f5ed77f74fc5fefbde57be414bd9a23b1764f48dc746bc
  • focused log SHA256: 1779a95c74afdd17670d2311dc99f09605b671ff1ecfc8dac9a209c4b0de55f8
  • L1 log SHA256: fca1054ab0b7604a0d86e2a1f212e7d78b7214c2702c53e0e4d4a62d7b310a03

Verification

  • focused Docker: RESULT pass=4 fail=0
  • clean baseline matrix: all 3 cases pass
  • mutations: 2/2 witnessed red at their named behavior
  • production agent-network build: pass
  • active L1: 15/15 pass, ALL PASS in 107s (wall 107.24s)
  • image/source byte provenance: 6/6 test-suite files match

Full evidence: docs/tests/report-test766-bunx-preflight.txt.

Honest limits

The healthy path uses an HTTP fixture after the real CLI selects and invokes bunx. It proves preflight and argv wiring, not registry availability or the full commhub-server implementation; existing Hub E2E remains authoritative for those layers.

@vansin

vansin commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

独立窄审 · PR #770 (Draft)

Verdict: CLEAN — no BLOCKER, no MAJOR, no MINOR.

Reviewer: 通信IM马 (independent, read-only). Extracted PR tree via git archive origin/pr-770 → temp dir; author worktree untouched. No merge, no deploy.

Per brief: this audit does NOT touch #766's product choice; it verifies only that test766 correctly PINS whatever behavior is currently in agent-network/bin/cli.ts main.


Provenance

value
HEAD (report-only) 69015033143a91fef7ba5d8230cd08a87e27738e ✓ matches brief
source 885a74d7c5418d0ee20de46c728396a367ec41cf ✓ matches brief
base (merge-base w/ main) 3b049160b2db542d06a32988a530f0b21f773c96 ✓ matches brief
current main tip 3b049160...same as base, brand new
files (base..HEAD) 8 (+304 / −0)
source→HEAD delta ONLY docs/tests/report-test766-bunx-preflight.txt (+83) ✓ per brief

Item-by-item

① strictly test/CI-only, no product semantic changes — CONFIRMED

Every changed path (8/8):

docs/tests/report-test766-bunx-preflight.txt   (test evidence)
scripts/qa.sh                                  (test wire)
tests/test766-bunx-preflight/Dockerfile        (test harness)
tests/test766-bunx-preflight/README.md         (test doc)
tests/test766-bunx-preflight/fake-bun-only/bun (fixture)
tests/test766-bunx-preflight/fake-bunx/bunx    (fixture)
tests/test766-bunx-preflight/fixture-server.ts (fixture)
tests/test766-bunx-preflight/run.sh            (test runner)

No files in agent-network/src/, agent-node/src/, server/src/, or any production/deploy path. Grepped: 0 changes to product code.

Note: run.sh DOES sed -i agent-network/bin/cli.ts in-container during mutation testing, but only inside the ephemeral Docker filesystem, restored via cp /tmp/test766-cli.orig after each mutation. Not persisted anywhere.

② tri-state matrix runs REAL production CLI preflight + bunx fixture preserves exact argv — CONFIRMED

All three probes invoke the actual production entry:

/usr/local/bin/bun "$ROOT/agent-network/bin/cli.ts" hub start ...

Not a mock CLI. Not a helper. The real Bun runtime executing the real cli.ts source.

Case 1 — probe_bun_only: PATH=fake-bun-only:/usr/bin:/bin (fake bun stub, no bunx)

  • Assertions: rc != 0 AND log contains both 找到了 bun,但没有 bunx AND ln -s "$(command -v bun)" AND log does NOT contain Starting CommHub Server
  • Verified against origin/main:agent-network/bin/cli.ts — the bun-only branch prints exactly these strings (I read the source, matches).

Case 2 — probe_neither: PATH=/usr/bin:/bin (no bun, no bunx)

  • Assertions: rc != 0 AND log contains anet hub start requires the Bun runtime AND NOT contains bun-only diagnosis AND NOT contains healthy banner
  • The bootstrap uses /usr/local/bin/bun (bypassing PATH restriction), but commandExists() inside the CLI checks PATH — so commandExists("bunx") false AND commandExists("bun") false → fall through to generic-Bun branch.

Case 3 — probe_bunx: PATH=fake-bunx:/usr/bin:/bin where fake-bunx is:

#!/bin/sh
set -eu
capture=${TEST766_CAPTURE:?TEST766_CAPTURE is required}
printf '%s\n' "$@" > "$capture"
exec /usr/local/bin/bun /repo/tests/test766-bunx-preflight/fixture-server.ts "$@"
  • Exact argv preservation: printf '%s\n' "$@" > $capture writes each arg on its own line, no shell splitting, no globbing. exec ... "$@" forwards verbatim.
  • run.sh assertions on captured argv:
    • sed -n '1p' "$capture" == "--bun" — argv[0] literally --bun
    • grep -Fxq '@sleep2agi/commhub-server@0.9.0-preview.29' "$capture" — the exact package spec argv is present (fixed-string, anchored)
  • Plus assertion grep -Fq 'Server running on http://127.0.0.1:27668' — the fixture server actually replied (proves the exec succeeded end-to-end, not just argv was captured).

The bunx-existence branch is verified BOTH at argv wiring (captured) AND at run-through completion (server bind). This is real preflight + real spawn + real HTTP fixture, not a static mock.

③ 2 mutations each red on named behavior, not baseline/suicide/no-op/unrelated — CONFIRMED

Mutation 1 — old-or-allows-bun-only:

# pre-guard: exactly one target string (defends against no-op OR ambiguous multi-match)
[[ "$(grep -Fc 'if (!commandExists("bunx")) {' agent-network/bin/cli.ts)" -eq 1 ]]
# sed
sed -i 's/if (!commandExists("bunx")) {/if (!commandExists("bunx") \&\& !commandExists("bun")) {/' agent-network/bin/cli.ts
# post-guard: new form present (defends against sed silent match miss)
grep -Fq 'if (!commandExists("bunx") && !commandExists("bun")) {' agent-network/bin/cli.ts
# expect_red: probe_bun_only must FAIL (bun-only would now be allowed → healthy banner appears, but probe requires reject-msg)
expect_red old-or-allows-bun-only probe_bun_only

Semantically restores the pre-fix permissive OR-form that was the whole reason bun-only was silently allowed. Mutation on real production code. Survival = mutation escaped, so expect_red fails loud.

Mutation 2 — valid-bunx-blocked:

[[ "$(grep -Fc 'if (!commandExists("bunx")) {' agent-network/bin/cli.ts)" -eq 1 ]]
sed -i 's/if (!commandExists("bunx")) {/if (true || !commandExists("bunx")) {/' agent-network/bin/cli.ts
grep -Fq 'if (true || !commandExists("bunx")) {' agent-network/bin/cli.ts
expect_red valid-bunx-blocked probe_bunx

Forces guard to always-reject. Under this mutation, even valid bunx gets rejected before spawn. probe_bunx requires the fixture server to be RUNNING (bind proof) — survived mutation means CLI never got to spawn → probe fails.

Guard analysis (defends against the classic mutation-test failure modes):

  • Baseline self-red: probe_all runs FIRST + ok "bun-only, neither, and bunx behavior matrix" — if baseline red, this stops before mutations. Safe.
  • Test-suicide: probes assert SPECIFIC log strings (找到了 bun,但没有 bunx, anet hub start requires the Bun runtime, Server running on http://127.0.0.1:27668), not just rc != 0. A mutation causing failure for the wrong reason (e.g. syntax error importing cli.ts) would not emit those exact strings → probe would report specific missing-line, not "generic fail". Defense present.
  • sed no-op: grep -Fc == 1 pre + grep -Fq new-string post — mutation aborts loud with [[ ]] bash failure OR the assertion catches it.
  • Unrelated failure: Each mutation is targeted at ONE line in a specific IF branch of production code. Not global. Failure attributed to the mutation is real.

Both mutations use the SAME single-line target if (!commandExists("bunx")) {. I verified against origin/main:agent-network/bin/cli.ts:

$ git show origin/main:agent-network/bin/cli.ts | grep -c 'if (!commandExists("bunx")) {'
1

Exactly one occurrence — pre-guard will succeed.

④ qa.sh adds test766 to active L1 + 15/15 evidence + pinned Bun digest — CONFIRMED

scripts/qa.sh diff:

 L1_TESTS=(
   ...
   "test686-rest-shape-golden"
+  "test766-bunx-preflight"
 )
 ...
     if [[ "$t" == "test686-rest-shape-golden" ]]; then
       build_args="--build-arg TEST686_SOURCE_COMMIT=$(git rev-parse HEAD)"
+    elif [[ "$t" == "test766-bunx-preflight" ]]; then
+      build_args="--build-arg TEST766_SOURCE_COMMIT=$(git rev-parse HEAD)"
     fi

Pinned Bun digest: Dockerfile uses

FROM oven/bun:1.3.14@sha256:e10577f0db68676a7024391c6e5cb4b879ebd17188ab750cf10024a6d700e5c4

Same digest as PR #762 (final source 79e4acea) — I independently verified this digest against Docker Hub's public API in that prior audit:

  • Multi-arch OCI index: e10577f0db68676a7024391c6e5cb4b879ebd17188ab750cf10024a6d700e5c4
  • linux/amd64 manifest: 50317d83cd5a5ae1d8b35b3379c69f57ce1a0dbf4def91f0965653d767851834
    Consistent L1 canonical Bun.

Report evidence:

  • 15/15 active L1 suites passed (14 existing + this new test766 = 15) ✓
  • ALL PASS in 107s
  • Focused image sha256:60994c0f0a08... recorded with TEST766_SOURCE_COMMIT=885a74d7...
  • L1 runner log SHA fca1054ab0b7... + focused runner log SHA 1779a95c74af...
  • Provenance byte check on 6 test files: PROVENANCE total=6 fail=0

⑤ source/report provenance + secret + current-main merge — CONFIRMED

  • source→HEAD: only report (+83); no source or test file touched by report commit ✓
  • Virtual merge: base == current main tip (both 3b049160) → 342 lines merge output, 0 true conflict markers, trivially clean
  • File drift: 0 commits on main touching any PR-changed path since base (trivially — base == main)
  • Secret sweep across 8 changed files (ntok_/utok_/atok_/BEGIN PRIVATE KEY): 0 hits
  • Fixture-server produces intentional-fixture strings (utok_test766_fixture in /api/auth/register response) — clearly-fabricated test placeholder, not a real token pattern, non-secret

#766 product choice NOT mixed into this narrow audit — CONFIRMED

Report explicit: "It pins the behavior selected by #767; it does not decide whether #766 should later adopt the separately tested automatic bun x fallback design."

Test766 verifies BEHAVIOR-AS-IS in origin/main:agent-network/bin/cli.ts:

  • bun-only → reject with specific msg
  • neither → reject with generic msg
  • bunx → allow

That IS the current cli.ts logic (I independently verified the bun-only branch's expected strings match line-by-line in main). Test766 is a REGRESSION gate for the current choice; not an ADVOCACY of any future choice.


Additional observations (informational, not blocking)

  • Test766 follows the same test686 pattern for CI (conditional per-suite --build-arg SOURCE_COMMIT), pinned same Bun digest as fix(ci): pin Bun across the L1 contract suite #762 — architecturally consistent with the established L1 pattern.
  • run.sh:127 printf 'source_commit=%s\n' "${TEST766_SOURCE_COMMIT:-unknown}" — echoes value but does NOT enforce != unknown fail-closed (same style as test686/test621). Not brief-material; a soft observation if consistency with test725's fail-closed style were desired later.
  • probe_bunx includes both a positive assertion (fixture server bound) AND a specific-argv assertion (--bun + @sleep2agi/commhub-server@0.9.0-preview.29). If bunx were called with different argv (accidentally reformatted), the exact-argv grep would red — protects against "bunx is invoked but with wrong args" regression.
  • expect_red helper: if "$@" >... 2>&1; then bad "survived"; else ok "witnessed red at named behavior"; fi — clean single-shot invocation, output captured to $ART/name.log for diagnostics. Good pattern.

Reviewer discipline (self)

Applied feedback_finding_confirmation_is_not_verdict: every focus item was mechanically mapped to brief wording. Nothing lands on BLOCKER/MAJOR/MINOR gate wording. Verdict: CLEAN.

Independent verifications:

  1. grep -c 'if (!commandExists("bunx")) {' origin/main:agent-network/bin/cli.ts = 1 → mutation pre-guard will succeed
  2. Read cli.ts source around bun-only branch — confirms probe_bun_only's assertion strings (找到了 bun,但没有 bunx, ln -s "$(command -v bun)") match source line-for-line
  3. Same-digest Bun image as PR fix(ci): pin Bun across the L1 contract suite #762 (already upstream-verified in that audit)
  4. base == current main tip → trivially clean merge
  5. All 8 changed paths in test/CI/docs scope; product code untouched at diff level

No approve, no merge, no deploy.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6901503314

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +7 to +8
COPY agent-network/package.json /repo/agent-network/package.json
RUN cd /repo/agent-network && bun install --ignore-scripts

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Copy the lockfile before running bun install

Because only package.json is present when bun install runs, the image resolves every ranged dependency from the registry instead of using the committed agent-network/package-lock.json; copying the repository afterward is too late. A new compatible dependency release can therefore make this active L1 suite pass or fail differently for the same source commit, undermining the recorded reproducibility guarantees. Copy the lockfile before installation and use a frozen-lockfile install.

AGENTS.md reference: AGENTS.md:L25-L25

Useful? React with 👍 / 👎.

Comment on lines +86 to +87
[[ "$(sed -n '1p' "$capture" 2>/dev/null || true)" == "--bun" ]] || rc=1
grep -Fxq '@sleep2agi/commhub-server@0.9.0-preview.29' "$capture" || rc=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.

P2 Badge Assert the complete bunx argument vector

The healthy-path probe only requires --bun to be first and the package string to occur somewhere in the capture. If the CLI appends an unintended argument, including a flag that changes server behavior, the fixture ignores it and this suite still passes despite claiming to preserve the exact argv. Compare the entire capture, including its line count and order, against the expected two arguments.

Useful? React with 👍 / 👎.

COPY agent-network/package.json /repo/agent-network/package.json
RUN cd /repo/agent-network && bun install --ignore-scripts

COPY agent-network /repo/agent-network

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Exclude host node_modules from the Docker context

When a checkout contains the ignored agent-network/node_modules directory, as it normally does after local setup, this COPY merges those host files over the dependencies installed in the preceding image layer because Docker does not honor .gitignore and this repository has no .dockerignore. The supposedly isolated suite can therefore use stale or host-specific dependency artifacts and produce results that differ between otherwise identical Docker runs; exclude node_modules from the build context or copy only tracked source paths.

AGENTS.md reference: AGENTS.md:L7-L7

Useful? React with 👍 / 👎.

set -e
if [[ "$rc" -eq 0 ]] \
|| ! grep -Fq '找到了 bun,但没有 bunx' "$log" \
|| ! grep -Fq 'ln -s "$(command -v bun)"' "$log" \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Check the complete bunx symlink remediation

The bun-only probe matches only the source half of the suggested ln -s command. If the destination is removed or changed to an invalid path, the suite still passes even though the user-facing remediation it explicitly intends to preserve no longer creates bunx. Assert the complete command, including "$(dirname "$(command -v bun)")/bunx", rather than this prefix.

Useful? React with 👍 / 👎.

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.

[hub] bun-only 机器通过 hub start 前置检查后 spawn bunx 失败,且报错归因错误(守卫 OR / 需求 bunx-only)

2 participants