Skip to content

feat(jit): add owner-fenced ambient notification feedback - #12808

Merged
Git-on-my-level merged 12 commits into
mainfrom
codex/jit-ambient-feedback-20260905
Sep 6, 2026
Merged

feat(jit): add owner-fenced ambient notification feedback#12808
Git-on-my-level merged 12 commits into
mainfrom
codex/jit-ambient-feedback-20260905

Conversation

@Git-on-my-level

@Git-on-my-level Git-on-my-level commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

What

Ambient JIT notifications now expose explicit Useful and Not relevant feedback actions. The owner, authenticated-session, and account-generation fences are rechecked at the feedback write and telemetry seams, stale native banners cannot resurrect after a cutover, and a delayed tap cannot dismiss a replacement card.

Why

Ambient notifications have no standing trigger, so planned-only snooze/disable controls must not be fabricated. Feedback must remain tied to the exact admitted candidate while account transitions and relaunches invalidate stale work. This keeps the existing single chat timeline and canonical feedback mutation path intact.

Changes

  • Route ambient feedback through the existing canonical Interject feedback mutation, retaining only Useful and Not relevant semantics.
  • Check the live card identity immediately before entering the existing asynchronous feedback mutation and repeat the identity check before dismissal; the feedback actor independently revalidates owner and account generation before its write and analytics.
  • Carry a per-process authorization nonce alongside the generation so a relaunch cannot reuse generation zero from an older native notification payload.
  • Carry opaque lane, delivery, candidate, and account-generation joins into analytics without emitting raw owner identity.
  • Reject stale planned or ambient JIT cards when their account generation no longer matches the current cutover control.
  • Materialize the JIT full prompt through the production prompt builder so replay fixtures can hash the exact runtime bytes.
  • Keep the feedback store process-scoped; this PR records analytics provenance but does not claim a durable JIT evaluator or adaptation loop.

Tests

  • xcrun swift test --package-path desktop/macos/Desktop --filter 'JITAmbientFeedbackTests|FloatingBarNotificationPreviewPolicyTests|ChatTranscriptGestureHarnessTests|FailLoudConfigTests|ChatDailySummaryTests|ProactiveNotificationKindTests' (104 passed: 16 JIT ambient, 23 floating policy, 21 transcript harness, 12 fail-loud, 25 daily summary, 7 notification kind)
  • xcrun swift test --package-path desktop/macos/Desktop --filter JITAmbientFeedbackTests (16 passed)
  • ./desktop/macos/scripts/swiftlint-wrapper.sh lint (0 violations)
  • ./desktop/macos/scripts/check_desktop_test_quality.py (baseline ratchet passed)
  • xcrun swift test --package-path desktop/macos/Desktop --filter FloatingBarNotificationPreviewPolicyTests (23 passed; current-generation presentation and mismatched-generation denial use the actual banner route)
  • git diff --check
  • Current-main integration: 24 repository preflight checks passed; the mandatory push-hook gates also passed.

No paid model calls or production flags were used.

Manual acceptance

floating-bar-functional is a source-coverage declaration; this change has no executed bridge-flow receipt. Before release, run that flow against the non-production app: launch signed in, open Ask Omi, submit the marker query, wait for the floating-bar chat to become idle, verify the echoed marker, and close Ask Omi. Record the run SHA, bundle, and bridge result before treating the flow as functional evidence.

Delivery ordering

The delivery actor records policy_approved before requesting the visible card so the durable row exists before presentation. It rechecks authenticated ownership immediately after that write, while the actual notification presenter rechecks authenticated ownership and account generation at its presentation seam. A generation change in the narrow interval after policy_approved and before presentation therefore rejects the card and invokes the existing dropped path, which terminalizes the row; policy_approved is not a presentation-success receipt. The floating bar's delayed feedback check is a pre-mutation identity fence, while the existing feedback actor remains the authoritative owner and account-generation write fence.

Chat drift evidence

The transcript harness derives the bound from the production 14 pt chat body: 14 * 1.25 + OmiMarkdownContent.chatLineSpacing(14) is 22.5 pt, with a 1.5 pt margin yielding a 24 pt bound. The same test asserts that the historical 48 pt multi-line glide is above that bound, while the live-edge harness exercises the real mounted transcript during 40 streaming flushes. This keeps the bound tied to typography and retains a negative control for the old regression.

Failure class

Failure-Class: FC-split-mutation-authority

Invariants

  • INV-CUTOVER-1: account-generation checks remain derived from the server-authoritative cutover control through its synchronized authority.
  • INV-AUTH-1: delayed ambient feedback remains bound to the original authenticated session at physical recording and response-publication boundaries.
  • INV-CHAT-1: feedback actions continue to project onto the shared canonical chat/notification identity and do not introduce a second transcript or surface-local writer.
  • INV-CHAT-2: the transcript gesture harness retains live-edge placement and explicit reader movement ownership while covering the measured typography bound.

Follow-up boundary

The paired legacy-versus-JIT quality/cost fixture is tracked separately and remains blocked until the actual runtime provider/model/rate-card attribution is resolved and approved.

Line-Count-Exception: desktop/macos/Desktop/Sources/AnalyticsManager.swift | 1693 -> 1710 | provenance fields stay beside the existing suggestion feedback event until analytics event ownership is split
Line-Count-Exception: desktop/macos/Desktop/Sources/FloatingControlBar/FloatingControlBarView.swift | 3130 -> 3158 | ambient feedback action routing remains beside the existing notification presentation seam
Line-Count-Exception: desktop/macos/Desktop/Sources/FloatingControlBar/FloatingControlBarWindow.swift | 5794 -> 5818 | canonical mutation call-site return handling remains at the existing owner boundary
Line-Count-Exception: desktop/macos/Desktop/Sources/ProactiveAssistants/Services/NotificationService.swift | 1535 -> 1706 | native JIT feedback serialization and session-generation fencing share the existing notification boundary

@cubic-dev-ai cubic-dev-ai 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.

3 issues found across 15 files

Confidence score: 2/5

  • JITProactivityDelivery.swift can present an ambient card using an outdated authorization snapshot after the owner’s cutover generation advances, creating a stale-authorization user-facing regression — revalidate authorization against the current generation before presentation.
  • InterjectSuggestionFeedbackStore.swift can leave a stale feedback row when an account-generation fence is supplied without an authorization snapshot, even though analytics are suppressed — include the account-generation fence in stale-row cleanup handling.
  • floating-bar-functional.yaml claims JITAmbientFeedback coverage without exercising it, which can hide regressions in that flow — remove the claim or add steps that actually validate the ambient feedback behavior.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="desktop/macos/Desktop/Sources/ProactiveAssistants/Core/JITProactivityDelivery.swift">

<violation number="1" location="desktop/macos/Desktop/Sources/ProactiveAssistants/Core/JITProactivityDelivery.swift:437">
P1: When the cutover generation advances for the same owner while this delivery is awaiting `completeDelivery`, the old authorization snapshot can remain current and this ambient card is still presented. Check the authoritative generation immediately before presentation, and drop the delivery when `execution.accountGeneration` is stale.</violation>
</file>

<file name="desktop/macos/Desktop/Sources/FloatingControlBar/Interject/InterjectSuggestionFeedbackStore.swift">

<violation number="1" location="desktop/macos/Desktop/Sources/FloatingControlBar/Interject/InterjectSuggestionFeedbackStore.swift:151">
P2: When a caller supplies an account-generation fence without an authorization snapshot, a generation change after the store write suppresses analytics but leaves the stale feedback row behind. Include the account-generation fence in the rollback condition so rejected mutations cannot remain in the canonical ledger.</violation>
</file>

<file name="desktop/macos/e2e/flows/floating-bar-functional.yaml">

<violation number="1" location="desktop/macos/e2e/flows/floating-bar-functional.yaml:47">
P2: This flow never exercises JITAmbientFeedback, so listing it in `covers:` claims coverage that doesn't exist. All steps (S1–S8) only assert the signed-in floating bar, open Ask Omi, send a stubbed typed turn, assert the stub marker echo, and close the bar. No step triggers an ambient JIT notification or taps the Useful/Not relevant actions routed by JITAmbientFeedbackActionRouter. Either remove the entry or add steps that drive an ambient card and its feedback actions.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

contextSummary: decision.reasoning, detail: execution.triggerID,
provenanceRef: deliveryID),
jitFeedbackContext: feedbackContext,
jitAmbientFeedbackContext: ambientFeedbackContext,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: When the cutover generation advances for the same owner while this delivery is awaiting completeDelivery, the old authorization snapshot can remain current and this ambient card is still presented. Check the authoritative generation immediately before presentation, and drop the delivery when execution.accountGeneration is stale.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At desktop/macos/Desktop/Sources/ProactiveAssistants/Core/JITProactivityDelivery.swift, line 437:

<comment>When the cutover generation advances for the same owner while this delivery is awaiting `completeDelivery`, the old authorization snapshot can remain current and this ambient card is still presented. Check the authoritative generation immediately before presentation, and drop the delivery when `execution.accountGeneration` is stale.</comment>

<file context>
@@ -400,6 +434,7 @@ actor JITProactivityDelivery {
             contextSummary: decision.reasoning, detail: execution.triggerID,
             provenanceRef: deliveryID),
           jitFeedbackContext: feedbackContext,
+          jitAmbientFeedbackContext: ambientFeedbackContext,
           onPresented: { [weak self] in
             Task {
</file context>

Comment thread desktop/macos/changelog/unreleased/20260905-jit-ambient-feedback.json Outdated
recordedAt: recordedAt
)
)
if !didEmitAnalytics, authorizationSnapshot != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: When a caller supplies an account-generation fence without an authorization snapshot, a generation change after the store write suppresses analytics but leaves the stale feedback row behind. Include the account-generation fence in the rollback condition so rejected mutations cannot remain in the canonical ledger.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At desktop/macos/Desktop/Sources/FloatingControlBar/Interject/InterjectSuggestionFeedbackStore.swift, line 151:

<comment>When a caller supplies an account-generation fence without an authorization snapshot, a generation change after the store write suppresses analytics but leaves the stale feedback row behind. Include the account-generation fence in the rollback condition so rejected mutations cannot remain in the canonical ledger.</comment>

<file context>
@@ -58,26 +100,61 @@ enum InterjectSuggestionFeedbackMutation {
-        recordedAt: recordedAt
-      )
-    )
+    if !didEmitAnalytics, authorizationSnapshot != nil {
+      // Do not leave a stale owner row behind when the transition won the
+      // race after the actor write but before the telemetry seam.
</file context>
Suggested change
if !didEmitAnalytics, authorizationSnapshot != nil {
if !didEmitAnalytics, authorizationSnapshot != nil || accountGeneration != nil {

- desktop/macos/Desktop/Sources/FloatingControlBar/Interject/InterjectReplyWindow.swift
- desktop/macos/Desktop/Sources/FloatingControlBar/Interject/InterjectSuggestionFeedbackStore.swift
- desktop/macos/Desktop/Sources/FloatingControlBar/Interject/InterjectVoiceFeedbackRouting.swift
- desktop/macos/Desktop/Sources/ProactiveAssistants/Core/JITAmbientFeedback.swift

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: This flow never exercises JITAmbientFeedback, so listing it in covers: claims coverage that doesn't exist. All steps (S1–S8) only assert the signed-in floating bar, open Ask Omi, send a stubbed typed turn, assert the stub marker echo, and close the bar. No step triggers an ambient JIT notification or taps the Useful/Not relevant actions routed by JITAmbientFeedbackActionRouter. Either remove the entry or add steps that drive an ambient card and its feedback actions.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At desktop/macos/e2e/flows/floating-bar-functional.yaml, line 47:

<comment>This flow never exercises JITAmbientFeedback, so listing it in `covers:` claims coverage that doesn't exist. All steps (S1–S8) only assert the signed-in floating bar, open Ask Omi, send a stubbed typed turn, assert the stub marker echo, and close the bar. No step triggers an ambient JIT notification or taps the Useful/Not relevant actions routed by JITAmbientFeedbackActionRouter. Either remove the entry or add steps that drive an ambient card and its feedback actions.</comment>

<file context>
@@ -44,6 +44,7 @@ covers:
   - desktop/macos/Desktop/Sources/FloatingControlBar/Interject/InterjectReplyWindow.swift
   - desktop/macos/Desktop/Sources/FloatingControlBar/Interject/InterjectSuggestionFeedbackStore.swift
   - desktop/macos/Desktop/Sources/FloatingControlBar/Interject/InterjectVoiceFeedbackRouting.swift
+  - desktop/macos/Desktop/Sources/ProactiveAssistants/Core/JITAmbientFeedback.swift
   - desktop/macos/Desktop/Sources/FloatingControlBar/PTTContextVocabularyProvider.swift
   - desktop/macos/Desktop/Sources/FloatingControlBar/PushToTalkManager.swift
</file context>

Comment thread desktop/macos/Desktop/Sources/ProactiveAssistants/Core/JITAmbientFeedback.swift Outdated
Add generation checks at direct, async, and queued notification presentation seams and derive ambient feedback identities from delivery provenance. Make audio permission setup deterministic and derive the streaming drift bound from chat typography.

Focused tests: JITAmbientFeedbackTests (11), stale-generation presentation (1), ChatTranscriptGestureHarnessTests (1), combined Chat/FailLoud/Floating tests (55).

Failure-Class: none
@Git-on-my-level
Git-on-my-level merged commit c4f98ec into main Sep 6, 2026
27 checks passed
@Git-on-my-level
Git-on-my-level deleted the codex/jit-ambient-feedback-20260905 branch September 6, 2026 00:38
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