Skip to content

feat(kernel): gate the macOS Contacts cohort behind contacts - #5365

Merged
senamakel merged 4 commits into
tinyhumansai:mainfrom
senamakel:contacts-gate
Aug 4, 2026
Merged

feat(kernel): gate the macOS Contacts cohort behind contacts#5365
senamakel merged 4 commits into
tinyhumansai:mainfrom
senamakel:contacts-gate

Conversation

@senamakel

Copy link
Copy Markdown
Member

Summary

  • Adds a default-ON contacts feature gating memory::people::address_book's macOS CNContactStore reader.
  • Sheds 6 packages on macOSobjc2, objc2-foundation, objc2-contacts, block2 and two transitives (294 → 288 in the slim profile), proven cross-target.
  • Nearly free: the off-state already existed. address_book.rs has always shipped a non-macOS imp stub returning an empty contact list; the gate widens that stub's cfg.
  • No-op on Linux/Windows — these crates were never in those graphs, so the kernel-floor ratchet does not move and there is no number to lower.
  • 4 files, +42/−11.

Problem

objc2, objc2-foundation, objc2-contacts and block2 are declared unconditionally in the cfg(target_os = "macos") dependency table, so every macOS build links the Contacts framework bindings — including headless, embedded and CI builds that never touch an address book.

All four are used by exactly one file. Verified by grepping every <crate>:: path across src/ and finding no other consumer:

crate references in address_book.rs references anywhere else
objc2 5 0
objc2-foundation 1 0
objc2-contacts 2 0
block2 1 0

Solution

A leaf gate over a pre-existing off-state, which is what makes it cheap. address_book.rs already had:

#[cfg(not(target_os = "macos"))]
mod imp { /* returns Ok(vec![]) */ }

The change narrows the real implementation to all(target_os = "macos", feature = "contacts") and widens the stub to not(all(...)). Consequences:

  • read(), read_with(), AddressBookError and SystemContactsSource stay compiled in every build.
  • The people RPC surface is byte-identical — no controller, store or tool changes.
  • With the gate off, an address-book refresh seeds nothing instead of failing. That is the same behaviour Linux and Windows have always had.

The in-module test's cfgs are updated in lockstep, so its empty-result assertion now covers the gated-off macOS build rather than silently skipping it.

On verifying a macOS-only shed from Linux

I had earlier set this gate aside as "unverifiable on a Linux dev box, and it would not move the kernel-floor ratchet anyway, so claiming it would be dishonest." That reasoning was wrong on the first half. cargo tree resolves a foreign target's dependency graph without building it:

cargo tree --target aarch64-apple-darwin -e normal --prefix none \
  --no-default-features --features tokenjuice-treesitter        # 288 packages
cargo tree --target aarch64-apple-darwin -e normal --prefix none \
  --no-default-features --features tokenjuice-treesitter,contacts  # 294 packages

and with the feature off, -i objc2, -i objc2-foundation, -i objc2-contacts and -i block2 each report did not match any packages.

Two things worth flagging for whoever verifies the next shed:

  1. That error message is the proof of absence. It is the exact non-zero inversion scripts/assert-shed.sh was written to warn about — a naive if cargo tree -i … reads it backwards.
  2. assert-shed.sh does not take a --target, which is why this PR uses the raw cross-target invocation. Teaching it --target would be a reasonable follow-up so target-specific sheds get the same guard as the rest.

The second half of my earlier reasoning still stands: the ratchet is Linux-measured, so it correctly does not move here.

Submission Checklist

  • Tests added or updated — the existing read_with(&SystemContactsSource) test's cfgs are updated so the empty-result assertion (the failure path: no contacts available ⇒ Ok(vec![]), not an error) now also covers macOS-with-gate-off. No new test: the gate has no new behaviour to cover, only a wider cfg on a path that was already asserted on Linux/Windows.
  • Diff coverage ≥ 80% — the only non-manifest changes are two cfg attributes and the matching test cfgs; memory::people runs 49 tests.
  • Coverage matrix updated — N/A: no feature rows added, removed, or renamed.
  • All affected feature IDs listed under ## RelatedN/A: no matrix feature IDs affected.
  • No new external network dependencies — N/A: this removes dependencies, adds none.
  • Manual smoke checklist — N/A: no release-cut surface touched. contacts is default-ON and forwarded to the desktop shell, so the shipped app is unchanged.
  • Linked issue closed via Closes #NNNN/A: no dedicated tracking issue. Dependency-shed work under the kernelization program (Feature gates for core subsystems — tracking (lightweight harness builds) #4795refactor(kernel): collapse 124 flat domains into 31 gate-aligned families #5328feat(kernel): realign DomainGroup with the family directories #5332).

Impact

Runtime/platform: none for the shipped desktop app. contacts is default-ON and forwarded in app/src-tauri/Cargo.toml (verified by check-feature-forwarding.mjs), so macOS builds behave exactly as before.

Slim macOS builds (--no-default-features) lose CNContactStore seeding and get the empty-list stub — the behaviour Linux and Windows already had.

Compatibility: no public API change. AddressBookContact, AddressBookError, SystemContactsSource, read, read_with all keep their signatures in both builds.

Performance: removes the Contacts framework link and four crate compilations from slim macOS builds.

Security: slightly reduces the macOS TCC surface in slim builds — no CNContactStore access is compiled in at all, so there is nothing to prompt for.

Related

  • Closes:
  • Follow-up PR(s)/TODOs:
    • Teach scripts/assert-shed.sh a --target flag so target-specific sheds are guarded like the rest, instead of relying on a hand-run cargo tree.
    • runtime-node gate (xz2 + the static liblzma C build). Design settled: facade + stub, forced by ShellTool holding Option<Arc<NodeBootstrap>> as an always-compiled struct field — not by agent/harness_init, whose node_runtime_step() is a registration site and leaf-gateable on its own.
    • memory-git (git2 + vendored libgit2) — cross-repo; vendor/tinycortex must carve its inert memory::diff types out from behind git-diff first.

AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: contacts-gate
  • Commit SHA: a90d7b707

Validation Run

  • pnpm --filter openhuman-app format:checkcargo fmt --check clean. Prettier half N/A: no frontend files changed.
  • pnpm typecheckN/A: no TypeScript changed.
  • Focused tests: cargo test --lib openhuman::memory::people49 passed.
  • Rust fmt/check: cargo check --lib and --no-default-features --features tokenjuice-treesitter both clean. check-kernel-floor.sh unmoved at 312/285/6 (expected — Linux no-op). check-feature-forwarding.mjs passes.
  • Tauri fmt/check: manifest-only change (one forwarded feature); no Rust in the shell touched.

Validation Blocked

  • command: a native macOS build
  • error: no macOS host available
  • impact: The gated code is macOS-only, so the compiled path could not be exercised here. Mitigated as far as is possible from Linux: the dependency shed is proven by cross-target resolution (numbers above), and the code change is two cfg predicates whose off-branch is the pre-existing, already-tested Linux/Windows stub. CI's macOS lanes will compile the on-branch.

Behavior Changes

  • Intended behavior change: none in any shipped configuration. Slim macOS builds lose CNContactStore seeding.
  • User-visible effect: none — contacts is default-ON and forwarded to the desktop shell.

Parity Contract

Duplicate / Superseded PR Handling

  • Duplicate PR(s): none
  • Canonical PR: this one
  • Resolution: N/A

Sheds `objc2`, `objc2-foundation`, `objc2-contacts` and `block2` — plus two
transitives, 6 packages total — from slim macOS builds. All four are used
exclusively by `memory::people::address_book`; verified by grepping every
`<crate>::` path in src/ and finding no other file.

Nearly free, because the off-state already existed: `address_book.rs` has
shipped a non-macOS `imp` stub returning an empty contact list since before the
gate. The change is to widen that stub's cfg from `not(target_os = "macos")` to
`not(all(target_os = "macos", feature = "contacts"))`, and narrow the real one
to match. So `read()`, `read_with()`, `AddressBookError` and
`SystemContactsSource` stay compiled in every build, the `people` RPC surface is
byte-identical, and an address-book refresh with the gate off seeds nothing
rather than failing. The in-module test's cfgs are updated in lockstep so the
empty-result assertion covers the gated-off macOS build too.

I had earlier deferred this gate as "unverifiable on a Linux dev box". That was
wrong: cargo resolves a foreign target's graph without building it, so the shed
is provable from any host —

  cargo tree --target aarch64-apple-darwin -e normal --prefix none \
    --no-default-features --features tokenjuice-treesitter

goes 294 → 288 packages, and `-i objc2` / `-i objc2-foundation` /
`-i objc2-contacts` / `-i block2` all report "did not match any packages" with
the feature off. Note that error IS the proof of absence — the exact non-zero
inversion `scripts/assert-shed.sh` exists to warn about; that script does not
take a `--target`, hence the raw cross-target invocation here.

The Linux kernel-floor ratchet is deliberately unchanged at 312/285/6: these
crates were never in the Linux graph, so this gate is a no-op there and there is
no number to lower.

Verified: default and gates-off builds clean; `memory::people` 49 tests pass;
`check-feature-forwarding.mjs` passes with `contacts` forwarded to the shell.

Co-authored-by: Medulla <medulla@tinyhumans.ai>
@senamakel
senamakel requested a review from a team August 4, 2026 10:40

@greptile-apps greptile-apps 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.

senamakel has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 15 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 23845aa8-3428-4003-a7bc-7dd43ce3fe28

📥 Commits

Reviewing files that changed from the base of the PR and between 5efb24a and a23e5f5.

📒 Files selected for processing (6)
  • .github/workflows/ci-lite.yml
  • AGENTS.md
  • Cargo.toml
  • app/src-tauri/Cargo.toml
  • src/openhuman/memory/people/address_book.rs
  • tests/raw_coverage/app_state_credentials_raw_coverage_e2e.rs

Comment @coderabbitai help to get the list of available commands.

@senamakel senamakel self-assigned this Aug 4, 2026

@greptile-apps greptile-apps 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.

senamakel has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@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: 82b6d01cec

ℹ️ 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 thread src/openhuman/memory/people/address_book.rs
The CI workflow now runs tests for the memory people address book module and includes it in the expected gated-test file set, ensuring the new module's tests are properly covered and the feature-gate smoke check remains accurate.
The regex used to detect gated test files now also matches `cfg(not(feature = ...))` attributes and `cfg(all(..., feature = "contacts"))` patterns, ensuring the allowlist check covers additional feature-gating styles used in the codebase.

@greptile-apps greptile-apps 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.

senamakel has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@senamakel
senamakel merged commit 7af8b0e into tinyhumansai:main Aug 4, 2026
19 checks passed
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