Make adaptive refresh agent-aware with explicit local activity consent#2111
Conversation
There was a problem hiding this comment.
Pull request overview
This PR makes Adaptive refresh agent-aware by default by feeding recent local Codex/Claude activity into the adaptive cadence decision, while keeping the Agent Sessions UI (and all remote discovery/SSH behavior) explicitly opt-in.
Changes:
- Extend the shared
AdaptiveRefreshPolicyCoredecision table with an optionallastCodingActivityAtsignal that caps otherwise-slower unconstrained delays at 5 minutes. - Make missing/unrecognized
refreshFrequencyresolve to.adaptive, and enable bounded local session monitoring when Adaptive is effective—without enabling the Agent Sessions UI. - Update replay tooling to treat activity-aware Adaptive as production (
adaptive), preserve the historical baseline asadaptive-menu-only, and keepadaptive-activityas a CLI alias.
Reviewed changes
Copilot reviewed 30 out of 30 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| Tests/CodexBarTests/SettingsStoreTests.swift | Update default/fallback expectations for refresh frequency; add explicit 5m preservation test. |
| Tests/CodexBarTests/CodexSessionRolloutTests.swift | Add bounded-rollout parsing coverage for local scanner limits. |
| Tests/CodexBarTests/ClaudeSessionMappingTests.swift | Add transcript list limiting coverage. |
| Tests/CodexBarTests/AgentSessionMenuDescriptorTests.swift | Cover Adaptive-only monitoring enablement, projection, and constraint pausing. |
| Tests/CodexBarTests/AdaptiveRefreshTimerTests.swift | Add timer pull-forward behavior tests for coding activity; ensure fixed-mode behavior stays in-memory only. |
| Tests/CodexBarTests/AdaptiveRefreshPolicyTests.swift | Verify app adapter forwards coding activity into policy decisions. |
| Tests/AdaptiveReplayKitTests/MenuOnlyAdaptivePolicyTests.swift | Reframe replay tests around production adaptive and the historical menu-only baseline. |
| Tests/AdaptiveReplayKitTests/AdaptiveRefreshPolicyCoreTests.swift | Add core policy tests for coding-activity cap, boundaries, and constraints. |
| Tests/AdaptiveReplayCLITests/CLIArgumentsTests.swift | Ensure adaptive-activity remains accepted and maps to production adaptive. |
| Sources/CodexBarCore/LocalAgentSessionScanner.swift | Bound process/rollout/transcript candidate handling to configured limits. |
| Sources/CodexBarCore/AgentSession.swift | Extend scan config with bounds; add transcript limiting support. |
| Sources/CodexBar/UsageStore+AdaptiveRefresh.swift | Wire lastCodingActivityAt through adaptive decisions and timer advancement behavior. |
| Sources/CodexBar/UsageStore.swift | Track lastCodingActivityAt in-memory; refactor timer restart helper. |
| Sources/CodexBar/StatusItemController+AgentSessions.swift | Forward activity timestamp into adaptive refresh; centralize agent session settings sync. |
| Sources/CodexBar/StatusItemController.swift | Track refresh-frequency changes alongside agent-session settings; use new wiring helpers. |
| Sources/CodexBar/SettingsStore.swift | Change missing/unrecognized refresh-frequency fallback to .adaptive. |
| Sources/CodexBar/AgentSessionsStore.swift | Enable local monitoring under Adaptive without enabling UI; project activity timestamp-only when UI is off. |
| Sources/CodexBar/AdaptiveRefreshPolicy.swift | Thread coding activity input through the production policy adapter into the shared core. |
| Sources/AdaptiveReplayKit/README.md | Update replay docs to reflect production activity-aware Adaptive and historical baseline. |
| Sources/AdaptiveReplayKit/HistoricalPolicies.swift | Add historical menu-only Adaptive baseline policy (adaptive-menu-only). |
| Sources/AdaptiveReplayKit/CandidatePolicies.swift | Remove replay-only activity candidate (now folded into production policy). |
| Sources/AdaptiveReplayKit/BaselinePolicies.swift | Feed coding activity into production replay policy; add helper for omitting the signal. |
| Sources/AdaptiveReplayCLI/main.swift | Update CLI help text and document alias behavior. |
| Sources/AdaptiveReplayCLI/CLIArguments.swift | Replace candidate policy with historical baseline; implement adaptive-activity alias parsing. |
| Sources/AdaptiveRefreshCore/AdaptiveRefreshPolicyCore.swift | Add coding-activity signal, reason, thresholds, and cap behavior to shared decision table. |
| README.md | Update high-level feature description and privacy note for adaptive local scan behavior. |
| docs/refresh-loop.md | Document new default fallback, activity signal, scanner bounds, and pause conditions. |
| docs/predictive-refresh-policy.md | Update decision record to reflect agent-aware adaptive default extension and constraints. |
| CHANGELOG.md | Document default/fallback change, activity cap behavior, and bounded/local-only scan implications. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| self.localRefreshInFlight = true | ||
| let sessions = await self.localScanner.scan() | ||
| self.localRefreshInFlight = false | ||
| guard !Task.isCancelled, self.settings.agentSessionsEnabled else { return } | ||
| self.localSessions = sessions | ||
| self.lastUpdatedAt = Date() | ||
| guard !Task.isCancelled, self.localMonitoringEnabled else { return } | ||
| self.applyLocalScanResult(sessions) |
There was a problem hiding this comment.
Fixed in 04df269. LocalAgentSessionScanner.scan is now @concurrent, so the synchronous ps/lsof work and transcript parsing do not inherit a MainActor caller. I kept structured concurrency instead of using a detached task, which preserves cancellation, priority, and task-local values. The 37 repair-focused tests pass and make check is clean.
|
Codex review: needs real behavior proof before merge. Reviewed July 17, 2026, 3:49 PM ET / 19:49 UTC. Summary Reproducibility: not applicable. This PR introduces a new refresh mode and consent flow rather than fixing a reproducible existing defect. The supplied earlier-head packaged run is useful behavioral evidence but does not reproduce a bug on current main. Review metrics: 2 noteworthy metrics.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Maintainer decision needed
Security Review detailsBest possible solution: Record the owner’s acceptance or rejection of the fresh-install/legacy contract, rebase onto current main while resolving localization tails, then provide a redacted exact-head packaged-app run covering plain Adaptive, opt-in agent-aware consent, same-launch notification authorization, bounded scanning, and future timestamps. Do we have a high-confidence way to reproduce the issue? Not applicable: this PR introduces a new refresh mode and consent flow rather than fixing a reproducible existing defect. The supplied earlier-head packaged run is useful behavioral evidence but does not reproduce a bug on current main. Is this the best way to solve the issue? Unclear: separating agent-aware scanning from plain Adaptive is the narrowest privacy-preserving shape in the supplied diff, but only the owner can approve the fresh-install default and markerless-legacy upgrade contract. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against cfb7b8036087. Label changesLabel changes:
Label justifications:
Evidence reviewedWhat I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (6 earlier review cycles)
|
|
@clawsweeper re-review The two current-head blockers are fixed in
The new settings suite covers fresh persistence, both launch markers, existing config, String and non-String invalid values, and all 7 valid cadence values across fresh and marked states. The PR body now states the only historical ambiguity: a completely untouched, configless v0.1-v0.3 install left no durable signal that differs from a new install.
|
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
@clawsweeper re-review Head
The PR body now puts this blocker-resolution matrix before the older policy replay and states the proof limits explicitly. |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
Maintainer review: needs owner decision; not merging this head. Thanks @hhh2210 — the implementation and proof here are unusually thorough. I read the full 62-file diff plus the adjacent scanner, timer, settings, and onboarding paths. The privacy boundary itself looks credible:
I am parking the PR for these blockers:
Verification performed on PR head
Recommended next step: owner decides both product questions. If approved, fix the code blockers, merge current |
|
Merged with the owner-decided opt-in shape. Plain Adaptive remains the fresh-install default. Adaptive (agent-aware) is a separate picker choice and requires explicit consent. Privacy and safety verification:
Verification:
Merge commit: Thanks @hhh2210 for the original agent-aware design and diagnosis. The contribution is preserved in the merge history. |
|
Follow-up CPU-budget coverage landed in #2276 as The new normal-suite tests cover the agent-aware Adaptive path from #2111: a synthetic capped 512-entry/depth-1 session tree must complete below 250 ms and never visit beyond the cap, while plain Adaptive plus undecided/declined consent must make zero local scan calls through a spy seam. The implementation now gives adaptive metadata enumeration a 150 ms internal deadline and stops Codex parsing after all live working directories are matched. Verification: 24 focused tests passed; |
Problem
#2029 found that the replay-only activity candidate did not improve global p95 menu staleness, so that result did not support a broad production claim. Main now has the local Agent Sessions scanner from #1968, which provides an attributable Codex/Claude activity signal without adding another detector.
This PR has a narrower target: while local coding is active, an unconstrained Adaptive decision should not wait longer than the old 5-minute default. Global p95 remains unchanged.
Consent and upgrade boundary
Adaptive cadence is not permission to inspect local process or session metadata.
undecided,allowed, ordeclinedchoice. Onlyallowedenables Adaptive-only scanning.undecidedafter upgrade and perform no Adaptive-only scan until they allow it.The historical edge case is unchanged: a completely untouched, configless v0.1-v0.3 install left no durable state that can distinguish it from a new install, so that cohort follows the fresh-install default.
Consent UI
Use Menu Activity Onlyalso responds to Escape. The same six consent/settings strings are present in all supported app language catalogs.Packaged runtime proof
Redacted exact-head proof report
The proof bundle came from clean commit
2c0a41479602a8aa804139cbf50bef358e32d3da. Its embeddedCodexGitCommitis2c0a4147; the debug executable SHA-256 iseb3361e2a96e9cae269ce4490e96ed555da09ce9f802268e33af24dfc4712499.The bundle ran with network access denied, Keychain access disabled, and synthetic
HOME/CODEX_HOMEdata containing one recent fixture rollout. It did not read a real Codex or Claude transcript.adaptive+undecided; showed the prompt; 0 local session scans before a decisionadaptive, consent missingundecided; showed the prompt; 0 local session scansfiveMinutes; no prompt; 0 local session scansallowedreplayedlongIdle 1800stocodingActivity 300s; 0 Tailscale/SSH eventsdeclinedreplayedThe persisted
allowedanddeclinedstates were replayed on relaunch to isolate the two runtime branches. Focused tests cover setter persistence, reload, invalid-value repair, existing-Adaptive behavior, and the Agent Sessions bypass.Sustained scan profile
The allowed process produced scanner starts at
t+0.000s,t+31.350s, andt+63.336s. The first scan pulled the pending Adaptive timer from 30 minutes to 5 minutes eight milliseconds later. No remote discovery, remote fetch, or SSH subprocess label appeared.A 65.646-second Time Profiler recording covered the next two scans:
AgentSessionsStore.refreshLocal,LocalAgentSessionScanner.scan,LocalAgentSessionScanner.processOutput, andSubprocessRunner.run;The raw trace SHA-256 is
be7943ea0588bdb6f60d60d3ddaa3ef65453f65dea5cc505f14cd1badae786a2. The trace stays local because Instruments embeds local paths and environment values. These are sampled runtime figures, not an energy claim.Policy replay
Frozen trace: 1,780 JSONL records, SHA-256
b1e4aa33180b7c177293eb9ed16b45e24e026d259600fba2b1b67b931b904f0b.Compared with menu-only Adaptive, the activity signal adds 2 simulated refreshes (
+0.29%) and removes all 4 observed active-coding delays above 5 minutes. Compared with fixed 5 minutes, it schedules 49.7% fewer simulated refreshes. These are policy-clock results, not observed provider requests.The trace comes from one machine. Activity covers 462 of 733 decision records, and p95 remains 1093 seconds. It samples activity at reconstructed decision points rather than replaying the 30-second scanner callback; the packaged profile above covers that callback.
Validation
make check: pass; SwiftFormat clean and SwiftLint reported 0 violationsThe released
adaptive-activityreplay CLI spelling remains a deprecated alias foradaptive, whileadaptive-menu-onlynames the historical baseline.