Skip to content

feat(plugins): ship Agent Session Recap (DAN-77) - #140

Merged
danielss-dev merged 2 commits into
mainfrom
developements/dan-77-agent-session-recap-a0ec
Oct 1, 2026
Merged

danielss-dev merged 2 commits into
mainfrom
developements/dan-77-agent-session-recap-a0ec

Conversation

@danielss-dev

Copy link
Copy Markdown
Owner

Implements DAN-77: capture Claude plugin research against today's declarative model, then ship Agent Session Recap as the first Tier-1 dogfood plugin.

What landed

  • Design note: docs/plugin-marketplace-backlog.md — Tier 1–4 ideas tagged with markdown/status, repository.read/ai.invoke, vs new view types, network.fetch, and isolation. Marketplace order recorded; remote catalog explicitly deferred.
  • Bundled builtin daniels.session-recap (Strand-maintained, same reservation as Heroi/Quick Notes). Static declarative views cannot follow worktree/review context, so Recap is not a fake static markdown snapshot.
  • Settings → Plugins install; Workbench surface Session Recap lists files touched, risky paths (auth/secrets/migrations), and TODOs in loaded patches. Open review reuses the existing Review bridge. Empty/no-repository states do not crash.
  • Permissions: repository.read only. No list/badge types, Risk Radar, secret sniffer, CI panels, or remote marketplace.

Proof

  • Manifest validates via validatePluginManifest; module reserved for daniels.session-recap.
  • pnpm --filter ./ui exec tsc --noEmit and pnpm --filter ./ui test pass (including recap.test.ts).

Linear: DAN-77

Open in Web Open in Cursor 

Capture the Tier 1-4 marketplace backlog against today's declarative
plugin model, then add a Strand-maintained Recap builtin that summarizes
files, risky paths, and leftover TODOs in the active worktree.

Co-authored-by: Daniels <danielss-dev@users.noreply.github.com>

@danielss-dev danielss-dev left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bugbot pass on Agent Session Recap (DAN-77). Found concrete failures in how Recap merges/loads patches for the TODO scan — not style nits. No approve/merge.

linkedWorktree: meta?.is_linked_worktree ?? false,
baselineShort: baseline?.short ?? null,
status: canReadRepo ? status : [],
diffs: canReadRepo ? uniqueDiffs(unstagedDiffs, stagedDiffs, review) : [],

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug Review pool overwrites loaded local patches — TODO scan stays empty

Failure mode: uniqueDiffs(unstagedDiffs, stagedDiffs, review) keeps the last pool per path. This effect loads patches into unstagedDiffs / stagedDiffs, but the model then prefers baselineDiffs / reviewUnstagedDiffs from refreshReviewDiffs(), which only writes summaries (patchLoaded: false, empty patch via mergeDiffSummaries). buildAgentSessionRecap skips those rows (patchLoaded === false || !diff.patch), so patchesScanned stays 0 and the UI permanently shows “Patches are not loaded yet…” even after the load Promise succeeds.

Same last-write-wins also drops the unstaged side when a path is partially staged (uniqueDiffs(unstaged, staged) keeps staged only), so TODOs only in the unstaged hunk are never scanned.

Evidence: refreshReviewDiffs → repoDiffSummary only; Recap retains + refreshes review, then discards the loaded local FileDiffs here.

Fix sketch: When merging pools, prefer an entry with patchLoaded !== false, or scan patches from every pool independently (no path Map overwrite). For partial stages, union both sides’ patches for the TODO extractor.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 8188117: uniqueDiffs prefers loaded patches; Recap uses unionDiffs so unstaged+staged patches for the same path are both scanned.

.map((diff) => diff.path);
return Promise.all([
unstaged.length ? current.loadDiffFiles('unstaged', unstaged) : Promise.resolve(),
staged.length ? current.loadDiffFiles('staged', staged) : Promise.resolve(),

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug Review patches are never loaded — baseline-only sessions never scan TODOs

Failure mode: The loader only calls loadDiffFiles('unstaged' | 'staged', …). Paths that exist only in baselineDiffs / reviewUnstagedDiffs (pinned Review baseline + clean worktree, or review-only rows) never receive patches. The empty-state copy tells users to “Pin a Review baseline”, then the ready UI sticks on plugins.recap.todosUnavailable (“not loaded yet”) forever — not a transient loading state.

Evidence: Missing list is built from uniqueDiffs(unstagedDiffs, stagedDiffs) only; no loadDiffFiles('review', …) despite retainDiffs(path, 'review') + refreshReviewDiffs().

Fix sketch: After refresh, also take up to RECAP_PATCH_SCAN_LIMIT review paths with patchLoaded === false and loadDiffFiles('review', paths) (keep the bound so Recap does not become a whole-tree loader).

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 8188117: recapMissingPatchPaths also loadDiffFiles('review', …) up to RECAP_PATCH_SCAN_LIMIT.

releaseReview();
releaseLocal();
};
}, [visible, path, baseline?.oid, canReadRepo]);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug Patch load effect does not re-run when the session keeps changing

Failure mode: Deps are only [visible, path, baseline?.oid, canReadRepo]. While Recap stays mounted/visible, further agent edits add paths or mergeDiffSummaries resets patchLoaded: false when the content revision changes. The file list updates via useMemo, but those patches are never loaded again, so leftover TODOs in the live session are missed until remount or baseline change.

Evidence: Effect cleanup/retainers keep diffs refreshing via needsDiffs, but this effect itself does not re-enter the load pipeline; mergeDiffSummaries intentionally clears patches on revision mismatch.

Fix sketch: Also depend on something that moves when unloaded paths appear (e.g. diffsTick, or a stable fingerprint of paths with patchLoaded === false), still capped by RECAP_PATCH_SCAN_LIMIT.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 8188117: load effect depends on recapUnloadedPatchKey so new/reset unloaded paths re-trigger load without looping on refresh.

Prefer loaded patches over review summaries, union unstaged and staged
sides, load review-only paths, and re-fetch when unloaded paths appear.

Co-authored-by: Daniels <danielss-dev@users.noreply.github.com>
@danielss-dev
danielss-dev marked this pull request as ready for review October 1, 2026 12:38
@danielss-dev
danielss-dev merged commit 755808e into main Oct 1, 2026
5 checks passed
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.

2 participants