Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 80 additions & 13 deletions cmd/agentsview/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -592,10 +592,49 @@ func TestCollectWatchRootsHermesSessionsWatchesStateDBParent(t *testing.T) {
assert.Equal(t, []string{sessionsDir}, roots[1].dirs)
}

func TestCollectWatchRootsUsesProviderWatchPlan(t *testing.T) {
func TestCollectWatchRootsUsesCoworkProviderRecursiveRoot(t *testing.T) {
root := t.TempDir()
for _, dir := range []string{"brain", "conversations", "implicit"} {
require.NoError(t, os.Mkdir(filepath.Join(root, dir), 0o755), "mkdir %s", dir)
cfg := config.Config{
AgentDirs: map[parser.AgentType][]string{
parser.AgentCowork: {root},
},
}

roots, unwatchedDirs := collectWatchRoots(cfg)

require.Empty(t, unwatchedDirs, "cowork root should be watched directly")
got, ok := findCollectedWatchRoot(roots, root)
require.True(t, ok, "cowork provider WatchPlan root not collected")
assert.False(t, got.shallow,
"cowork provider recursive WatchPlan must override legacy ShallowWatch")
assert.Equal(t, []string{root}, got.dirs)
}

func TestCollectWatchRootsUsesGeminiProviderMetadataRoot(t *testing.T) {
root := t.TempDir()
tmpRoot := filepath.Join(root, "tmp")
require.NoError(t, os.Mkdir(tmpRoot, 0o755), "mkdir tmp")
cfg := config.Config{
AgentDirs: map[parser.AgentType][]string{
parser.AgentGemini: {root},
},
}

roots, unwatchedDirs := collectWatchRoots(cfg)

require.Empty(t, unwatchedDirs, "all gemini provider roots exist")
metadataRoot, ok := findCollectedWatchRoot(roots, root)
require.True(t, ok, "gemini provider metadata root not collected")
assert.True(t, metadataRoot.shallow)
tmp, ok := findCollectedWatchRoot(roots, tmpRoot)
require.True(t, ok, "gemini provider recursive tmp root not collected")
assert.False(t, tmp.shallow)
}

func TestCollectWatchRootsUsesAntigravityCLIHistoryRoot(t *testing.T) {
root := t.TempDir()
for _, subdir := range []string{"brain", "conversations", "implicit"} {
require.NoError(t, os.Mkdir(filepath.Join(root, subdir), 0o755))
}
cfg := config.Config{
AgentDirs: map[parser.AgentType][]string{
Expand All @@ -605,16 +644,44 @@ func TestCollectWatchRootsUsesProviderWatchPlan(t *testing.T) {

roots, unwatchedDirs := collectWatchRoots(cfg)

require.Empty(t, unwatchedDirs, "unwatched dirs before watcher setup")
require.Len(t, roots, 4)
assert.Equal(t, filepath.Join(root, "brain"), roots[0].root)
assert.False(t, roots[0].shallow)
assert.Equal(t, filepath.Join(root, "conversations"), roots[1].root)
assert.True(t, roots[1].shallow)
assert.Equal(t, root, roots[2].root)
assert.True(t, roots[2].shallow, "history.jsonl root should be watched shallowly")
assert.Equal(t, filepath.Join(root, "implicit"), roots[3].root)
assert.True(t, roots[3].shallow)
require.Empty(t, unwatchedDirs, "all antigravity cli provider roots exist")
historyRoot, ok := findCollectedWatchRoot(roots, root)
require.True(t, ok, "antigravity cli history.jsonl root not collected")
assert.True(t, historyRoot.shallow)
conversations, ok := findCollectedWatchRoot(
roots, filepath.Join(root, "conversations"),
)
require.True(t, ok, "antigravity cli conversations root not collected")
assert.True(t, conversations.shallow)
brain, ok := findCollectedWatchRoot(roots, filepath.Join(root, "brain"))
require.True(t, ok, "antigravity cli brain root not collected")
assert.False(t, brain.shallow)
}

func TestMissingWatchRootCoverageDoesNotTreatShallowAncestorAsRecursive(t *testing.T) {
root := filepath.Clean(filepath.Join(t.TempDir(), "state"))
shallowRoots := []watchRoot{{root: root, shallow: true}}
recursiveRoots := []watchRoot{{root: root, shallow: false}}

assert.True(t,
pathCoveredByAnyWatchRootCreation(filepath.Join(root, "sessions"), shallowRoots),
"shallow roots can observe immediate child creation")
assert.False(t,
pathCoveredByAnyWatchRootCreation(filepath.Join(root, "nested", "sessions"), shallowRoots),
"shallow ancestors must not be treated like recursive watches")
assert.True(t,
pathCoveredByAnyWatchRootCreation(filepath.Join(root, "nested", "sessions"), recursiveRoots),
"recursive roots cover nested missing roots")
}

func findCollectedWatchRoot(roots []watchRoot, path string) (watchRoot, bool) {
path = filepath.Clean(path)
for _, root := range roots {
if filepath.Clean(root.root) == path {
return root, true
}
}
return watchRoot{}, false
}

func TestResyncCoversSignals(t *testing.T) {
Expand Down
3 changes: 0 additions & 3 deletions cmd/agentsview/parse_diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,9 +268,6 @@ func parseDiffAgentSupported(def parser.AgentDef) bool {
if !def.FileBased {
return false
}
if def.DiscoverFunc != nil {
return true
}
switch parser.ProviderMigrationModes()[def.Type] {
case parser.ProviderMigrationProviderAuthoritative:
_, ok := parser.ProviderFactoryByType(def.Type)
Expand Down
22 changes: 22 additions & 0 deletions cmd/agentsview/parse_diff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,28 @@ func TestParseDiffAgentTypes(t *testing.T) {
}
}

func TestParseDiffSupportedAgentsIncludesProviderAuthoritativeAgents(t *testing.T) {
supported := parseDiffSupportedAgents()
modes := parser.ProviderMigrationModes()
// Build the expected set from the registry so the contract covers every
// current file-based, provider-authoritative agent and stays correct as
// the migration manifest changes, rather than a hand-maintained subset.
checked := 0
for _, def := range parser.Registry {
if !def.FileBased ||
modes[def.Type] != parser.ProviderMigrationProviderAuthoritative {
continue
}
checked++
assert.True(t, parseDiffAgentSupported(def),
"parse-diff support must include provider-authoritative %s", def.Type)
assert.Contains(t, supported, string(def.Type),
"parse-diff supported list must include %s", def.Type)
}
require.Positive(t, checked,
"expected at least one file-based provider-authoritative agent")
}

func TestParseDiff_EmptyArchiveRunsClean(t *testing.T) {
isolateParseDiffEnv(t)

Expand Down
23 changes: 7 additions & 16 deletions cmd/agentsview/token_use.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,9 @@ const (
// multiple suffix matches exist without an exact row, the
// most recent wins and an ambiguity warning is emitted.
// 3. Canonical disk probe: when input begins with a registered
// agent prefix, strip the prefix and call that agent's
// FindSourceFunc so a truly canonical-but-unsynced ID on disk
// still resolves.
// 4. Raw disk probe: call every file-based agent's FindSourceFunc
// agent prefix, strip the prefix and ask that agent's disk source
// lookup so a truly canonical-but-unsynced ID on disk still resolves.
// 4. Raw disk probe: ask every file-based agent's disk source lookup
// with the raw input; the first hit yields "<prefix><input>".
// 5. No match anywhere: returned unchanged with known=false.
//
Expand Down Expand Up @@ -124,13 +123,9 @@ func resolveRawSessionID(
}

// agentHasDiskSourceLookup reports whether a session source can be located on
// disk by raw ID for the agent: via the legacy AgentDef FindSourceFunc hook, or
// via a provider-authoritative provider's FindSource for agents whose lookup was
// folded onto the provider (e.g. Codex).
// disk by raw ID for the agent, via its provider-authoritative provider's
// FindSource.
func agentHasDiskSourceLookup(def parser.AgentDef) bool {
if def.FindSourceFunc != nil {
return true
}
if parser.ProviderMigrationModes()[def.Type] !=
parser.ProviderMigrationProviderAuthoritative {
return false
Expand All @@ -140,13 +135,9 @@ func agentHasDiskSourceLookup(def parser.AgentDef) bool {
}

// findAgentSourceFile resolves a raw agent session ID to an on-disk source path
// under dir, using the legacy FindSourceFunc when present and otherwise the
// provider's FindSource (RawSessionID lookup). Returns "" when no source
// resolves or the agent has no on-disk lookup.
// under dir via the provider's FindSource (RawSessionID lookup). Returns ""
// when no source resolves or the agent has no on-disk lookup.
func findAgentSourceFile(def parser.AgentDef, dir, rawID string) string {
if def.FindSourceFunc != nil {
return def.FindSourceFunc(dir, rawID)
}
factory, ok := parser.ProviderFactoryByType(def.Type)
if !ok {
return ""
Expand Down
61 changes: 58 additions & 3 deletions cmd/agentsview/token_use_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,8 @@ func TestResolveSessionID_CanonicalCodexID_OnDiskNotInDB(t *testing.T) {
ctx := context.Background()

// Canonical "codex:<uuid>" not yet synced but present on
// disk must resolve via the canonical disk probe — which
// strips the prefix before calling FindSourceFunc (the
// underlying finder rejects colon-bearing IDs).
// disk must resolve via the canonical disk probe, which strips
// the prefix before asking the agent source lookup.
codexDir := filepath.Join(t.TempDir(), "codex-sessions")
uuid := "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
dayDir := filepath.Join(codexDir, "2026", "04", "17")
Expand All @@ -249,6 +248,39 @@ func TestResolveSessionID_CanonicalCodexID_OnDiskNotInDB(t *testing.T) {
assert.True(t, known, "canonical disk probe")
}

func TestResolveSessionID_ProviderAuthoritativeCursorOnDiskNotInDB(t *testing.T) {
d := newTestDB(t)
ctx := context.Background()

cursorDir := t.TempDir()
rawID := "provider-cursor"
transcriptPath := filepath.Join(
cursorDir,
"Users-fiona-Documents-demo",
"agent-transcripts",
rawID+".jsonl",
)
require.NoError(t, os.MkdirAll(filepath.Dir(transcriptPath), 0o755))
require.NoError(t, os.WriteFile(
transcriptPath,
[]byte(`{"role":"user","content":"hi"}`+"\n"),
0o644,
))

agentDirs := map[parser.AgentType][]string{
parser.AgentCursor: {cursorDir},
}
got, known := resolveRawSessionID(ctx, d, agentDirs, rawID)
assert.Equal(t, "cursor:"+rawID, got,
"provider FindSource should resolve unsynced raw cursor IDs")
assert.True(t, known, "provider disk probe")

got, known = resolveRawSessionID(ctx, d, agentDirs, "cursor:"+rawID)
assert.Equal(t, "cursor:"+rawID, got,
"canonical provider ID should resolve via provider FindSource")
assert.True(t, known, "canonical provider disk probe")
}

func TestResolveSessionID_RawOpenClawCollidesWithCodexPrefix(t *testing.T) {
d := newTestDB(t)
ctx := context.Background()
Expand Down Expand Up @@ -291,6 +323,29 @@ func TestResolveSessionID_UnderscoreID_NoFalseMatch(t *testing.T) {
assert.True(t, known)
}

func TestAgentHasDiskSourceLookupIncludesProviderAuthoritativeAgents(t *testing.T) {
for _, agent := range []parser.AgentType{
parser.AgentGptme,
parser.AgentPi,
parser.AgentOMP,
parser.AgentWorkBuddy,
parser.AgentCortex,
parser.AgentKimi,
parser.AgentQwenPaw,
parser.AgentOpenHands,
parser.AgentCursor,
parser.AgentVibe,
parser.AgentClaude,
parser.AgentCowork,
parser.AgentHermes,
} {
def, ok := parser.AgentByType(agent)
require.True(t, ok, "agent %s", agent)
assert.True(t, agentHasDiskSourceLookup(def),
"token-use disk probe must include provider-authoritative %s", agent)
}
}

func TestUsageExitCode_TokenData(t *testing.T) {
u := &db.SessionUsage{HasTokenData: true}
assert.Equal(t, tokenUseExitOK, usageExitCode(u))
Expand Down
15 changes: 15 additions & 0 deletions docs/superpowers/plans/2026-06-20-provider-dual-run-harness.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Provider Dual-Run Harness Implementation Plan

> **Status:** Superseded for the `provider-explicit-registry` stack tip. This
> plan records the historical root-harness slice that was implemented on the
> lower `provider-facade-core` branch. Do not execute these steps against the
> final stack tip: `ProviderMigrationLegacyOnly` has been removed there,
> concrete parse-capable providers are expected to be `provider-authoritative`,
> and Claude.ai / ChatGPT are `import-only`. The stack-tip legacy cleanup in
> kata issue `n489` is complete; remaining provider-facade tracking lives in the
> caller and provider-group tasks listed in the provider facade design spec.
>
> For new provider migrations after this stack, use the current
> `internal/parser/provider_migration.go` contract: a lower branch may use
> `shadow-compare` as a transitional mode while parity is established, but the
> explicit-registry tip must reject `"legacy-only"` and keep the final manifest
> authoritative.

> **For agentic workers:** REQUIRED SUB-SKILL: Use
> superpowers:subagent-driven-development (recommended) or
> superpowers:executing-plans to implement this plan task-by-task. Steps use
Expand Down
4 changes: 4 additions & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,9 +272,13 @@ func TestDefault_IncludesCodexArchivedSessionsDir(t *testing.T) {
}

func TestDefault_SkipsAiderUntilConfigured(t *testing.T) {
t.Setenv("AIDER_DIR", "")
cfg, err := Default()
require.NoError(t, err)

// Aider has no safe default root: a passive viewer must not enumerate
// $HOME (macOS privacy prompts), so it stays unresolved until the user
// opts in via AIDER_DIR or aider_dirs.
assert.Empty(t, cfg.ResolveDirs(parser.AgentAider))
assert.False(t, cfg.IsUserConfigured(parser.AgentAider))
}
Expand Down
30 changes: 28 additions & 2 deletions internal/importer/importer.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,20 @@ func ImportClaudeAI(
}
}()

err := parser.ParseClaudeAIExport(r, func(
provider, ok := parser.NewProvider(
parser.AgentClaudeAI, parser.ProviderConfig{},
)
if !ok {
return stats, fmt.Errorf("claude.ai provider unavailable")
}
exporter, ok := provider.(parser.ClaudeAIExportParser)
if !ok {
return stats, fmt.Errorf(
"claude.ai provider does not support exports",
)
}

err := exporter.ParseClaudeAIExport(r, func(
result parser.ParseResult,
) error {
if ctx.Err() != nil {
Expand Down Expand Up @@ -279,7 +292,20 @@ func ImportChatGPT(
assetsDir: assetsDir,
}

err := parser.ParseChatGPTExport(dir, resolver,
provider, ok := parser.NewProvider(
parser.AgentChatGPT, parser.ProviderConfig{},
)
if !ok {
return stats, fmt.Errorf("chatgpt provider unavailable")
}
exporter, ok := provider.(parser.ChatGPTExportParser)
if !ok {
return stats, fmt.Errorf(
"chatgpt provider does not support exports",
)
}

err := exporter.ParseChatGPTExport(dir, resolver,
func(result parser.ParseResult) error {
if ctx.Err() != nil {
return ctx.Err()
Expand Down
Loading