Skip to content

Gate Antigravity nudges on the invocation counter - #669

Open
Coldaine wants to merge 1 commit into
kurrent-io:mainfrom
MooseGooseConsulting:fix/antigravity-nudge-repeat
Open

Gate Antigravity nudges on the invocation counter#669
Coldaine wants to merge 1 commit into
kurrent-io:mainfrom
MooseGooseConsulting:fix/antigravity-nudge-repeat

Conversation

@Coldaine

Copy link
Copy Markdown

Closes #668 — Linear id omitted: the issue was filed minutes ago and has not been imported yet.

What & why

Antigravity's PreInvocation is a per-turn callback (CallbackMayRepeat: true). The memory index is fenced against that by the once-per-conversation lease; the work-items and harness nudges are not — they are resolved at the output layer, after the lease is decided, as pure functions of the session id. Every turn therefore wrote another injectSteps userMessage, which Antigravity persists for the conversation. This gates both on invocationNum, the one payload field that separates the callbacks. An absent or non-numeric counter reads as first, so a payload change degrades to emitting rather than to silence.

Where to look

The gate lives at the hook's output site, not inside the emitters, so the lease lanes keep their current acquire/complete/retry behaviour untouched.

Kiro, OpenCode and Pi also declare CallbackMayRepeat: true and resolve the nudge unconditionally. They are left alone here — each would need its own counter or marker — and #668 records that.

Verification

Same conversation id, three invocations, before and after (stdout bytes):

invocationNum 0.11.33 this branch
1 929 929
2 929 0
3 929 0

AntigravitySessionStartMemoryTests: 22/22 pass. Release publish is clean of IL2026/IL3050.

PreInvocation fires once per turn and the nudges carry no lease, so every
turn appended another persistent injectSteps userMessage. The vendor's
invocationNum is the only payload field that separates the callbacks.
Copilot AI lite review requested due to automatic review settings August 25, 2026 22:08
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copilot AI 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.

Pull request overview

This PR prevents Antigravity’s repeating PreInvocation hook from re-injecting persistent “nudge” userMessage steps on every turn by gating nudge emission on the vendor-provided invocationNum counter (emit only on the first invocation; fail open when the counter is absent/non-numeric).

Changes:

  • Gate Antigravity work-items + harness nudges behind IsFirstInvocation(payload) so only the first callback in a conversation emits nudges.
  • Add IsFirstInvocation(JsonObject payload) helper that treats missing/non-numeric (and <= 1) as first invocation.
  • Add unit tests pinning the first vs later invocation behavior and the fail-open semantics.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
test/Capacitor.Cli.Tests.Unit/Harness/Antigravity/AntigravitySessionStartMemoryTests.cs Adds tests for the new “first invocation only” nudge gating behavior and fail-open handling.
src/Capacitor.Cli/Commands/Harness/AntigravityHookCommand.cs Implements nudge gating via invocationNum and introduces IsFirstInvocation helper.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +318 to +321
/// Whether this PreInvocation is the conversation's first, read from the vendor's own
/// <c>invocationNum</c> counter — the one payload field that varies between callbacks. An absent
/// or non-numeric value reads as first: a payload without the counter cannot distinguish
/// callbacks, and emitting once too often beats never emitting.
Comment on lines +260 to +265
// Fail-open: a payload that cannot distinguish callbacks must still emit.
[Test]
public async Task A_missing_or_non_numeric_counter_reads_as_the_first_invocation() {
await Assert.That(AntigravityHookCommand.IsFirstInvocation(Payload("{}"))).IsTrue();
await Assert.That(AntigravityHookCommand.IsFirstInvocation(Payload("""{"invocationNum":"2"}"""))).IsTrue();
await Assert.That(AntigravityHookCommand.IsFirstInvocation(Payload("""{"invocationNum":0}"""))).IsTrue();

@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: 2598b6c782

ℹ️ 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 on lines +212 to +215
var workItemsNudge = IsFirstInvocation(payload)
? HarnessNudgeEmitter.Combine(
WorkItemsNudgeEmitter.Resolve(SessionStartHarness.Antigravity, sessionId, activeProfile?.DisableWorkItemsNudge is true),
HarnessNudgeEmitter.ResolveFragmentForHook(activeProfile?.DisableHarnessNudge is true, config))

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 Badge Keep the throttled harness nudge outside the turn gate

In a long-running Antigravity conversation, this gate prevents HarnessNudgeEmitter.ResolveFragmentForHook from ever checking again after turn 1, so an agent installed later—or one missed because the first check fell within the shared six-hour throttle—will not be offered until the user starts another conversation or invokes another surface. Unlike the work-items nudge, the harness nudge is already stateful: TryClaimCheck throttles evaluation for six hours and StampOffered enforces the per-vendor reoffer floor, so it cannot be injected on every turn and should remain outside this first-invocation gate.

Useful? React with 👍 / 👎.

@realtonyyoung

Copy link
Copy Markdown
Collaborator

Hi @Coldaine , thanks for the PR! May you please sign the CLA and we're happy to accept the pull: https://cla-assistant.io/kurrent-io/kcap-cli?pullRequest=669

@alexeyzimarev

Copy link
Copy Markdown
Member

@Coldaine thanks! Do you ming accepting the CLA?

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.

Antigravity re-injects the work-items nudge on every turn

5 participants