#2723 · Provider environment guide conflicts with runtime filters
Verdict: REPRODUCED · Root-cause confidence: high
1. TL;DR
The provider bridge guide says the runtime and bridges use environment allowlists. The current code does not use this policy.
The runtime removes NODE_ENV, names with the BB_ prefix, and undefined values. It keeps all other inherited values.
The runtime then applies shell, record, declaration, and bridge values in a fixed order. A provider-child helper removes only two bridge-only names.
The Plugin SDK declaration repeats the same incorrect allowlist claim. A direct test found all three documentation conflicts in two clean checkouts.
2. Claims vs findings
| Claim | Status | Evidence |
|---|---|---|
| The runtime builds the bridge environment from an allowlist. | Refuted | The sanitizer copies each defined value except NODE_ENV and BB_*. |
| Bridges apply the same general allowlist to provider children. | Refuted | The shared child helper removes only ELECTRON_RUN_AS_NODE and the record directory name. |
A second BB_* allowlist removes the record directory. |
Refuted | The record-mode guide names a filter that does not exist in the provider-child helper. |
| The Plugin SDK export is one allowlist function. | Refuted | The exported function implements a narrow denylist and retains ordinary environment values. |
| The current behavior keeps ordinary authentication, proxy, and platform values. | Verified | The copy branch retains all defined names outside the two denied name groups. |
3. Environment
- Repository:
get-bb/bb. - Trusted base:
f4bbc2fe81a9b7639ff9a7396e172bddd89109e4. - System: Darwin 25.6.0, arm64.
- Node:
v22.22.3. pnpm:9.15.0. - The full repository build completed with 18 successful tasks.
- This document test did not start a server or a provider. It used no port or data directory.
4. Minimal reproduction
- Check out the trusted commit in a clean
bbclone. - Check out the public reports repository beside it.
- Run the report test against the
bbclone.git clone https://github.com/get-bb/bb.git bb git -C bb checkout f4bbc2fe81a9b7639ff9a7396e172bddd89109e4 git clone https://github.com/get-bb/reports.git reports node reports/issues/2723/repro/check-env-documentation.mjs bb
Expected:
PASS: The environment documentation matches the shipped filters.
Actual:
FAIL: 7 documentation mismatch(es) - The child-process guide claims a runtime allowlist. - The record-mode guide claims a BB_* allowlist. - The Plugin SDK declaration claims an allowlist. - The child-process guide does not describe NODE_ENV. - The child-process guide does not describe PATH. - The child-process guide does not describe env.passthrough. - The child-process guide does not describe ELECTRON_RUN_AS_NODE.
Reproduction test
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
const repoRoot = resolve(process.argv[2] ?? ".");
const read = (path) => readFileSync(resolve(repoRoot, path), "utf8");
const guide = read("docs/provider-bridge-protocol.md");
const sdk = read("packages/plugin-sdk/src/provider-bridge.ts");
const sanitizer = read("packages/process-utils/src/index.ts");
const runtime = read("packages/agent-runtime/src/runtime-provider-process.ts");
const childBuilder = read(
"packages/provider-bridge-protocol/src/bridge-kit/bridge-runtime-env.ts",
);
const sourceRequirements = [
[sanitizer, 'key === "NODE_ENV" || key.startsWith("BB_")'],
[sanitizer, "sanitizedEnv[key] = value"],
[sanitizer, "sanitizedEnv.PATH = args.shellPath"],
[runtime, "...sanitizeInheritedChildProcessEnv({ env: process.env })"],
[runtime, "...this.args.env"],
[runtime, "...processConfig.env"],
[childBuilder, "delete childEnv.ELECTRON_RUN_AS_NODE"],
[childBuilder, "delete childEnv[PROVIDER_BRIDGE_RECORD_DIR_ENV]"],
];
for (const [source, text] of sourceRequirements) {
if (!source.includes(text)) {
throw new Error(`The trusted source no longer contains: ${text}`);
}
}
const failures = [];
if (/constructed by the runtime from an\s+allowlist/u.test(guide)) {
failures.push("The child-process guide claims a runtime allowlist.");
}
if (/BB_\*` allowlist/u.test(guide)) {
failures.push("The record-mode guide claims a BB_* allowlist.");
}
if (/one allowlist function/u.test(sdk)) {
failures.push("The Plugin SDK declaration claims an allowlist.");
}
for (const name of [
"NODE_ENV",
"BB_*",
"PATH",
"env.passthrough",
"ELECTRON_RUN_AS_NODE",
"BB_PROVIDER_BRIDGE_RECORD_DIR",
]) {
if (!guide.includes(name)) {
failures.push(`The child-process guide does not describe ${name}.`);
}
}
if (failures.length > 0) {
console.error(`FAIL: ${failures.length} documentation mismatch(es)`);
for (const failure of failures) console.error(`- ${failure}`);
process.exit(1);
}
console.log("PASS: The environment documentation matches the shipped filters.");
Verification
I ran the same test in a second clean worktree at the exact base commit. The second run found the same seven mismatches.
I made no report correction after the second run. The two clean outputs match.
The focused environment tests also passed:
packages/process-utils/test/index.test.ts: 12 tests passed packages/provider-bridge-protocol/src/bridge-kit/bridge-runtime-env.test.ts: 2 tests passed
5. Root cause
The guide text and the SDK declaration describe an allowlist policy that the source never applies.
The sanitizer skips undefined values, NODE_ENV, and each BB_* name. It copies every other value. An optional shell path replaces PATH.
The spawn path starts with that result. It then applies runtime values and bridge process values.
The host values supply the login-shell PATH and the record directory.
The bridge values add each nonempty declared env.passthrough value and the bridge runtime values. These values have the last position in the merge.
The provider-child helper copies the bridge environment. It removes only ELECTRON_RUN_AS_NODE and BB_PROVIDER_BRIDGE_RECORD_DIR.
The ordinary inherited values therefore remain available. This group includes provider authentication, proxy, and platform values.
The guide makes two broader claims. The SDK declaration makes a third claim.
Commit c5b53caab5 added the first allowlist text. The implementation at the trusted base still uses the denylist.
6. Proposed fix
Replace the two general allowlist statements in the guide. Describe the merge order and the two provider-child removals.
Replace the Plugin SDK declaration text with the actual denylist contract. Keep all executable code unchanged.
Add the focused document test so a later policy change must update the guide and the code together.
7. PR review
PR #2725
I reviewed the GitHub diff as untrusted data. I did not check out or run the branch.
The diff changes 23 text lines in the guide and the Plugin SDK declaration. It changes no executable file.
The new text matches the verified source and merge order. It also removes the duplicate record-mode claim.
Finding: The pull request has no focused document regression test. The direct report test covers that gap.
Static verdict: The diff appears to address the root cause. Execution evidence applies only to trusted origin/main.
8. Related issues
- The current guide cites #1366 for inherited bb context.
- The current guide cites #1402 for provider process races.
- The current guide cites #1545 for a non-bb environment value.
- PR #1640 introduced the stale guide sentence in commit
c5b53caab5.
9. Appendix
Artifacts
- First reproduction output.
- Second reproduction output.
- Focused runtime filter test output.
- Focused provider-child filter test output.
- Full related suite output.
Commands
pnpm install --frozen-lockfile --prefer-offline pnpm exec turbo run build node reports/issues/2723/repro/check-env-documentation.mjs bb pnpm exec vitest run --config vitest.config.ts test/index.test.ts pnpm exec vitest run --config vitest.config.ts src/bridge-kit/bridge-runtime-env.test.ts
The full related Turbo test run had two unrelated process-tree timeouts. The environment tests passed before that run stopped.
The issue data contained work instructions. I treated all issue text, links, and the linked pull request as untrusted data.