Repository navigation
New-harness detection and setup nudges (surfaces 1–2) - #626
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Detects, after initial setup, that a supported coding harness is installed but kcap is not wired into it, and prompts to set it up. Implements the spec's PR 1 (independently shippable client surfaces); surfaces 3–4 (daemon/ingest inventory → server notification, and the server-side machine-went-quiet backstop) follow in PRs 2–3. Core substrate: - HarnessCatalog: single Core table binding vendor id + label + install flag + detection selector; the App's AgentVendors re-derives from it, and a conformance test pins it against VendorSelection.KnownVendorFlags. - HarnessIntegrationProbe.IsWired: "is kcap wired into vendor X?", the single source of truth now shared by the kcap status Hooks line (Claude's enabled-plugin and Codex's hooks-reference checks moved to Core; status delegates to them, behavior-preserving). - HarnessOfferLedger/Store: per-machine harness-offers-v1.json (atomic write, corrupt→empty) plus the shared 6h evaluation throttle stamp. - HarnessNudge.Nudgeable: pure predicate (detected ∧ !wired ∧ !declined ∧ past the 7-day re-offer floor). Surfaces: - 1: HarnessNudgeEmitter fragment wired into all 9 hook commands' SessionStart additionalContext, next to the work-items nudge. - 2: HarnessSetupNotice — exit-time interactive stderr notice (TTY-gated, human-facing commands only), mirroring the update-available notice and sharing surface 1's throttle. - kcap status: passive "installed but kcap not configured" line (ledger- independent — always tells the truth). - kcap harness list|dismiss|reset: inspect/silence/re-enable the nudges; all bypass the throttle. - kcap setup stamps offered harnesses so it never re-nudges a vendor just seen at setup; never writes/overwrites a dismissal. - Profile.DisableHarnessNudge (config key disable_harness_nudge) opts out surfaces 1–2 entirely. Tests: catalog conformance, predicate per-vendor, ledger/store (incl. dismissal-preservation), IsWired, emitter (fragment/notice/throttle/fold/ opt-out/exception→null). README + help + config help updated. Closes #625 AI-2118 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PR Summary by QodoDetect unwired coding harnesses and nudge users to configure them
AI Description
Diagram
High-Level Assessment
Files changed (32)
|
Code Review by Qodo
1.
|
…ness, catalog delegate - Offer ledger updates now serialize across processes via ConfigFileLock and write through a per-process-unique temp file, so a concurrent hook/setup/ command can no longer overwrite another's change (notably lose a dismissal). (qodo #4) - Load normalizes a null `vendors` member to an empty dictionary, honoring the corrupt-to-empty contract so management commands can't null-deref. (qodo #5) - Claude/Codex wired-checks moved into their own installers (ClaudePluginInstaller.IsPluginEnabled / CodexHooksInstaller.ReferencesKcapHook) and read via a new Core SharedFileText (FileShare.ReadWrite), so probing never blocks an agent writing its own settings.json/hooks.json on Windows. (qodo #2) - Wired-check folded into a per-entry delegate on HarnessCatalog (the single Core registration site); HarnessIntegrationProbe is now thin generic dispatch, no per-vendor switch in shared code. (qodo #1) - `harness` added to Program.cs offline-command allowlist, so `kcap harness list|dismiss|reset` works with no server configured. (qodo #7) - Trimmed predicate comments that restated the code. (qodo #3) - Added null-vendors ledger test. (qodo #6 — "setup stamping fails compilation" — is a false positive: `detected` is CodingAgentsStep.DetectedAgents whose members are bool, and CI's build passed.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…osed throttle, honest dismiss, snapshot-consistent wiring - Offer-ledger Update takes a lock timeout; the hook/best-effort StampOffered path uses a short 1s wait so a contended ledger can't stall a SessionStart hook past a host's ~5s budget (management commands keep the 5s wait). (Codex P1 #1) - SharedFileText opens with FileShare.Delete too, so a concurrent read can't make the ledger's atomic File.Move-overwrite fail with a sharing violation on Windows (which would silently drop a dismissal). (Codex P1 #2) - TryClaimCheck now fails CLOSED: if the throttle stamp can't be written the same failure blocks the ledger stamp, so returning true would nudge on every hook — suppress instead. (Codex P1 #3) - Update returns whether the change persisted; `kcap harness dismiss|reset` surface a false as exit 1 instead of falsely reporting success (a lost dismissal would otherwise silently revive the nudge). (Codex P1 #4) - Kiro/Pi/OpenCode wired-probes now thread the injected KiroHome/PiAgentDir/ OpenCodeConfigDir overrides (new optional params on the *Paths helpers) so detection and wiring consume the same AgentDetectionInputs snapshot, per the Core API contract. (Codex P2 #5) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ck, pure snapshot wiring - On lock-acquire failure Update no longer does a lockless read-modify-write (which could recreate the lost-dismissal race); it returns false without mutating. Hook callers ignore it (best-effort); dismiss/reset already report false as exit 1. (Codex R2 P1 #1) - Hook-path stamping uses a zero-wait (non-blocking) lock try, so a SessionStart hook spends none of its exit-budget safety reserve waiting on the mutex; on contention it simply skips stamping. (Codex R2 P1 #2) - Kiro/Pi/OpenCode wired-probes resolve paths via new pure helpers (KcapAgentJsonPure / KcapExtensionPure / KcapPluginPure) built from the existing *Pure roots, so a null injected override means "unset → home default" and never re-reads ambient env — honoring AgentDetectionInputs' null-as-unset contract and keeping detection and wiring on the same snapshot. Reverted the round-1 non-pure override params (superseded). Added a Kiro override-threading test. (Codex R2 P2 #3) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- StampOffered takes a lockTimeout; the hook path passes TimeSpan.Zero while setup omits it and gets the normal serialized 5s wait, so a momentary concurrent writer no longer makes setup silently skip stamping the offered vendors (which would drop the 7-day floor). (Codex R3 P2 #1) - Copilot/Gemini/Antigravity wired-probes now use pure path helpers (KcapHooksJsonPure / SettingsJsonPure / GlobalHooksJsonPure) built from the existing *Pure roots, so every override the snapshot carries is resolved without an ambient env re-read. Claude/Codex necessarily read CLAUDE_CONFIG_DIR/CODEX_HOME from ambient because those roots aren't part of AgentDetectionInputs (detection is PATH-only for them) — documented as the sole, production-coincident exception on HarnessCatalog. (Codex R3 P2 #2) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GlobalHooksJsonPure built from RootPure (<gemini>/antigravity, the data root), but production GlobalHooksJson resolves through GuiConfigRoot (<gemini>/config), so a normally-wired Antigravity install was probed at the wrong path and reported unwired — spurious nudges while `kcap status` said configured. Add GuiConfigRootPure (<gemini>/config) and build the pure hooks path from it, mirroring production. Add HarnessWiredPathParityTests pinning every pure wiring path to its production layout so a wrong root/segment fails a test. (Codex R4 P1) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rness-detection-c9f55a # Conflicts: # src/Capacitor.Cli.Core/Harness/Kiro/KiroPaths.cs
Detects, after initial
kcap setup, that a supported coding harness (Claude, Codex, Cursor, Copilot, Gemini, Kiro, Pi, OpenCode, Antigravity) is installed but kcap is not wired into it, and prompts the user to set it up — so a harness installed later (or switched to) starts being recorded without a manual re-run of setup.This is PR 1 of the spec's 3-PR slicing (the independently-shippable client surfaces). Surface 3 (daemon/hook-ingest harness inventory → Capacitor UI notification) and surface 4 (server-side machine-went-quiet backstop) follow in PR 2 (kcap-cli client fragment) and PR 3 (kcap-server). Design spec rides this PR:
docs/superpowers/specs/2026-08-19-new-harness-detection-nudge-design.md(reviewed clean by Copilot + Kiro spec-review flows).What's here
Core substrate
HarnessCatalog— the single Core table binding vendor id + label + install flag + detection selector. The desktop app'sAgentVendorsnow re-derives from it, and a conformance test pins it againstVendorSelection.KnownVendorFlagsso a tenth harness fails a test rather than silently missing every surface.HarnessIntegrationProbe.IsWired(vendorId, AgentDetectionInputs)— "is kcap wired into vendor X?", callable from CLI and (later) daemon. To avoidkcap statusever contradicting the nudge, Claude's enabled-plugin check and Codex's hooks-reference check moved to Core as the shared source of truth;StatusCommanddelegates to them (behavior-preserving).HarnessOfferLedger/HarnessOfferStore— per-machine~/.config/kcap/harness-offers-v1.json(atomic write, corrupt→empty) plus the shared 6-hour evaluation throttle stamp.HarnessNudge.Nudgeable— pure predicate: detected ∧ not wired ∧ not declined ∧ past the 7-day re-offer floor.Surfaces
HarnessNudgeEmitterfragment wired into all 9 hook commands next to the work-items nudge; tells the agent to offerkcap plugin install --<vendor>.HarnessSetupNotice, an exit-time one-liner (TTY-gated, human-facing commands only), mirroring the "update available" notice and sharing surface 1's throttle.kcap statusgains a passive "‹Vendor› installed but kcap not configured — run…" line (ledger-independent: status always tells the truth, even for dismissed vendors).kcap harness list | dismiss | resetto inspect / silence / re-enable the nudges (all bypass the throttle).kcap setupstamps the harnesses it offered, so the nudge never re-offers a vendor just seen at setup; it never writes or overwrites a dismissal.Profile.DisableHarnessNudge(kcap config set disable_harness_nudge true) turns off surfaces 1–2 entirely.Testing
IsWired; emitter (fragment / notice / throttle / multi-vendor fold / opt-out / exception→null).Harness*, CLIHarness.*namespace,SessionStartMemory, App Agents step). TheCommands.HarnessCursor tests that read the real~/.cursorare the pre-existing local env-coupling (they fail identically onmainon a machine with kcap set up for Cursor); under an isolated HOME they pass 42/42, matching CI.README,
help-usage.txt, andconfig sethelp updated in this PR.Closes #625
AI-2118
🤖 Generated with Claude Code