[AI-1530] Ship the Capacitor guided-tour skill and point setup at it#369
[AI-1530] Ship the Capacitor guided-tour skill and point setup at it#369stktung wants to merge 29 commits into
Conversation
A user who finishes `kcap setup` is recording and has no idea what that buys them. Every skill we ship today assumes the user already knows what Capacitor does and which surface they want. Adds `kcap/skills/guided-tour/SKILL.md` — the onboarding path: the team's recorded sessions, spend and errors, then per-use-case tutorial tours (evals, session recall, PR review, analytics), plus the missing-pieces paths (offer to install kcap, offer to import history). Skill content is verbatim from the trialled source; the only edits are the short frontmatter name (matching `recap`/`errors`) and scoping the "empty or inert on day one" claim to a fresh workspace. Registered in AgentsSkillsInstaller.SourceNames so it reaches Codex, Cursor, Kiro and Antigravity, not just Claude Code. Setup now closes with a call-to-action panel whose wording is pinned by a test. Also derives CodingAgentsStep's installed-skills line from SourceNames — the hand-maintained copy had already drifted (printed `review-flows` without its `kcap-` prefix). Closes #368 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| /// with the skill's actual invocation name. | ||
| /// </summary> | ||
| internal const string GuidedTourCallToAction = | ||
| "New here? Run /kcap:guided-tour in your agent for a guided tour of what got recorded."; |
There was a problem hiding this comment.
- Let's change the CTA to 'Prompt "Start kcap guided tour" in your agent for a guided tour of Capacitor'
There was a problem hiding this comment.
Done in e0ffee4 — the CTA is now exactly:
Prompt "Start kcap guided tour" in your agent for a guided tour of Capacitor
Verified live: kcap setup closes with that panel.
Agreed it's the better call, and it changed the contract in a way worth flagging. A slash command is vendor-specific — /kcap:guided-tour only resolves through the Claude Code plugin, while under ~/.agents/skills the skill is kcap-guided-tour. A prompt works everywhere.
But that also means the phrase we tell users to type has to be something the skill actually advertises, and it wasn't. So I added "Start kcap guided tour" to the skill's frontmatter trigger list, and a test (GuidedTourCallToAction_prompt_is_a_trigger_in_the_skill_description) that reads kcap/skills/guided-tour/SKILL.md and fails if the CTA and the skill's triggers ever drift apart.
That is an edit to a file the brief called final — I judged it in scope because it's the frontmatter (already being adapted for name:), not the tour body. Happy to pull it back out if you'd rather the description stay untouched, but then the only discovery path we ship rests on the agent inferring the trigger.
PluginCommandCodexTests.SourceNames_contains_expected_skills asserts the installer's list explicitly, so adding a skill has to be a conscious edit there too. Keeping that deliberate — it's the intended gate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Per review on #369: the setup call-to-action is now `Prompt "Start kcap guided tour" in your agent for a guided tour of Capacitor`. A natural-language prompt travels better than a slash command — the invocation differs per vendor (/kcap:guided-tour via the Claude Code plugin, kcap-guided-tour under ~/.agents/skills) while the prompt works everywhere. Adds the phrase to the skill's trigger list, and a test pinning the two together: if we tell users to type a phrase the skill doesn't advertise, the only discovery path we ship depends on the agent guessing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
/review |
Test + AOT resultsAOT publish — clean. Unit suite — 3954 passed / 9 skipped / 6 failed of 3969. No failure is in code this PR touches, and every test this PR adds or changes passes:
On the 6 failuresThey are a rotating set — the failing subset differs between full runs, which is the signature of load-sensitivity rather than a deterministic break:
Failure modes are timing budgets ( One caveat I could not fully close
So it is proven flaky on unmodified main — but the rate on this branch was higher, and I am not going to pretend I explained that away. Two things argue it is environmental: the branch rate climbed over the session (1/4 early → 4/6 late) while the machine accumulated 30 orphaned I left the orphaned |
Code Review by Qodo
1.
|
|
/review |
Qodo rule violation: CLAUDE.md asks for self-explanatory code over verbose comments, and 11 lines of commentary around a three-line print is the drift-prone narration that rule exists to prevent — it had already gone stale once when the CTA wording changed. Keeps the two non-obvious facts (prints unconditionally; wording is test-pinned) and drops the rest. The prompt-vs-slash-command rationale is enforced by GuidedTourCallToAction_prompt_is_a_trigger_in_the_skill_description, which is a stronger guarantee than a paragraph asking readers to keep two things in sync. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Code review by qodo was updated up to the latest commit e0ffee4 |
|
/review |
|
Code review by qodo was updated up to the latest commit bb5614f |
Qodo: the test claimed the setup CTA's prompt is among the skill's advertised triggers, but asserted Contains() over all of SKILL.md. Vendors match on the frontmatter `description:` only — the body is read after the skill fires — so the phrase could move below the fences, stop triggering anything, and the guard would stay green. Now slices the YAML frontmatter and asserts inside the description value. Verified by mutation: moving the phrase from the description into the body fails the test, where the old assertion passed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Qodo: `SUM(DISTINCT s.duration_min)` dedupes by duration VALUE across the whole repo bucket, not by session, so two sessions sharing a byte-identical duration would collapse into one. Verified against a live tenant before and after: the shipped query is correct on current data — `v_an_sessions.duration_min` is `EXTRACT(EPOCH FROM (ended_at - started_at)) / 60.0`, a 16-decimal-place quotient, and the only repeated value among cost-bearing sessions is NULL, which SUM ignores. Running both variants in one query returned a 0.0 hour delta on every top repo. So this is a latent hazard, not a live defect: correctness rests on an unenforced property of a view in another repo. Round durations to whole minutes server-side and this silently starts eating sessions. The replacement removes the dependency rather than documenting it. `v_an_cost` is one row per (session, model), so pre-aggregating it to one row per session makes the join 1:1 — and both DISTINCT crutches become unnecessary and are deleted. Fewer moving parts than what it replaces. Both versions confirmed to return identical hours and identical total cost. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Qodo flagged Q-ERR as an unbounded query in the menu's 12s critical path. Measured: it returns 78 rows of (text, bigint) — not a payload that threatens the budget. Its two suggested bounds are both worse than the status quo. Bounding by error count is measurably wrong: Q-ERR is a lookup consulted after Q-COST has chosen the rows, and the two rank differently — one repo sits 3rd by cost but 7th by errors, so `ORDER BY errors DESC LIMIT 5` would drop it and the missing-repo rule would render 0 where the real count is 271. Fusing Q-ERR into Q-COST via a CTE is correct but serialises two calls beat 1 deliberately dispatches in parallel, trading a guaranteed round-trip against a 78-row concern. So: prune with `errors > 0`, which is free. Verified by running both variants in one query at one instant — 78 rows becomes 62, identical totals, and zero repos whose count changed. The 16 dropped rows are all-zero repos that the missing-repo rule already renders as 0, so the output is unchanged. The prose above the query is the part that lasts: nothing recorded WHY Q-ERR was unbounded, which is what let it read as an oversight. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ng demonstrable Session recall now precedes evals in the menu, the TODO list and the marker table. Recall works on day one with whatever the record already holds; evals need a body of scored sessions before they show anything. Leading with the use case that pays off immediately. Drops the standalone "Learn how to reduce token cost and hours wasted" line under the table — it promoted the evals tour from the position the eye lands on first, which is now the wrong pitch to lead with. Adds WHEN EVALS ARE NOT DEMONSTRABLE YET. Evals are a pipeline — score a session, accumulate facts across many, promote guidance, write it to CLAUDE.md — and only the first arrow fits inside a tour. On a fresh workspace the later stages are empty, and staging a curation demo on a record that cannot support one reads as a broken product. The skill now checks reach before promising (session_count from Q-DONE, plus `curate apply --dry-run`, which reports without writing) and falls back to onboarding: still run one real eval on their own session, read those scores, step through what the rest needs, then show a clearly labelled illustration of the end state. That illustration is the single sanctioned exception to "their data, always", so rule 3 now names it rather than contradicting it, and the Never list carves out --dry-run from the ban on `curate apply`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
/review |
|
Code review by qodo was updated up to the latest commit 98b9b9c |
… reference Each menu section now opens with one plain sentence saying what it does, replacing copy that varied in length and register between sections. Evals deliberately describes only the part that works on day one — an LLM-judge score of one session. The previous copy sold cross-session pattern-finding and curated repo guidance, which a fresh workspace cannot show; promising it on the first screen is what WHEN EVALS ARE NOT DEMONSTRABLE YET exists to avoid. That capability is now unadvertised in the menu and lives in the tour body instead. PR review says the sessions carry why the code was written that way, rather than claiming "discarded alternatives" — a transcript may not contain any, and the skill's honesty rules forbid promising what the data might not hold. Also fixes a contradiction Qodo caught in the new evals section: it said session_count is fetched in beat 1, but Q-DONE needs <user> from whoami and runs in beat 2. An agent following the wrong one could read the count before it exists. Now names beat 2 and states the dependency inline. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Codex review: setup printed the guided-tour CTA unconditionally, so a machine with no supported agent CLI got "No supported agent CLI detected" and "Prompt \"Start kcap guided tour\" in your agent" in the same output. Nothing was installed to answer that prompt. Codex suggested gating on a successful skill/plugin/MCP install. That would break the common case: the installers return false when work was skipped as already-current, so every re-run with skills in place would lose the CTA — exactly the users who can act on it. Gated on the two early returns instead, which are the only paths that install nothing at all. CodingAgentsStep owns that fact rather than SetupCommand re-deriving the condition, so the two can't drift. Verified live both ways: with an agent detected the panel prints; with every detection route defeated (empty PATH plus each vendor's relocation env var) the warning appears and the CTA does not. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
/review |
|
Code review by qodo was updated up to the latest commit 58ab24d |
Round-2 review found the previous gate wrong in both directions, so this replaces it rather than patching it. Codex: a Claude-only machine run with --skip-claude-hooks installs nothing, yet AgentSetupRan stayed true and the CTA printed. Codex also caught that adding a defaulted field to Result broke two existing equality assertions in CodingAgentsStepTests — I had run only SetupCommandTests and reported green, which is how that reached the branch. Qodo: the design spec still said the CTA prints unconditionally. That was true when written and stopped being true when the gate landed; the spec is now updated with the revision and why. Dropping AgentSetupRan/AgentSetupSkipped removes the broken assertions at the cause. The replacement asks two direct questions instead of threading install flags: is there an agent to type the prompt into, and is the skill on disk for one of them. Skill presence is read from the filesystem because the installers report false when work was skipped as already-current — a wired-up machine re-running setup, which must still get the CTA. The agent-detected half was missing from every earlier attempt: a machine carrying the skill but running no agent CLI has nothing to prompt. Neither reviewer raised it; it surfaced writing the scenarios out. Five tests cover the helper directly, replacing one that asserted against a hand-built record and would have passed through a real regression. Live-verified: no agent detected suppresses, agent plus skills present prints. Agent present with nothing on disk is unit-tested only — AgentsPaths.UserSkillsDir resolves from the real Windows profile regardless of a HOME override, so a scratch harness cannot isolate it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
/review |
|
Code review by qodo was updated up to the latest commit 7974a63 |
…st labelled Each of the ten welcome variants now asks the user to wait in so many words — hang tight, give it a few seconds, hold on — instead of implying it. The hook sentence of each variant is untouched; only the closing beat changed, phrasing varied so the ten stay distinct. Time promises stay at seconds/a moment, consistent with the ~12s menu budget. The table's cost column is headed "LLM cost (USD)" — the bare number needed its currency and its meaning on a first screen. The SQL alias stays cost_usd: the rendered header comes from the template line, and the "exactly as returned" rule governs values, not titles. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
/review |
|
Code review by qodo was updated up to the latest commit fc652e6 |
…wo minutes Every sample prompt reworked against three constraints from review of the rendered menu: Solo user. The old prompts assumed a team — "has anyone hit this before?" typed cold answers "nobody else" on a one-person record, and "what did we spend" presumes a we. All eight prompts now pay off on the user's own record alone, and a new rule in Executing the menu makes the skill assume the user works alone when composing its own Prompt suggestions, unless their data has already shown other authors. Trigger keywords. Menu prompts get retyped in fresh sessions (the marker scheme depends on it), so each now carries the vocabulary the target skill advertises: "my last session" / "left off" for recap, "show ... tool errors" for errors, "review PR" and "why was ... implemented this way" for kcap-review, "spend"/"cost" for analytics. The old "Recall the last 3 times I asked why" needed a stopword workaround section to be answerable at all — a demo requiring a workaround is the wrong demo; the section is deleted with it. Bounded turns. No menu prompt may trigger a turn over ~2 minutes: the eval handler now asks for a go before the 1-3 minute judge run and backgrounds it where the harness allows; the menu-context PR review is the bounded summary-plus-reasoning form, ending with an offered prompt for the full deep review. Session recall also gains an action prompt — "Pick up where my last session left off" resumes work rather than reporting on it, which is the strongest first-screen proof that the record is working memory rather than an archive. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
/review |
|
Code review by qodo was updated up to the latest commit f463121 |
Qodo, two findings on the prompt rework.
Variant 9's hook was an analogy ("warming up the memory banks") under a
voice section that says no analogies, and variant 10 claimed "cheaper
sessions" — the savings claim the honesty rules explicitly say nothing
backs yet. Both emitted verbatim on a random draw, so the contradiction
shipped intermittently. Reworded to what the record does support.
Q-PR returned a bare number, but the kcap-review tools need
owner/repo#N (or a URL, or branch auto-detection) — so "review PR# 283"
retyped in a later session on an unrelated branch could fail to resolve
or resolve against the wrong repo, undercutting exactly the cross-session
retyping the prompts were reworked for. Q-PR now joins
v_an_repositories and returns the full ref (verified through the
governed endpoint), the prompts read `review {{PR}}`, and the handler
passes the ref explicitly rather than relying on auto-detection.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
/review |
"review <ref>" said what to do; "Review <ref> and its key decisions" says what makes this review different — the recorded decisions behind the diff. Same handler, same bounded form; "and its key decisions" also steers the bounded menu review toward the reasoning summary rather than a line-by-line pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Each section now opens with "Prompt ❯ `Start the <use case> tour` to begin" directly under the header, before the description and sample prompts. The tour is the guided path and belongs first; the samples are the à-la-carte alternatives. The backticked phrase is unchanged, so the completion markers and TODO matching are unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Observed in a live tour run: a step described the query it was about to run and then offered `go` as the advance. Typing `go` teaches nothing — the real phrasing stayed hidden in the step's prose, which defeats rule 2 (typing it IS the tutorial). Composed prompt suggestions must now be the self-contained phrasing the user would type in a fresh session — the advance for "I'll query your last 7 days of spend" is the spend question itself, and variations are full prompts too, not deltas like "make it 30 days instead". Pure tour navigation (skip to ..., back to the menu) stays conversational by design. Rule 5 cross-references the composition rules so the two can't drift. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…for scanning The table caption says whose sessions these are: "Your team's coding agent sessions at a glance". A first-time reader doesn't yet know that "session" means an agent conversation. Tour steps now carry explicit formatting rules — bold step-number header, results as markdown tables rather than sentences of numbers, typed/run things in fenced code, the takeaway in bold, and a hard cap of two or three short paragraphs before the Prompt lines. Observed in live runs that steps drift toward prose walls without this. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…deferred tools
Live turn-1 runs surfaced three latency sinks.
Q-DONE is disabled outright. Even at limit 20 its marker search returned
~100KB of snippets, sinking the turn budget for a TODO checklist. The
markers are still emitted at tour completion so history accrues, but
nothing looks them up until the server offers a snippet-size cap or
marker-only search. Everything that leaned on Q-DONE is rewired: the
TODO list renders only what turn 1 can know (whoami for the install
line, this-conversation completions for tour lines), the no-sessions
fallback now triggers from the tours' own author lookup, and the evals
readiness check does its own search_sessions at limit 1, reading
resolved_author.session_count off the envelope rather than the results.
Q-PR gains scope global: the repo-scoped default probes the cwd for a
checkout and costs a retry when there is none, and since {{PR}} is a
full owner/repo#N ref the demo works from any directory.
Beat 1 now says to load deferred MCP tool schemas in ONE lookup in the
same message as the welcome — per-tool lookups are full round-trips
that serialise a beat designed to run parallel.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Q-COST and Q-PR merge into Q-MENU: the pr_ref rides along as a constant column on the table rows (LEFT JOIN latest_pr ON TRUE, so an empty PR view cannot empty the table). Both were scope-global already, and every query_analytics call pays full transport — live turn-1 runs showed the two calls serialising in practice, so the fix is not "run them in parallel" but "stop there being two". Beat 1 is now whoami plus exactly one MCP query. Verified through the governed endpoint. The menu header gains the lightning bolt, and every tour step — plus turn 1 itself — now closes with "Learn more: https://capacitor.kurrent.io/docs" after the Prompt lines. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Live test: "start the kcap guided tour" matched the skill's description (trigger matching worked), but the model then invoked kcap:guided-tour — the plugin-namespaced id hard-coded inside the description text — instead of the id the listing actually carried, and got "Unknown skill". The installer rewrites name: per surface (guided-tour in the plugin, kcap-guided-tour under ~/.agents, Kiro, Antigravity, personal copies) but the description text never changes, so on every non-plugin surface it advertised an id that does not exist there. The description now says "invoke this skill directly by its listed name" and refers to "the recap skill" rather than kcap:recap — no hard-coded ids to mislead invocation on any surface. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
With Q-DONE disabled the import line rendered permanently unticked, which reads as "you still need to import" to a user whose table two lines up is full of sessions. Q-MENU already answers the question well enough: at least one raw row with sessions >= 1 means the record has data, so the TODO crosses out. Judged on the raw rows before the render filter, so a solo user whose repos are all personal (filtered from display) still gets the tick — the import offer only replaces the table when Q-MENU returned nothing at all. Team-wide is a looser proxy than the user's own count (which needs Q-DONE), accepted until the marker lookup returns. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Auditing whether the guided tour works on every harness found one hole: Pi's kcap-mcp.ts bridge hardcoded review/sessions/flows/memory — no analytics. The tour's menu query calls query_analytics, so on Pi the skill installed fine (it rides the shared ~/.agents/skills tree) but every tour would have opened with the degrade fallback, permanently and silently. Every other harness already carries kcap-analytics: the Claude plugin's .mcp.json, Codex's .codex-mcp.json and TOML registration, and the ForCursor set that Cursor/Copilot/Gemini/Kiro/OpenCode/Antigravity all register. Pi was the one surface assembled from its own hardcoded list. Existing Pi users pick the fixed bridge up automatically: the extension carries a version marker the npm postinstall refresh rewrites on upgrade. Tests: CodingAgentsStepTests 131/131, SetupCommandTests 39/39; AOT publish clean, zero IL2026/IL3050. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
/review |
|
Code review by qodo was updated up to the latest commit 0c3279d |
Qodo: adding analytics to KCAP_MCP_SERVERS made the "four hung servers" comment stale. Reworded without a count, as suggested — comments that hard-code the size of an adjacent list drift every time it grows. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Yes @realtonyyoung, it's added in the same way that is added same way the other skills are any Claude says it's good
|
|
/review |
|
Code review by qodo was updated up to the latest commit 9833114 |

Closes #368 · AI-1530
What
Ships the Capacitor guided-tour skill in the kcap plugin, and gives it a discovery path from
kcap setup.A user who finishes
kcap setupis recording and has no idea what that buys them. Every skill we ship today assumes the user already knows what Capacitor does and which surface they want.guided-touris the onboarding path: it shows the team's recorded sessions, spend and errors, then offers per-use-case tutorial tours (session recall, evals, PR review, analytics) — and handles the missing pieces, offering to install kcap when it isn't set up and to import history when the user has no sessions.Spec:
docs/superpowers/specs/2026-07-27-guided-tour-skill-design.mdChanges
kcap/skills/guided-tour/SKILL.mdsrc/Capacitor.Cli.Core/AgentsSkillsInstaller.csSourceNames+=guided-tour— reaches Codex/Cursor/Kiro/Antigravity via~/.agents/skills/kcap-guided-tour/src/Capacitor.Cli/Commands/SetupCommand.csinternal constsrc/Capacitor.Cli/Commands/CodingAgentsStep.csSourceNamesinstead of a hand-maintained copy (which had already drifted — it printedreview-flowsunprefixed)kcap/.claude-plugin/plugin.jsonsrc/Capacitor.Cli.Core/Pi/PiMcpExtensionInstaller.csanalytics— the only harness missing it; the tour's menu query needsquery_analytics, and on Pi it would have degraded permanentlyREADME.md,kcap/README.md,help-plugin.txtSourceNamesmirror + a guard pinning it to the installer; CTA wording; CTA-phrase-in-frontmatter guardProvenance of the skill content
The skill was authored and iterated against live trials outside this repo (
kcap-quickstart-eval@c6717f1). It landed here verbatim and was then changed deliberately, in review. It is no longer byte-identical to that source — here is every deviation and why:Adaptations for shipping in the plugin
name: kcap-guided-tour→name: guided-tour, matching the existing short-name convention (recap,errors, …).AgentsSkillsInstaller.RewriteNameFrontmatterre-prefixes it tokcap-guided-touron copy for non-Claude vendors.Neverbullet "curate apply…,kcap-memory,kcap-flows— empty or inert on day one" → scoped to "typically … on a fresh workspace". The original asserted something about every org.Changed in review
"Start kcap guided tour"— the phrase the setup CTA tells users to type. A test pins the two together.Q-COSTrewritten to pre-aggregate cost per session before joining (see below), then extended to carry the whole table;Q-ERRwas first pruned (WHERE t.errors > 0) and later folded away entirely by thetool callschange below.hourscolumn becametool calls, and the whole table is now one query.v_an_sessions.duration_minis wall clock (ended_at - started_at) — sessions left open dominate it, and the schema itself labels it ELAPSED / not-human-effort. Time-based replacements were investigated and rejected: every duration surface in the views brackets human decisions (details in kurrent-io/kcap-server#1189). A count is immune, and it givestool errorsthe denominator it never had. Both counts come fromv_an_session_steps— the same view — so the error rate a reader computes is internally consistent;Q-ERRfolded away entirely and beat 1 shrank to three parallel calls. Verified through the governed/api/analytics/queryendpoint.go(with the skip-navigation exception); every step and turn 1 close with a docs link; step formatting rules (bold step header, tables for results, takeaway in bold); the tour entry point leads each section;LLM cost (USD)column header; explicit wait cues in all ten welcome variants (two also fixed to obey the skill's own no-analogy / no-savings-claim rules); the frontmatter description no longer hard-codes any invocation id (it advertised/kcap:guided-tour, which does not exist on non-plugin surfaces and misled invocation in a live run).pr_refrides every row viaLEFT JOIN ... ON TRUE; verified through the governed endpoint); Q-DONE disabled outright — its marker search returned ~100KB of snippets even atlimit: 20— with markers still emitted for when a server-side snippet cap exists; the import TODO ticks off Q-MENU's session counts instead; deferred-MCP hosts load tool schemas in one batched call.WHEN EVALS ARE NOT DEMONSTRABLE YET. Evals are a pipeline — score a session, accumulate facts across many, promote guidance, write it to CLAUDE.md — and only the first stage fits inside a tour. On a fresh workspace the rest is empty. The skill now checks reach before promising (its ownsearch_sessionslookup atlimit: 1, reading the author's session count off the response envelope, pluscurate apply --dry-run, which reports without writing) and falls back to onboarding: run one real eval on the user's own session, read those scores, step through what the rest needs, then show a clearly-labelled illustration. That illustration is the one sanctioned exception to the skill's "their data, always" rule, which now names it rather than contradicting it.The marker tokens (
PromptedStartEvalsTouretc.) are unchanged — earlier tours match on them across sessions, so only their ordering moved.Notes on scope
.codex-pluginversion bump.kcap/skills/is a single shared tree — Claude reads it in place via the marketplace entry; every other vendor gets a copy fromAgentsSkillsInstaller..codex-plugin/plugin.jsononly points at./.codex-mcp.json, which this PR doesn't touch, and it has never been bumped in lockstep since the rename commit.v_an_*analytics views, part of the server's governed schema./kcap-guided-tourrepo-wide.Review changes applied
skills/guided-tour/SKILL.mdexists at a surface an agent would load it from — the registered Claude marketplace path read back from settings (kcap/legacy keys, matchingIsInstalled), falling back to this build's plugin dir only when nothing is registered, or the shared/Kiro/Antigravity skills dirs. Every clause is a file-existence check on the artifact itself; install-result flags are deliberately not used because installers report false on already-current. Negative tests pin each failure mode found on the way: skip-flags, upgrade-from-older-skills, empty folder after failed copy, empty dir defaults, stale plugin dir, legacy-only registration./kcap:guided-touronly works through the Claude Code plugin, while under~/.agents/skillsthe skill iskcap-guided-tour. The prompt works everywhere. That made the skill's advertised triggers part of the contract, so a test now fails if the CTA and the skill's triggers drift apart.Q-COST(Qodo).SUM(DISTINCT s.duration_min)dedupes by duration value across the whole repo bucket rather than by session. Verified against a live tenant: the shipped query is correct on current data —duration_minis a 16-decimal-place quotient and the only repeated value among cost-bearing sessions isNULL, whichSUMignores; both variants returned a 0.0 hour delta on every top repo. So it was a latent hazard resting on an unenforced property of a view in another repo, not a live defect. Fixed anyway, and the replacement is smaller: pre-aggregatingv_an_cost(one row per session per model) to one row per session makes the join 1:1, so bothDISTINCTcrutches are deleted.Q-ERR(Qodo). Flagged as an unbounded query in the menu's critical path. Measured at 78 rows. Both suggested bounds are worse: bounding by error count is measurably wrong — Q-ERR is a lookup consulted after Q-COST has chosen the rows, and one repo sits 3rd by cost but 7th by errors, so a top-5-by-errors limit would render0where the real count is 271; fusing it into Q-COST via a CTE serialises two calls beat 1 deliberately dispatches in parallel. Took the free prune instead:errors > 0, verified to drop 78 rows to 62 with identical totals and zero repos whose count changed, plus prose recording why there is noLIMIT.CLAUDE.md's self-explanatory-code rule. Trimmed to 4.Contains()over all ofSKILL.mdwhile claiming to pin the frontmatter. Vendors match on the frontmatterdescriptiononly. Now slices the YAML block and asserts inside the description value; proven by mutation — moving the phrase into the body fails the test, where the old assertion passed.AI-794) in the new spec file. It isn't there, and isn't on any line this PR adds — the string occurs once in the repo, in pre-existingREADME.mdtext two lines below the edited block.Verification
Live-run
kcap plugin install --skills(scratchHOME,KCAP_PLUGIN_DIR→ this worktree) writes~/.agents/skills/kcap-guided-tour/SKILL.mdwithname: kcap-guided-tour, alongside the other six.kcap setup --server-url <stub> --no-promptexits 0 and closes with the call-to-action panel./api/analytics/queryendpoint — accepted, correct shape, and equivalence to the previous versions confirmed by running old and new in a single query at one instant.Tests. Unit suite 3954/3969 pass, 9 skipped, 6 failed — every failure in a rotating set of pre-existing timing / temp-file flakes. The failing subset differs run to run, none of it is code this PR touches, and the flakiest of them fails on unmodified
origin/maintoo (sampling numbers in a comment below). AOT publish clean: zero IL2026/IL3050 warnings.NOT exercised by any live run — prompt paths this repo's test suite cannot cover. They rest on the pre-merge trials, and the reordered menu and the new evals-fallback section have not been through a trial:
whoamifails → tour degrades to setup help.Q-COSTreturns nothing → table replaced by the import offer.curate apply --dry-runreadiness probe and the labelled illustration.✓ Prompted<Id>Tour; a later tour in another repo finds it and crosses the TODO out.kcap evalstep (1–3 min, backgrounded)./kcap:guided-tournor the"Start kcap guided tour"prompt was driven end-to-end in a real agent session. Frontmatter parses and is discovered as a skill, and the~/.agents/skillsinstall path is verified live, but the trigger-to-tour hop is unproven here.Server-side follow-up filed
Rendering turn 1 against real data showed the original column was not credible — is wall clock, so sessions left open dominate it. Resolved in this PR by replacing the column with (see above). The underlying data-semantics problem — all three duration surfaces in the governed views bracket human decisions — is filed as kurrent-io/kcap-server#1189 with the projection-level fix paths.
🤖 Generated with Claude Code