feat: preflight telemetry capture loop pieces 1–4 (#65)#101
Merged
Knapp-Kevin merged 3 commits intoApr 29, 2026
Merged
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
48f3aa1 to
37e9fc5
Compare
…AI#65) Adds opt-in local-only preflight telemetry — captures preflight events and downstream tool engagement for failure-mode triage. Default off; hashed by default; raw via separate env var. New module: preflight_telemetry.py - Salt at ~/.bicameral/salt (mode 0o600), per-install, race-safe init - hash_topic, hash_file_paths (order-independent set hash) - new_preflight_id (UUIDv4) - write_preflight_event, write_engagement (JSONL append, mode 0o600) - _maybe_rotate (50MB / 30 days, keeps last 5) preflight_id plumb-through: - PreflightResponse, LinkCommitResponse, BindResponse, RatifyResponse gain optional preflight_id: str | None field - update.py dict returns also gain preflight_id key (11 sites) - server.py inputSchema for affected tools accepts optional preflight_id Pieces 5 (SessionEnd reconciliation skill) and 6 (triage CLI) are deferred to follow-up plans BicameralAI#65-pt2 and BicameralAI#65-pt3. Closes BicameralAI#65 (pieces 1–4) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
37e9fc5 to
b22900a
Compare
The Tier 1 lint gate from BicameralAI#102 caught 32 stylistic findings on this branch (22 in the new test files plus 10 in pre-existing files): - timezone.utc → datetime.UTC alias (UP017 from PEP 695) - import sorting (I001) - 12 files needing ruff format All auto-fixable. No behavior change. 28 telemetry tests still pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…cure mypy flagged the os.PathLike return type as incompatible with the actual BufferedWriter from os.fdopen. Use typing.IO[bytes] which is what the with-block consumes anyway. Pure type fix; no behavior change. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced Apr 29, 2026
Closed
This was referenced May 6, 2026
Knapp-Kevin
pushed a commit
to Knapp-Kevin/bicameral-mcp
that referenced
this pull request
May 21, 2026
…st (BicameralAI#192) Single env var now owns the entire telemetry-flag namespace. Three accepted forms: bool (`0`/`off`/`false`/`no` → all off; `1`/`on`/`true`/`yes` → relay only), csv (`relay,preflight,raw`), and unset (default → relay only). New `telemetry_flags.py` module owns parsing; `consent.telemetry_allowed()` and `preflight_telemetry.{telemetry_enabled, raw_capture_enabled}` delegate to a frozen `TelemetryFlags` cached once per process. Backwards-compat preserved on three axes: 1. Legacy `BICAMERAL_PREFLIGHT_TELEMETRY=1` and `BICAMERAL_PREFLIGHT_TELEMETRY_RAW=1` continue to work as additive overlays — first read of either emits a one-line stderr deprecation warning per process. Removed in v1.x. 2. `BICAMERAL_TELEMETRY=1` semantics unchanged (relay only — does NOT auto-enable preflight). 3. Non-canonical truthy values (`enabled`, `t`, `active`, etc. — used in pre-BicameralAI#192 deployments) map to relay-only with a stderr warning pointing at the canonical form. Caught by Codex review as a P2 finding; preserves the pre-BicameralAI#192 contract that any non-OFF value enabled relay. Semantics: - CSV form is explicit — what's listed is on, what's not is off (so `BICAMERAL_TELEMETRY=preflight,raw` turns OFF the default-on relay, documented in the setup wizard). - `raw` always implies `preflight` (raw is a mode of preflight events; defensive double-check in `raw_capture_enabled()`). - Process-cached parsing via `lru_cache`; tests use `_reset_for_tests()` via an autouse fixture in `tests/conftest.py` so monkeypatched env vars take effect cross-test. 35 fixtures in `tests/test_telemetry_flags.py` cover all forms + integration with the existing call sites + the legacy-truthy preservation case. 87/87 green across all 7 telemetry-touching test files (including 52 regression tests for BicameralAI#39 / BicameralAI#101 / BicameralAI#112 behaviors). Closes BicameralAI#192. Unblocks BicameralAI#65 phase 4. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
First slice of issue #65 — local-only, default-off capture loop that records
bicameral.preflightevents plus downstream tool engagement, attributable per-call via a newpreflight_id. Used for self-triage of false fires / silent misses; never leaves the machine and is not part of the existing PostHog relay path.This PR covers pieces 1–4 only. Pieces 5 (SessionEnd reconciliation skill) and 6 (triage CLI) are deferred to follow-up plans
#65-pt2and#65-pt3.Architecture
Privacy stance
BICAMERAL_PREFLIGHT_TELEMETRY=1to capture; unsetting it makes every writer a no-op.BICAMERAL_PREFLIGHT_TELEMETRY_RAW=1to additionally store plaintext — separate, explicit opt-in.surfaced_idsare written raw. Opaque ledgerdecision_idstrings, already non-PII; hashing them would defeat the only useful triage join. Documented as an invariant in the module docstring (audit S1).~/.bicameral/, mode0o600. Never leaves the machine. Separate path from the PostHog relay intelemetry.py.Test results
Pre-existing collection failures in
test_v055_region_anchored_preflight.pyandtest_v0412_preflight.py(refer to old preflight internals:_merge_decision_matches,_has_actionable_signal_in_search) are unchanged on this branch — they fail onorigin/devunmodified. Verified viagit stashround-trip.Audit reference
Plan:
plan-preflight-telemetry-65.mdin the parent worktree.Audit verdict: PASS with MF1 applied inline.
_get_or_create_saltwraps theos.O_EXCLos.openin atry/except FileExistsErrorand falls back to_SALT_FILE.read_bytes()on the race-loser path. Two dedicated tests (test_salt_race_loser_reads_winner_bytes,test_salt_race_loser_handles_exclusive_failure) exercise the path including a synthetic race injection viamonkeypatch.setattr(os, "open", flaky_open).surfaced_idswritten raw) — documented inpreflight_telemetry.pymodule docstring.handle_update, all carrypreflight_id.Out of scope (tracked separately)
#65-pt2). Reads the JSONL files, classifies entries assuspected_miss/suspected_false_fire/normal, writesfailure_review.jsonl.#65-pt3).bicameral-mcp triageCLI for labeling failure rows; promotion totests/eval/real_dataset.jsonlrequires explicit redaction.Test plan
Closes
Closes #65 (pieces 1–4 only — pieces 5–6 tracked separately as
#65-pt2and#65-pt3).🤖 Generated with Claude Code