Skip to content

Gate Kiro nudges behind a durable once-per-session claim - #697

Merged
realtonyyoung merged 2 commits into
mainfrom
ai-2240-counter-doc-followup
Aug 29, 2026
Merged

realtonyyoung merged 2 commits into
mainfrom
ai-2240-counter-doc-followup

Conversation

@realtonyyoung

Copy link
Copy Markdown
Collaborator

AI-2354 — no GitHub-issue half: the Kiro repeat is tracked in Linear only.

What & why

Kiro's agentSpawn fires on every prompt and appends hook stdout straight into agent context, yet the work-items and harness-setup nudges are resolved unconditionally — the accumulation #668 fixed for Antigravity, whose vendor counter Kiro's payload lacks. The gate is a durable once-per-session claim in the SessionStart memory store under a nudge-scoped key domain, so it survives per-prompt hook processes and is swept with the store's other records. Also aligns the Antigravity counter doc and pinning test with the actual <= 1 semantics.

Where to look

The claim starts beside the memory fetch and is awaited only at the output site, so a wedged store cannot delay the fragment write. Its failure direction is refuse-to-emit — the opposite of Antigravity's counter fallback — because the store, unlike a vendor payload field, also guards every later turn.

Verification

  • NudgeLeaseTests + the foundation/Kiro/Antigravity memory classes: 65/65 pass.
  • dotnet publish -c Release: no IL2026/IL3050.

🤖 Generated with Claude Code

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Aug 29, 2026

Copy link
Copy Markdown

AI-2240

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Gate Kiro nudges with a durable once-per-session claim

🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Persist a Kiro claim so repeated agentSpawn hooks emit nudges only once.
• Isolate nudge and memory leases with domain-separated session keys.
• Cover claim durability, canonicalization, collision avoidance, and Antigravity counter semantics.
Diagram

graph TD
  A["Kiro agentSpawn"] --> B["Memory fetch"] --> E{"Claim won?"} -->|Yes| F["Resolve nudges"] --> G["Write stdout"]
  A --> C["Nudge claim"] --> D["Lease store"] --> E
  E -->|No| G
Loading
High-Level Assessment

Reusing the SessionStart memory lease store with a domain-separated key is the best fit: it provides cross-process durability, existing locking and sweeping, and no new persistence subsystem. In-memory flags would not survive per-prompt hook processes, while a dedicated store would duplicate lifecycle and cleanup logic. Starting the claim beside the memory fetch also preserves the stdout write budget.

Files changed (6) +130 / -8

Bug fix (3) +65 / -3
KiroHookCommand.csGate Kiro nudges with a bounded durable claim +17/-3

Gate Kiro nudges with a bounded durable claim

• Starts a 750 ms nudge claim concurrently with memory retrieval and emits work-item and harness nudges only when that claim succeeds. Store failures, invalid sessions, and repeated prompts suppress nudges without delaying the memory-fragment write path beyond the bounded claim.

src/Capacitor.Cli/Commands/Harness/KiroHookCommand.cs

NudgeLease.csAdd a fail-closed once-per-session nudge lease +33/-0

Add a fail-closed once-per-session nudge lease

• Adds a helper that begins and immediately completes a SessionStart store record within one shared budget. It returns false for repeat claims, invalid identities, expired budgets, or operational failures so persistent context is not reinjected each turn.

src/Capacitor.Cli/NudgeLease.cs

SessionStartMemoryIdentity.csCreate domain-separated keys for nudge claims +15/-0

Create domain-separated keys for nudge claims

• Adds a nudge-specific SHA-256 identity using the existing harness and normalized session inputs under a distinct domain byte. This prevents nudge claims from colliding with or consuming the session memory lease.

src/Capacitor.Cli/SessionStartMemory/SessionStartMemoryIdentity.cs

Tests (2) +62 / -2
AntigravitySessionStartMemoryTests.csAlign Antigravity counter test naming and documentation +3/-2

Align Antigravity counter test naming and documentation

• Renames the fallback-counter test to cover all unusable values and documents non-positive counters explicitly. Existing assertions verify missing, non-numeric, and zero values remain first invocations.

test/Capacitor.Cli.Tests.Unit/Harness/Antigravity/AntigravitySessionStartMemoryTests.cs

NudgeLeaseTests.csVerify durable nudge claim isolation and normalization +59/-0

Verify durable nudge claim isolation and normalization

• Adds tests for first-writer success, repeat refusal, independent sessions, GUID canonicalization, memory-key isolation, and invalid session rejection. These cases validate both once-per-session behavior and the fail-closed policy.

test/Capacitor.Cli.Tests.Unit/SessionStartMemory/NudgeLeaseTests.cs

Documentation (1) +3 / -3
AntigravityHookCommand.csClarify Antigravity's usable first-invocation counter semantics +3/-3

Clarify Antigravity's usable first-invocation counter semantics

• Documents that absent, non-numeric, and non-positive invocation counters are treated as the first callback. This aligns the comment with the existing 'invocation <= 1' behavior.

src/Capacitor.Cli/Commands/Harness/AntigravityHookCommand.cs

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (2) 📜 Skill insights (0)

Grey Divider


Action required

1. NudgeLeaseTests directory mismatch 📘 Rule violation ⚙ Maintainability
Description
The new tests are under SessionStartMemory/, but the production type they test is at the project
root. This breaks the required mirrored production/test directory organization.
Code

test/Capacitor.Cli.Tests.Unit/SessionStartMemory/NudgeLeaseTests.cs[4]

+namespace Capacitor.Cli.Tests.Unit.SessionStartMemory;
Evidence
Rule 13 requires tests to mirror production directories. The added test declares a
SessionStartMemory namespace and resides in that directory, while the tested NudgeLease type is
newly added at src/Capacitor.Cli/NudgeLease.cs in the project root.

CLAUDE.md: Test Projects Must Mirror Production Boundaries and Share Utilities Through Helpers
test/Capacitor.Cli.Tests.Unit/SessionStartMemory/NudgeLeaseTests.cs[4-12]
src/Capacitor.Cli/NudgeLease.cs[5-18]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`NudgeLeaseTests` is placed in the `SessionStartMemory` test directory even though `NudgeLease.cs` is located at the production project root.

## Issue Context
PR Compliance ID 13 requires test directories to mirror production directories. Move the test to the unit-test project root and align its namespace, or move the production type into `SessionStartMemory` and align its namespace if that is the intended production boundary.

## Fix Focus Areas
- test/Capacitor.Cli.Tests.Unit/SessionStartMemory/NudgeLeaseTests.cs[1-12]
- src/Capacitor.Cli/NudgeLease.cs[1-16]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. NudgeLease documentation is overlong 📘 Rule violation ⚙ Maintainability
Description
The new type summary spans eight lines and narrates implementation details that are already evident
from the method body. It can state the durable fail-closed gating constraint much more directly.
Code

src/Capacitor.Cli/NudgeLease.cs[R7-10]

+/// <summary>
+/// Durable once-per-session gate for the SessionStart nudges on harnesses whose start callback
+/// repeats per prompt with no vendor-side counter to key on. Claims and immediately completes a
+/// record in the shared SessionStart memory store under a nudge-scoped key, so the decision
Evidence
Rule 24 requires comments to focus on non-obvious constraints and identifies unnecessarily long or
code-restating comments as failures. The added summary explains record creation, key scope, process
survival, sweeping, and exception behavior across eight lines, although those mechanics are directly
represented by the implementation below.

CLAUDE.md: Comments Must Document Only Current, Non-Obvious Constraints
src/Capacitor.Cli/NudgeLease.cs[7-15]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The `NudgeLease` XML summary is unnecessarily long and restates mechanics visible in the implementation.

## Issue Context
PR Compliance ID 24 requires comments to be scarce, durable, and normally one or two lines while documenting only non-obvious constraints. Preserve the enduring once-per-session and fail-closed behavior, but remove the implementation walkthrough.

## Fix Focus Areas
- src/Capacitor.Cli/NudgeLease.cs[7-15]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Store creation bypasses fallback 🐞 Bug ☼ Reliability
Description
SessionStartMemoryLeaseStore.Create is evaluated before TryClaimAsync enters its exception
handler, so an inaccessible or rejected cache directory throws out of HandleAgentSpawn instead of
merely suppressing the nudge. This can abort the Kiro hook before its memory fragment is written and
before lifecycle processing completes.
Code

src/Capacitor.Cli/Commands/Harness/KiroHookCommand.cs[R248-250]

+        var nudgeClaim = NudgeLease.TryClaimAsync(
+            SessionStartMemoryLeaseStore.Create(config, clock.Time),
+            HarnessId.Kiro, sessionId, NudgeClaimBudget);
Evidence
The new call constructs the store at the caller before entering TryClaimAsync; store construction
performs filesystem operations that can throw. The neighboring memory path catches the same
construction failures, proving hook execution is expected to degrade safely rather than abort.

src/Capacitor.Cli/Commands/Harness/KiroHookCommand.cs[102-117]
src/Capacitor.Cli/Commands/Harness/KiroHookCommand.cs[248-250]
src/Capacitor.Cli/NudgeLease.cs[17-30]
src/Capacitor.Cli/SessionStartMemory/SessionStartMemoryStorePaths.cs[8-18]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The nudge store is constructed while evaluating the `TryClaimAsync` arguments, outside that method's `try` block. Filesystem failures from store construction therefore abort the Kiro hook instead of producing the intended refuse-to-emit result.

## Issue Context
The existing memory-index path explicitly catches store/provider construction failures and returns no fragment. The nudge path must provide the same best-effort behavior so a nudge coordination failure cannot prevent other hook output.

## Fix Focus Areas
- src/Capacitor.Cli/Commands/Harness/KiroHookCommand.cs[248-250]
- src/Capacitor.Cli/NudgeLease.cs[17-30]
- src/Capacitor.Cli/SessionStartMemory/SessionStartMemoryStorePaths.cs[8-18]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View high (1)
4. Claim can exceed hook budget 🐞 Bug ☼ Reliability
Description
The claim always receives a fresh 750 ms budget and is awaited directly before stdout, even when the
5-second hook has little or no work budget remaining. If earlier repository work consumes most of
the ceiling, this added wait can let Kiro kill the process before it accepts the otherwise-ready
memory fragment.
Code

src/Capacitor.Cli/Commands/Harness/KiroHookCommand.cs[R248-250]

+        var nudgeClaim = NudgeLease.TryClaimAsync(
+            SessionStartMemoryLeaseStore.Create(config, clock.Time),
+            HarnessId.Kiro, sessionId, NudgeClaimBudget);
Evidence
The hook creates a five-second shared budget, and Remaining shrinks with elapsed time while
retaining a 1.5-second safety reserve. The new claim ignores that value, yet output is delayed until
the claim finishes; Kiro only consumes stdout from a hook that completes before its timeout.

src/Capacitor.Cli/Commands/Harness/KiroHookCommand.cs[177-178]
src/Capacitor.Cli/Commands/Harness/KiroHookCommand.cs[235-250]
src/Capacitor.Cli/Commands/Harness/KiroHookCommand.cs[264-280]
src/Capacitor.Cli/Commands/HookBudget.cs[14-23]
src/Capacitor.Cli/NudgeLease.cs[21-28]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The nudge claim uses an independent 750 ms timeout rather than the current hook remainder. Awaiting that task before writing stdout can overrun Kiro's hard hook ceiling and discard all output.

## Issue Context
`HookBudget.Remaining` reserves time for output, spooling, and exit. The memory fetch already uses that shared budget, while the newly added claim can continue consuming time after the shared work budget reaches zero.

## Fix Focus Areas
- src/Capacitor.Cli/Commands/Harness/KiroHookCommand.cs[47-50]
- src/Capacitor.Cli/Commands/Harness/KiroHookCommand.cs[248-250]
- src/Capacitor.Cli/Commands/Harness/KiroHookCommand.cs[269-276]
- src/Capacitor.Cli/Commands/HookBudget.cs[14-23]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: ⚖️ Balanced: This changes runtime hook behavior and durable session-store coordination across multiple code paths; it carries meaningful correctness and regression risk, but the logic is cohesive enough for one careful review rather than redundant passes.

Grey Divider

Tip of the day
💡 Did you know, you can group findings by type and pick your Finding display, from Minimal to Full

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread test/Capacitor.Cli.Tests.Unit/SessionStartMemory/NudgeLeaseTests.cs Outdated
Comment thread src/Capacitor.Cli/NudgeLease.cs Outdated
Comment thread src/Capacitor.Cli/Commands/Harness/KiroHookCommand.cs Outdated
Comment thread src/Capacitor.Cli/Commands/Harness/KiroHookCommand.cs Outdated
@realtonyyoung
realtonyyoung force-pushed the ai-2240-counter-doc-followup branch 2 times, most recently from 81f6d47 to eae3092 Compare August 29, 2026 15:04
agentSpawn carries no per-conversation counter, so the claim rides the
SessionStart memory store under its own key domain and is swept with it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@realtonyyoung
realtonyyoung force-pushed the ai-2240-counter-doc-followup branch from eae3092 to 656ca98 Compare August 29, 2026 15:09
@realtonyyoung
realtonyyoung merged commit 53be3b7 into main Aug 29, 2026
10 of 11 checks passed
@realtonyyoung
realtonyyoung deleted the ai-2240-counter-doc-followup branch August 29, 2026 15:41
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