Skip to content

[AI-613] history: explicit scope selection + --private - #58

Merged
alexeyzimarev merged 14 commits into
mainfrom
ai-613-history-import-scope
May 13, 2026
Merged

alexeyzimarev merged 14 commits into
mainfrom
ai-613-history-import-scope

Conversation

@alexeyzimarev

Copy link
Copy Markdown
Member

Summary

Replaces the default-import behaviour of kapacitor history with an explicit scope choice and a mandatory confirmation prompt, so users can't accidentally upload sessions from personal/private repos.

  • New scope flags: --all, --org, --repo <owner/name>, --repo . (current cwd's repo). Mutually exclusive.
  • Interactive Spectre picker when no flag is passed on a TTY (three top-level choices; "Specific repository" opens a sub-picker that pins the current repo at the top).
  • Mandatory confirmation summary (scope, matched count, repo samples, visibility); skip with --yes / -y.
  • --private: after import, PUT /api/sessions/{id}/visibility {"visibility":"none"} for every session imported in this run.
  • Non-interactive runs require both a scope flag and --yes — missing either errors with exit 1.

--cwd, --session, --min-lines, --since, and the excluded_repos profile setting continue to work and compose as additional filters on top of the new scope step.

Setup gains a one-line tip after completion: Optional: import past sessions with kapacitor history --org. Help text is updated to document every flag and the picker fallback.

Migration

CI/scripts currently calling kapacitor history without a flag on non-TTY will start erroring. Pass --all --yes for the prior behaviour. This is intentional — AI-613 is specifically about eliminating accidental uploads, and a silent default in automation can drift over time.

Test plan

  • Unit: ImportScopeArgsTests (parser + resolver, 15 tests), HistoryScopeFilterTests (5 tests), HistoryScopePromptTests (formatter + repo-choices, 7 tests). All 528 unit tests pass.
  • Integration: HistoryPrivateImportTests (WireMock — verifies one PUT per session id, request body {"visibility":"none"}, and that one 500 doesn't abort the loop). All 6 integration tests pass.
  • AOT publish: zero IL2026/IL3050 warnings.
  • Manual smoke (recommended before merge):
    • kapacitor history (picker path; pick each of the three options; verify confirmation; answer N cancels with exit 0)
    • kapacitor history --all --yes (no picker, no prompt, immediate import)
    • KAPACITOR_PROFILE=default kapacitor history --org (errors with "tenant-bound profile")
    • kapacitor history --repo . --yes inside a git repo (only this repo's sessions match)
    • cd /tmp && kapacitor history --repo . (errors with "not a git repo")
    • kapacitor history --repo EventStore/kapacitor --yes --private (summary shows visibility: private (--private); imported sessions show visibility: none on the server)
    • kapacitor history < /dev/null (errors "--all, --org, or --repo")
    • kapacitor history --all < /dev/null (errors "--yes is required")
    • kapacitor history --all --org --yes (errors "mutually exclusive")

Spec: docs/superpowers/specs/2026-05-13-ai-613-history-import-scope-design.md
Plan: docs/superpowers/plans/2026-05-13-ai-613-history-import-scope.md

Closes AI-613.

🤖 Generated with Claude Code

alexeyzimarev and others added 12 commits May 13, 2026 09:23
Designs three scope flags (--all/--org/--repo) plus interactive
picker fallback, a mandatory confirmation prompt (skippable with
--yes), and a --private override that post-imports PUT
visibility=none for each session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Nine TDD-shaped tasks covering ImportScope type, pure parser/filter/
formatter helpers, Spectre picker wrappers, HistoryCommand pipeline
integration, --private post-import visibility loop, Program.cs wiring,
setup tip + help text, AOT publish check, and manual smoke.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…config load

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the bare HandleHistory call with scope resolution: parse
ImportScopeFlags, detect the current repo, resolve the active profile,
and pass scope/skipConfirmation/forcePrivate/activeProfile/currentRepo
to HandleHistory.
@linear

linear Bot commented May 13, 2026

Copy link
Copy Markdown

AI-613

@qodo-code-review

Copy link
Copy Markdown

Review Summary by Qodo

[AI-613] Add explicit scope selection and confirmation to history import

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add explicit scope selection for kapacitor history with --all, --org, --repo <owner/name>,
  and --repo . flags
• Implement interactive Spectre picker when no scope flag on TTY; mandatory confirmation prompt
  before import
• Add --private flag to mark imported sessions as owner-only via post-import visibility API calls
• Enforce non-interactive mode requires both scope flag and --yes to prevent accidental uploads
Diagram
flowchart LR
  A["CLI args"] -->|ParseFlags| B["ImportScopeArgs"]
  B -->|Resolve| C["ImportScope<br/>All/Org/Repo"]
  C -->|null scope| D["HistoryScopePrompt<br/>RunPicker"]
  D --> E["Resolved Scope"]
  E -->|Apply| F["HistoryScopeFilter"]
  F --> G["Filtered transcripts"]
  G -->|PromptConfirm| H["Confirmation summary"]
  H -->|User confirms| I["Import sessions"]
  I -->|forcePrivate| J["SetVisibilityNone<br/>POST loop"]
  J --> K["Complete"]
Loading

Grey Divider

File Changes

1. src/kapacitor/Commands/ImportScope.cs ✨ Enhancement +12/-0

Add ImportScope record hierarchy for scope types

src/kapacitor/Commands/ImportScope.cs


2. src/kapacitor/Commands/ImportScopeArgs.cs ✨ Enhancement +95/-0

Add pure flag parser and scope resolver

src/kapacitor/Commands/ImportScopeArgs.cs


3. src/kapacitor/Commands/HistoryScopeFilter.cs ✨ Enhancement +36/-0

Add pure async scope filter for transcripts

src/kapacitor/Commands/HistoryScopeFilter.cs


View more (11)
4. src/kapacitor/Commands/HistoryScopePrompt.cs ✨ Enhancement +137/-0

Add Spectre picker and confirmation prompt helpers

src/kapacitor/Commands/HistoryScopePrompt.cs


5. src/kapacitor/Commands/HistoryCommand.cs ✨ Enhancement +127/-9

Wire scope resolution, filtering, confirmation, and --private loop

src/kapacitor/Commands/HistoryCommand.cs


6. src/kapacitor/Program.cs ✨ Enhancement +34/-1

Parse and resolve history scope flags in main handler

src/kapacitor/Program.cs


7. src/kapacitor/Commands/SetupCommand.cs ✨ Enhancement +1/-0

Add tip to import sessions after setup completes

src/kapacitor/Commands/SetupCommand.cs


8. src/Kapacitor.Core/Resources/help-history.txt 📝 Documentation +13/-3

Document new scope flags and --private option

src/Kapacitor.Core/Resources/help-history.txt


9. test/kapacitor.Tests.Unit/ImportScopeArgsTests.cs 🧪 Tests +205/-0

Add 15 unit tests for flag parser and resolver

test/kapacitor.Tests.Unit/ImportScopeArgsTests.cs


10. test/kapacitor.Tests.Unit/HistoryScopeFilterTests.cs 🧪 Tests +72/-0

Add 5 unit tests for scope filter logic

test/kapacitor.Tests.Unit/HistoryScopeFilterTests.cs


11. test/kapacitor.Tests.Unit/HistoryScopePromptTests.cs 🧪 Tests +99/-0

Add 7 unit tests for picker and formatter helpers

test/kapacitor.Tests.Unit/HistoryScopePromptTests.cs


12. test/kapacitor.Tests.Integration/HistoryPrivateImportTests.cs 🧪 Tests +66/-0

Add WireMock tests for --private visibility loop

test/kapacitor.Tests.Integration/HistoryPrivateImportTests.cs


13. docs/superpowers/specs/2026-05-13-ai-613-history-import-scope-design.md 📝 Documentation +215/-0

Add design spec for history import scope feature

docs/superpowers/specs/2026-05-13-ai-613-history-import-scope-design.md


14. docs/superpowers/plans/2026-05-13-ai-613-history-import-scope.md 📝 Documentation +1548/-0

Add detailed implementation plan with 9 TDD tasks

docs/superpowers/plans/2026-05-13-ai-613-history-import-scope.md


Grey Divider

Qodo Logo

@qodo-code-review

qodo-code-review Bot commented May 13, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Remediation recommended

1. Picker error misreported cancel ✓ Resolved 🐞 Bug ◔ Observability
Description
HistoryScopePrompt.RunPicker returns null for real error states (e.g., selecting Org while
activeProfile is "default", or when no repos are detectable), but HandleHistory treats any null
scope as a user cancellation and prints "Scope selection cancelled.", obscuring the actual failure
reason.
Code

src/kapacitor/Commands/HistoryCommand.cs[R284-288]

+            scope = HistoryScopePrompt.RunPicker(activeProfile, currentRepo, distinct);
+            if (scope is null) {
+                await Console.Error.WriteLineAsync("Scope selection cancelled.");
+                return 1;
+            }
Evidence
The picker explicitly returns null on error conditions, and HandleHistory unconditionally labels
null as cancellation.

src/kapacitor/Commands/HistoryScopePrompt.cs[91-105]
src/kapacitor/Commands/HistoryCommand.cs[273-288]

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

### Issue description
`HistoryScopePrompt.RunPicker(...)` returns `null` for multiple reasons (actual error conditions like "no org" / "no repos", not just cancellation). `HistoryCommand.HandleHistory(...)` currently treats any `null` as user cancellation and prints a generic cancellation message, which is misleading and can hide the actionable error.

### Issue Context
- `RunPicker` prints an error and returns `null` when the org choice is invalid (`activeProfile == "default"`) or when the repo sub-picker has no choices.
- `HandleHistory` then prints `"Scope selection cancelled."` for any `null` and exits 1.

### Fix Focus Areas
- src/kapacitor/Commands/HistoryScopePrompt.cs[91-105]
- src/kapacitor/Commands/HistoryCommand.cs[273-288]

### Suggested fix
- Change `RunPicker` to return a richer result (e.g., `bool ok` + `ImportScope scope` + `string? error`), **or** stop printing inside `RunPicker` and instead return an error string to the caller.
- Update `HandleHistory` to:
 - print the returned error message (or propagate it) and exit 1 for error states
 - reserve "cancelled" messaging for an actual user-cancel path (if you later add a cancel option).
- If you want a minimal fix without changing signatures: remove the extra `"Scope selection cancelled."` line and just `return 1;` when `scope is null`, since `RunPicker` already prints a specific error message today.

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


Grey Divider

Qodo Logo

alexeyzimarev and others added 2 commits May 13, 2026 10:53
RunPicker prints the specific reason (default profile / no repos
detected) before returning null. The trailing "Scope selection
cancelled." in HandleHistory obscured that real reason and was
misleading — Spectre's SelectionPrompt has no graceful cancel path
today, so a null return is always an error state.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both tests captured progress via `new Progress<T>(events.Add)` and
then slept `Task.Delay(50)` hoping the SynchronizationContext-marshalled
callbacks had landed. On Linux CI the window was occasionally tight and
the assertions raced ahead of the callbacks. Replace with a synchronous
IProgress<T> that appends in the calling thread — events are guaranteed
to be in the list by the time the awaited Import* call returns. Sleeps
removed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@alexeyzimarev
alexeyzimarev merged commit 4351cd7 into main May 13, 2026
3 checks passed
@alexeyzimarev
alexeyzimarev deleted the ai-613-history-import-scope branch May 13, 2026 09:03
alexeyzimarev added a commit that referenced this pull request May 13, 2026
* docs: update history command for explicit scope selection

Aligns README with PR #58 (AI-613): quick-start uses --org, and the
CLI reference documents the required scope flags, picker fallback,
confirmation prompt, --yes, --private, and the non-interactive
scope+--yes requirement.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: clarify --org and --repo . scope semantics

Addresses Qodo review findings on #60:

- --org doesn't look up org metadata; it uses the active profile *name*
  literally as the GitHub org login. Works after `kapacitor setup`
  (which names the profile after the picked tenant), errors on
  `default`, and matches zero sessions for arbitrarily-named profiles
  like `work`/`oss`. README now spells this out and points users to
  --repo <owner/name> as the fallback.
- --repo . requires the cwd to be in a git repo with an origin remote.
  Inline comment updated to call that out.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: use generic owner/repo placeholder in --repo examples

Avoid leaking a specific org into the public README examples.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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