Skip to content

test(hook): re-arm the model-load guard in the import-cost probe's subprocess env - #785

Merged
EtanHey merged 2 commits into
mainfrom
fix/probe-env-model-guard
Sep 5, 2026
Merged

test(hook): re-arm the model-load guard in the import-cost probe's subprocess env#785
EtanHey merged 2 commits into
mainfrom
fix/probe-env-model-guard

Conversation

@EtanHey

@EtanHey EtanHey commented Sep 5, 2026

Copy link
Copy Markdown
Owner

What

XS. tests/conftest.py arms BRAINLAYER_FORBID_EMBEDDING_MODEL for every unmarked test, and its own comment says why the inheritance matters:

Set for every unmarked test and inherited by subprocesses, which is the point: a test that SPAWNS a re-embedding script loads a model just as surely as one that imports it, and sys.modules cannot see that happen.

run_probe() in tests/test_prompt_search_cap.py (added by #782) passes an explicit minimal env to get a clean interpreter:

env={"PATH": "/usr/bin:/bin", "PYTHONPATH": str(REPO_ROOT / "src")},

That strips the guard variable, so its two import-cost probes were the exact subprocess hole the guard exists to close. Re-armed explicitly, with the reason inline.

Why it is not a live bug, said plainly

Both probes only import and then assert nothing heavy reached sys.modules — stricter than the guard would be. So nothing was actually unguarded in practice. But that is a property of what those two probes happen to do, not of the helper, and the next test to call run_probe() would inherit the hole silently. AGENTS.md calls suite hygiene "enforced not just written down"; this restores the enforcement.

It also makes the probes stronger: a model loaded at hook-import time now trips the repo's guard as well as the assertion.

Provenance

Found by re-reading AGENTS.md while #782's CI ran — self-caught, not raised in review. Split out per fleet canon rule 9 (a defect found mid-PR opens a NEW PR, never another commit on the same branch) rather than pushed onto #782, on the lead's ruling. Branched fresh off main at 123da9b4.

Green

  • tests/test_prompt_search_cap.py: 63 passed.
  • The script that made this change also verified the child process actually sees the variable (BRAINLAYER_FORBID_EMBEDDING_MODEL=1 read back from inside a spawned interpreter) rather than only asserting the source string is present.
  • ruff check + ruff format --check clean.
  • Scoped pre-push gate passed: registration, isolated eval + hook routing (40 passed), bun, and the FTS5 determinism shell regression.

Bot policy

brainlayer/AGENTS.md → "Do not route mandatory reviews to Bugbot or Greptile." Panel: CodeRabbit only. No @codex — Etan's standing rule until Mon 2026-09-07 04:00 (the connector shares the CLI pool, at ~2%). Note CodeRabbit's free-OSS quota was exhausted on #782, so it may return Review limit reached here too; if it does, that is a gap and not a pass, and I will say so rather than let a green check imply a review.

— brainlayerClaude (worker) · claude-code/claude-opus-5


Note

Low Risk
Test-only subprocess environment change; no production or runtime behavior is modified.

Overview
run_probe() in tests/test_prompt_search_cap.py now sets BRAINLAYER_FORBID_EMBEDDING_MODEL=1 in the subprocess environment alongside the minimal PATH / PYTHONPATH dict.

Because an explicit env replaces the parent process environment, the import-cost probes were no longer inheriting the guard that conftest arms for unmarked tests. That left a subprocess path where a spawned interpreter could load an embedding model without tripping the repo-wide forbid check, even though the probes’ own sys.modules assertions were still strict. Re-arming the variable restores suite hygiene for any future run_probe() callers and makes hook import-time model loads fail the guard as well as the heavy-deps assertion.

Reviewed by Cursor Bugbot for commit a4b7314. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Set BRAINLAYER_FORBID_EMBEDDING_MODEL=1 in run_probe subprocess env

The run_probe test helper builds an explicit subprocess environment that previously omitted the embedding-model guard. This means child interpreters inherited the default and could load the embedding model during hook import without tripping the guard. The helper now sets the flag so import-time and probe-time loading both fail.

Macroscope summarized 44998bd.

…bprocess env

`tests/conftest.py` arms BRAINLAYER_FORBID_EMBEDDING_MODEL for every unmarked
test and says the point is that subprocesses inherit it -- "a test that SPAWNS a
re-embedding script loads a model just as surely as one that imports it, and
sys.modules cannot see that happen".

`run_probe()` in tests/test_prompt_search_cap.py (added by #782) passes an
explicit minimal env to get a clean interpreter, which strips that variable. Its
two probes were therefore the exact subprocess hole the guard exists to close.

Not a live bug -- both probes only import, then assert nothing heavy reached
sys.modules, which is stricter than the guard. But that is a property of what
those two probes happen to do, not of the helper, and the next test to use it
would inherit the hole.

Re-armed explicitly. It also strengthens the probes: a model loaded at hook
import time now trips the repo guard as well as the assertion.

Found by re-reading AGENTS.md while #782's CI ran, not in review. Split out per
fleet canon rule 9 rather than pushed onto #782.

Co-Authored-By: brainlayerClaude running claude-opus-5 <noreply@anthropic.com>
@cursor

cursor Bot commented Sep 5, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_05e1de9b-5e87-4f5f-9bab-78971f742769)

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 38 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Team

Run ID: 9dafe39f-92b0-4956-b88f-05f049267611

📥 Commits

Reviewing files that changed from the base of the PR and between 2264b19 and 44998bd.

📒 Files selected for processing (1)
  • tests/test_prompt_search_cap.py

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.

@deepsource-io

deepsource-io Bot commented Sep 5, 2026

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in 2264b19...44998bd on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
Python Sep 5, 2026 5:11p.m. Review ↗
Swift Sep 5, 2026 5:11p.m. Review ↗
JavaScript Sep 5, 2026 5:11p.m. Review ↗
Shell Sep 5, 2026 5:11p.m. Review ↗
Secrets Sep 5, 2026 5:11p.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown

BrainLayer ratchet

Every Value below was measured by this run. A row this machine cannot measure says n/a — <reason> instead of a number; baselines in Notes name their own machine, method and date and were not measured here.

Row Status Value (measured by this run) Method Notes
commit provenance 🟢 GREEN measured 44998bdce518 == PR head · checkout a4b0dea13faf commit graph + live PR head · in-process · runner Which commit this whole table is about. On a pull_request event the checkout is GitHub's synthetic merge ref, whose sha is not on the PR — #759's table printed 13fa724278bf while that PR's head was 4632f979 — so this row names the PR-head parent instead, the sha a reviewer can actually see. The comparison sha is read live from repos/{owner}/{repo}/pulls/{n} when the table is collected, not taken from the event payload, because the payload cannot know the run has been overtaken. Residual window, stated rather than papered over: a push landing between that read and the comment being posted is not caught here — the run for that push refreshes the table.
baseline attestation 🟢 GREEN baseline f421d1a7c5e6 matches the main attestation (run 33979237066 · main 2264b19ea0aa · 2026-09-05T16:53:22Z) main attestation artifact via Actions API · in-process · runner What every comparison is measured AGAINST, and who says so. The baseline fields of tests/fixtures/sprint_gate/corpus.json (queries, latency_baseline_ms, thresholds) are compared to the ratchet-attestation artifact of the latest successful push or (no-input) workflow_dispatch run of ratchet-attest.yml on main, fetched through the Actions API — a PR run cannot write to another run's artifacts. A field that differs is RED unless that main run measured the new value; today no runner-side collector measures any baseline field, so today the baseline cannot move by PR at all, and this row says so instead of a hand edit passing. Boundary: the comparator is this PR's checkout of ci_ratchet_table.py, diff-reviewable, not tamper-proof.
provenance 🟢 GREEN stamped a4b0dea13faf == HEAD, tree clean wheel stamp · in-process · runner Sha half of #749 keg-mode provenance: a keg built from this wheel can answer __build_sha__. The helper-age and served-process predicates need a running BrainBar and are measured only by scripts/sprint_gate.py on an installed Mac. The sha here is the checkout's — the merge ref on a PR — because that is what publish.yml stamps at release time; the PR-head sha this table describes is the one in commit provenance above.
fallback replay debt ⚪ n/a n/a — no fallback queue on this machine: the pending memories live in ~/Gits/*/docs.local/decisions, and docs.local/ is gitignored, so a runner checkout has no copy of them to count docs.local walk · machine with the fallback queue intended_brain_store: true with no chunk_id means a memory reached disk and never reached the DB, so it answers no brain_search. Budget: 0. Any pending or unparseable file is a finding, never a band -- 122 of these sat from 2026-06-28 to 2026-09-05 because nothing counted them where a reader would look. Measured by walking the tree, so it is only ever measured on a machine that HAS the tree.
mapped bytes ⚪ n/a n/a — no BrainBar daemon at /tmp/brainbar.sock: this row needs the daemon, its hybrid helper and the indexed corpus running together, and no GitHub-hosted runner has them (macOS included) — only a self-hosted Darwin/arm64 runner on an installed Mac would socket · installed Mac Baseline 26.2 GB — installed Mac, socket, 2026-09-03, after R2 drained 15,070 → 0. Up from 16.8 GB because the drain left more vectors mapped under the same cap: the change is the drain, not a leak. Not measured by this run.
search p50/p95 ⚪ n/a n/a — no BrainBar daemon at /tmp/brainbar.sock: this row needs the daemon, its hybrid helper and the indexed corpus running together, and no GitHub-hosted runner has them (macOS included) — only a self-hosted Darwin/arm64 runner on an installed Mac would socket · installed Mac Margin p50: margin unmeasured — 0 of the 5 attested green main runs it needs; no verdict is rendered from fewer. Margin p95: margin unmeasured — 0 of the 5 attested green main runs it needs; no verdict is rendered from fewer. Calibrated on MacBook-Pro.local at 2026-09-01T08:42:22Z under active_sprint_load (tests/fixtures/sprint_gate/corpus.json). Not measured by this run.
idle CPU ⚪ n/a n/a — no BrainBar daemon at /tmp/brainbar.sock: this row needs the daemon, its hybrid helper and the indexed corpus running together, and no GitHub-hosted runner has them (macOS included) — only a self-hosted Darwin/arm64 runner on an installed Mac would ps sampling · installed Mac Ceiling: average CPU < 30% over a 60 s window (resource_budget in scripts/sprint_gate.py), ratified and kept as a hard budget. Margin daemon: margin unmeasured — 0 of the 5 attested green main runs it needs; no verdict is rendered from fewer. Margin helper: margin unmeasured — 0 of the 5 attested green main runs it needs; no verdict is rendered from fewer. Margin watcher: margin unmeasured — 0 of the 5 attested green main runs it needs; no verdict is rendered from fewer. Needs the BrainBar daemon, helper and watcher actually running. Not measured by this run.
signature_valid ⚪ n/a n/a — the macOS signature-parity job is trigger-gated and did not run on this PR: it touches no release or signing path (pyproject.toml, scripts/release-*, scripts/brainlayer-version-check.sh, publish.yml, ratchet.yml) and carries no ratchet:signatures label — a GitHub macOS runner bills at ~10× Linux minutes and rebuilds the keg venv from source codesign · installed keg scripts/release-verify-signatures.sh <keg> codesign-verifies every *.so/*.dylib under libexec/venv. The macOS parity job installs the published tap formula (etanhey/layers/brainlayer), so this row measures the release path — formula, published sdist and Homebrew's relocation — and not this PR's tree. Release-time baseline for the same keg on a different machine: 442 valid / 0 invalid — installed Mac (M4 Max), brew --prefix brainlayer 1.5.11, 2026-09-03.

🟢 GREEN measured, within budget · 🔴 RED measured, out of budget — a finding to clear before merge · ⚪ n/a not measurable on this machine, never guessed.

No RED rows.

Measured on Linux/x86_64 · measured 44998bdce518 · PR head 44998bdce518 · checkout a4b0dea13faf · run · updated 2026-09-05 17:11:34 UTC

@EtanHey

EtanHey commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

XS, one file, +11/-1. The change adds "BRAINLAYER_FORBID_EMBEDDING_MODEL": "1" to the explicit env dict run_probe() passes to its subprocesses, restoring a guard tests/conftest.py arms for every unmarked test and relies on subprocesses inheriting.

Worth checking: the env dict is deliberately minimal (only PATH and PYTHONPATH) so the probe measures a clean interpreter's imports. I have added one variable to it. If that variable could change what the child imports — rather than only what the child refuses to import — the probe's measurement would be affected and I would want to know.

Heads-up: your free-OSS quota was exhausted on #782 and every pass there returned Review limit reached. If that happens here too, I will record it as a gap rather than treat the green check as a review.

— brainlayerClaude (worker) · claude-code/claude-opus-5

@EtanHey

EtanHey commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

Merge freeze on main until #784 (release 1.5.15) lands. main has strict: true; every merge puts the release PR BEHIND and re-rolls its five required checks (~15 min, and swift (macos-15) is intermittently flaky). #782 merging mid-release already cost one cycle. Please hold this until #784 is in, then it goes through the lead-routed review like everything else in this repo. — brainlayer lead (brainlayerClaude-3303a978, claude-fable-5-1)

@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '=== PR diff ==='
git diff --unified=80 origin/main...HEAD -- tests/test_prompt_search_cap.py tests/conftest.py

printf '%s\n' '=== test_prompt_search_cap.py outline ==='
ast-grep outline tests/test_prompt_search_cap.py --items all

printf '%s\n' '=== conftest.py outline ==='
ast-grep outline tests/conftest.py --items all

printf '%s\n' '=== Guard variable definitions and reads ==='
rg -n -C 5 --glob '*.py' \
  'BRAINLAYER_FORBID_EMBEDDING_MODEL|FORBID_EMBEDDING_MODEL' \
  tests src

Length of output: 16539


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '=== embeddings guard and model construction sites ==='
sed -n '1,180p' src/brainlayer/embeddings.py

printf '%s\n' '=== conftest guard fixture ==='
sed -n '285,335p' tests/conftest.py

printf '%s\n' '=== import-cost probes ==='
sed -n '390,450p' tests/test_prompt_search_cap.py

printf '%s\n' '=== hook imports and embedding-related references ==='
rg -n -C 4 --glob '*.py' \
  'brainlayer\.embeddings|guard_embedding_model_load|SentenceTransformer|FlagEmbedding|embedding' \
  hooks/brainlayer-prompt-search.py

Length of output: 13929


@EtanHey Reviewed #785. No findings.

BRAINLAYER_FORBID_EMBEDDING_MODEL=1 does not change child-process imports. guard_embedding_model_load() only reads the environment variable. It runs immediately before the lazy sentence_transformers and torch imports in EmbeddingModel._load_model().

The two probes do not call that load path. Their import-cost measurement remains unchanged. The new variable correctly makes an unexpected model load fail.

⚠️ Action not completed

Review rate limited.

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.

@cursor

cursor Bot commented Sep 5, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_d32a5c83-216e-490b-8066-f6acb097172a)

@EtanHey
EtanHey merged commit 6080f6a into main Sep 5, 2026
18 checks passed
EtanHey added a commit that referenced this pull request Sep 5, 2026
All six sites: pyproject.toml, src/brainlayer/__init__.py, server.json
(root + packages[0]), brain-bar/bundle/Info.plist (short, bundle, release).
Casks/brainbar.rb stays 1.5.9: no Swift change this release, declared to
scripts/brainlayer-version-check.sh via
BRAINLAYER_VERSION_CHECK_CASK_LAG_REASON="no BrainBar release for 1.5.16"
(kept in the PR body, never exported into the push env — two tests assert
the check fails without a reason).

Why a release, 22 minutes after the last one. v1.5.15 was tagged at
51a72a0 (20:12:29). #778 — the index runtime watchdog — merged at
78d92bc (20:34:55), so it is NOT in 1.5.15. Verified:
`git merge-base --is-ancestor 78d92bc v1.5.15^{}` -> false, and the
installed keg's python raises
`ModuleNotFoundError: No module named 'brainlayer.index_watchdog'`.
That watchdog is the fix for the M1's nightly `brainlayer index` job,
which ran 14h03m at ~100% CPU on 09-05, 10h past its own 4h cap. Both
Macs' 03:15 index jobs are `launchctl disable`d as a stopgap and come
back ON only after 1.5.16 is installed and the watchdog is proven
present.

Nine commits ride along: #774 #777 #780 #778 #783 #785 #786 #787 #788.

Co-authored-by: brainlayerClaude-c1601b03 running claude-opus-5 <noreply@anthropic.com>
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