Skip to content

fix(launchd): pin the nightly backup template to the keg interpreter (XS) - #817

Open
EtanHey wants to merge 1 commit into
mainfrom
wt/launchd-pin
Open

fix(launchd): pin the nightly backup template to the keg interpreter (XS)#817
EtanHey wants to merge 1 commit into
mainfrom
wt/launchd-pin

Conversation

@EtanHey

@EtanHey EtanHey commented Sep 8, 2026

Copy link
Copy Markdown
Owner

Summary

Found by the PR #815 pair review (finding 5). #790 pinned every hook to the keg python; the launchd template for the nightly JSONL backup was missed and still shipped /usr/bin/env python3.

That matters for the reason AGENTS.md already documents: bare python3 fronts the framework python, whose _brainlayer.pth injects ~/Gits/brainlayer/src. A git checkout at the repo root therefore silently re-aims the job — and the job here is the one that prevents data loss.

Latent, not active — and why it still matters

The installed plist on this machine is safe: it runs through brainlayer-env-run.sh with BRAINLAYER_PYTHON set to the keg. I measured that directly. The template is what a reinstall renders, so the hazard is latent rather than live.

That distinction is the whole point of shipping this separately: it is exactly the shape that put a checkout's install.sh in front of every LaunchAgent on 09-05. The reviewer and I initially disagreed here — we had each measured a different file, and both measurements were correct.

The test reuses the existing gate

test_launchd_plist_templates_pin_their_interpreter calls hook_python.is_pinned_interpreter — the gate #790 already built — rather than inventing a second definition of "pinned", so the two cannot drift apart. It walks every launchd template, judges only direct python invocations (a wrapper script makes no interpreter claim), and fails on anything it cannot vouch for, per that gate's affirmative stance.

Verified RED against origin/main's plist and green with the pin. 88 passed, 1 skipped.

Scope

Bot policy

Read brainlayer/AGENTS.md"Do not route mandatory reviews to Bugbot or Greptile" → panel = CodeRabbit + Codex only. Non-core diff (a plist template and a test), so Bugbot is off by tiering as well.

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


Note

Low Risk
Template-only interpreter pin plus a regression test; no runtime auth, data, or install-script changes.

Overview
Fixes a gap left after #790: the launchd template for the nightly JSONL backup still invoked /usr/bin/env python3, so a reinstall could run the backup through PATH/framework python and silently pick up a checkout via _brainlayer.pth — risky for the job that prevents data loss.

The template now calls /opt/homebrew/opt/brainlayer/libexec/venv/bin/python directly (with an inline comment matching the hooks rationale).

Adds test_launchd_plist_templates_pin_their_interpreter, which walks every launchd/*.plist, treats /usr/bin/env … as unpinned, and reuses hook_python.is_pinned_interpreter so launchd and hook policy stay aligned.

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

Note

Pin nightly backup launchd template to keg interpreter

Replaces /usr/bin/env python3 in the launchd backup ProgramArguments with the Homebrew keg virtual-environment interpreter at /opt/homebrew/opt/brainlayer/libexec/venv/bin/python, using the stable opt/ symlink. Risk: the job now fails if the Brainlayer keg is unlinked or the opt/ symlink is removed, since it no longer falls back to PATH-resolved python3.

📊 Macroscope summarized dee6a59. 1 file reviewed, 1 issue evaluated, 0 issues filtered, 1 comment posted

🗂️ Filtered Issues

…(XS)

Found by the PR #815 pair review. #790 pinned every hook to the keg python; the
launchd template for the nightly JSONL backup was missed and still shipped
`/usr/bin/env python3`.

That matters here for the reason AGENTS.md already documents: bare `python3`
fronts the framework python, whose `_brainlayer.pth` injects
`~/Gits/brainlayer/src`. So a `git checkout` at the repo root silently re-aims
the job -- and the job in question is the one that PREVENTS data loss.

The installed plist on this machine happens to be safe: it goes through
brainlayer-env-run.sh with BRAINLAYER_PYTHON set to the keg. The template is what
a reinstall would render, so the hazard is latent rather than active. That is
exactly the shape that put a checkout's install.sh in front of every LaunchAgent
on 09-05.

The test reuses the gate #790 already built -- hook_python.is_pinned_interpreter
-- rather than inventing a second definition of "pinned", so the two cannot drift.
It walks every launchd template, judges only direct python invocations (a wrapper
script makes no interpreter claim), and fails on anything it cannot vouch for.
Verified RED against origin/main's plist and green with the pin.

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

cursor Bot commented Sep 8, 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_129145ca-5652-4e3f-84a5-44976de6ab69)

@EtanHey EtanHey added the size:XS Tight-loop PR size: 50 or fewer hand-written lines changed label Sep 8, 2026
@EtanHey

EtanHey commented Sep 8, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Focus: the test judges only args[0] (plus args[1] for the /usr/bin/env form). If a template ever invokes python through a wrapper that itself picks the interpreter, this gate says nothing — by design, since a wrapper makes no interpreter claim. Worth confirming that boundary is the right one, because the installed job here does exactly that and is safe only because BRAINLAYER_PYTHON is set.

Also worth a look: hardcoding the ARM Homebrew prefix makes the template machine-specific. AGENTS.md argues that is the intended failure for hooks — "bad interpreter" loudly beats a silent run against the wrong library — and I applied the same stance here. Push back if a launchd template deserves different treatment from a hook shebang.

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

@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 35 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: Advanced

Run ID: 7fa202fa-8817-4372-97e8-b092652e86b9

📥 Commits

Reviewing files that changed from the base of the PR and between 105dd47 and dee6a59.

📒 Files selected for processing (2)
  • launchd/com.brainlayer.jsonl-backup.plist
  • tests/test_hook_python.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.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@github-actions

github-actions Bot commented Sep 8, 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 dee6a5981c80 == PR head · checkout c8da58666395 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 34268957015 · main 105dd47e8bb8 · 2026-09-08T19:26:32Z) 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. The calibrated socket collector can license p50/p95; every absent measured path stays locked, so missing collection never passes as permission for a hand edit. Boundary: the comparator is this PR's checkout of ci_ratchet_table.py, diff-reviewable, not tamper-proof.
provenance 🟢 GREEN stamped c8da58666395 == 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 dee6a5981c80 · PR head dee6a5981c80 · checkout c8da58666395 · run · updated 2026-09-08 20:00:22 UTC

python, whose _brainlayer.pth injects ~/Gits/brainlayer/src - so a `git checkout`
at the root would silently re-aim the nightly backup at a working tree. Same
reasoning as #790 for hooks; the `opt/` symlink outlives any single keg. -->
<string>/opt/homebrew/opt/brainlayer/libexec/venv/bin/python</string>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High launchd/com.brainlayer.jsonl-backup.plist:17

On Intel Homebrew installations, the scheduled backup cannot start because /opt/homebrew/opt/brainlayer/libexec/venv/bin/python does not point to the installed keg at /usr/local/opt/brainlayer/libexec/venv/bin/python. Render the actual Homebrew prefix/interpreter or invoke a prefix-aware wrapper instead of hard-coding /opt/homebrew.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @launchd/com.brainlayer.jsonl-backup.plist around line 17:

On Intel Homebrew installations, the scheduled backup cannot start because `/opt/homebrew/opt/brainlayer/libexec/venv/bin/python` does not point to the installed keg at `/usr/local/opt/brainlayer/libexec/venv/bin/python`. Render the actual Homebrew prefix/interpreter or invoke a prefix-aware wrapper instead of hard-coding `/opt/homebrew`.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS Tight-loop PR size: 50 or fewer hand-written lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant