Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,17 @@ or complete-query contract and its regression coverage are tracked in
archive-semantics fix.

The action, navigation, relation-card, header-menu, and archive-tree suites own the
regression coverage. Repository-wide check results are recorded in the PR status
rather than duplicated here.
focused regression coverage. Desktop journey `LODY-SESSION-004` exercises the full
user sequence with two real worktree forks: archive and archived-root delete remove
the opener and its child Tab while the opened Sessions, their ACP processes, and
their worktrees survive. It then opens both survivors to verify non-navigable deleted
provenance. The same journey delays the real initial metadata Flock scan across a
renderer reload and closes an empty child Tab populated by live metadata events,
proving exact cleanup remains available before full hydration. Repository-wide check
results are recorded in the PR status rather than duplicated here. The journey waits
for every blocked metadata scan and the canonical root-Tab route before teardown, so
runtime evidence cannot race the hydration-triggered navigation. Run-configuration
menu steps accept an already selected Agent and use keyboard submenu activation. Fork
steps likewise use keyboard activation and identify the source ACP Session by its
project working directory, avoiding hover-card interception and concurrent title-agent
events.
27 changes: 14 additions & 13 deletions e2e/COVERAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,20 @@ Backlog rows are evidence-backed gaps, not executable or promised scenarios.

## Active P1 journeys

| Stable id | Journey | Renderer | Electron / IPC | Bundled CLI | Durable state | External wire |
| ------------------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------------- | -------------------------------------------- | ------------------------------------------------- | ----------------------------------------------------- | ------------------------------------ |
| `LODY-FORK-001` | Fork a completed Session into an independent worktree Session | Session fork destination and origin | Real window and Session RPC | Fork operation and recovery markers | Independent Session history and worktree | Scripted ACP |
| `LODY-MCP-001` | Create a workspace MCP server and preserve explicit turn selection through dispatch | MCP settings and composer selection | Real window and workspace RPC | Catalog persistence and ACP dispatch | Workspace catalog plus turn input selection | Synthetic stdio MCP and scripted ACP |
| `LODY-MCP-002` | Edit, disable, re-enable, and delete a workspace MCP catalog entry | MCP catalog editor and enabled-state switch | Real window and workspace RPC | Workspace MCP catalog create, update, and delete | MCP fields and enabled state across settings reopen | None |
| `LODY-PROJECT-001` | Add, select, and remove a local project without deleting its directory | Project picker, sidebar selection, and removal confirmation | Real window and local project control bridge | Project catalog add, list, and delete | Workspace project catalog lifecycle | None |
| `LODY-PROJECT-002` | Switch between local projects without duplicating a reopened folder | Project picker and sidebar switching | Real window and local project control bridge | Project catalog identity and duplicate prevention | Two unique local project records | None |
| `LODY-QUEUE-001` | Remove one queued follow-up and dispatch only the retained message | Queue controls and pending-message list | Real window and Session RPC | Queue persistence and ordered dispatch | Pending Turn queue removal and drain | File-signaled scripted ACP |
| `LODY-REVIEW-001` | Open, hide, and switch a synthetic large diff | Large diff Review lifecycle | Real window and diff RPC | Real owned runtime | Synthetic project and Session lifecycle | Scripted ACP |
| `LODY-ROLE-001` | Create an Agent Role and freeze its execution target into a Session | Agent Role settings and composer selection | Real window and workspace RPC | Frozen dispatch configuration | Role catalog plus Session provenance | Scripted ACP |
| `LODY-SESSION-002` | Rename, pin, archive, and restore a local Session | Session metadata and Archive UI | Real window and Session RPC | Real owned runtime | UI-created history, title, pin, archive, and restore | Scripted ACP |
| `LODY-SESSION-003` | Mark a Session unread and clear the state by opening it | Session unread indicator, context menu, and sidebar navigation | Real window and Session RPC | Real owned runtime and Session histories | Two Sessions plus unread and read receipt transitions | Scripted ACP |
| `LODY-SETTINGS-001` | Cancel a theme preview without replacing the committed appearance | Appearance selector and live theme root | Real Electron window | Real owned runtime bootstrap | Committed theme in isolated local storage | None |
| Stable id | Journey | Renderer | Electron / IPC | Bundled CLI | Durable state | External wire |
| ------------------- | ----------------------------------------------------------------------------------- | --------------------------------------------------------------------- | -------------------------------------------------- | ---------------------------------------------------- | ----------------------------------------------------------------- | ------------------------------------ |
| `LODY-FORK-001` | Fork a completed Session into an independent worktree Session | Session fork destination and origin | Real window and Session RPC | Fork operation and recovery markers | Independent Session history and worktree | Scripted ACP |
| `LODY-MCP-001` | Create a workspace MCP server and preserve explicit turn selection through dispatch | MCP settings and composer selection | Real window and workspace RPC | Catalog persistence and ACP dispatch | Workspace catalog plus turn input selection | Synthetic stdio MCP and scripted ACP |
| `LODY-MCP-002` | Edit, disable, re-enable, and delete a workspace MCP catalog entry | MCP catalog editor and enabled-state switch | Real window and workspace RPC | Workspace MCP catalog create, update, and delete | MCP fields and enabled state across settings reopen | None |
| `LODY-PROJECT-001` | Add, select, and remove a local project without deleting its directory | Project picker, sidebar selection, and removal confirmation | Real window and local project control bridge | Project catalog add, list, and delete | Workspace project catalog lifecycle | None |
| `LODY-PROJECT-002` | Switch between local projects without duplicating a reopened folder | Project picker and sidebar switching | Real window and local project control bridge | Project catalog identity and duplicate prevention | Two unique local project records | None |
| `LODY-QUEUE-001` | Remove one queued follow-up and dispatch only the retained message | Queue controls and pending-message list | Real window and Session RPC | Queue persistence and ordered dispatch | Pending Turn queue removal and drain | File-signaled scripted ACP |
| `LODY-REVIEW-001` | Open, hide, and switch a synthetic large diff | Large diff Review lifecycle | Real window and diff RPC | Real owned runtime | Synthetic project and Session lifecycle | Scripted ACP |
| `LODY-ROLE-001` | Create an Agent Role and freeze its execution target into a Session | Agent Role settings and composer selection | Real window and workspace RPC | Frozen dispatch configuration | Role catalog plus Session provenance | Scripted ACP |
| `LODY-SESSION-002` | Rename, pin, archive, and restore a local Session | Session metadata and Archive UI | Real window and Session RPC | Real owned runtime | UI-created history, title, pin, archive, and restore | Scripted ACP |
| `LODY-SESSION-003` | Mark a Session unread and clear the state by opening it | Session unread indicator, context menu, and sidebar navigation | Real window and Session RPC | Real owned runtime and Session histories | Two Sessions plus unread and read receipt transitions | Scripted ACP |
| `LODY-SESSION-004` | Preserve opened worktree Sessions through opener archive and delete | Session archive, Archive delete, child Tabs, and opened-by provenance | Real window, local repo, terminal, and Session RPC | Real archive and worktree cleanup command processing | Containment deletion, provenance retention, and worktree survival | Scripted fork-capable ACP |
| `LODY-SETTINGS-001` | Cancel a theme preview without replacing the committed appearance | Appearance selector and live theme root | Real Electron window | Real owned runtime bootstrap | Committed theme in isolated local storage | None |

## Evidence-backed backlog

Expand Down
54 changes: 54 additions & 0 deletions e2e/journeys/registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,60 @@
"blockedReason": null,
"estimatedMinutes": 4
},
{
"id": "LODY-SESSION-004",
"state": "active",
"priority": "P1",
"runtime": "simulator",
"title": "Preserve opened worktree Sessions through opener archive and delete",
"owner": "session-lifecycle",
"feature": "src/features/session-management.feature",
"fixture": "session-fork-and-seeded-relations",
"fingerprint": "bf5df94ba0e024a2ac8e9c34b0bd8879e821e67337be85bc01732979e8e05b87",
"ownerPaths": [
"packages/components/src/hooks/use-session-actions.ts",
"packages/components/src/lib/session-navigation.ts",
"packages/components/src/components/shared/session-relation-card.tsx"
],
"actions": [
{ "id": "session.createCompletedSource" },
{ "id": "session.forkWorktree", "args": { "count": 2 } },
{ "id": "session.seedRelations" },
{ "id": "session.archiveOpener" },
{ "id": "archive.deletePermanently" },
{ "id": "session.openSurvivors" },
{ "id": "session.reloadWithMetadataScanBlocked" },
{ "id": "session.closeEmptyChildTab" }
],
"checkpoints": [
"archive affects only the root and direct child Tab",
"permanent delete removes only the root and direct child Tab",
"opened Sessions retain documents, provenance, running ACP processes, and worktrees",
"dangling opener provenance is visible and non-navigable",
"exact child deletion succeeds before metadata hydration completes"
],
"cleanup": [
"opened worktree Sessions and ACP processes are permanently deleted",
"isolated app and Git fixture directories are removed"
],
"coverage": {
"renderer": "Session archive, Archive delete, child Tabs, and opened-by provenance",
"electronIpc": "Real window, local repo, terminal, and Session RPC",
"bundledCli": "Real archive and worktree cleanup command processing",
"durableState": "Containment deletion, provenance retention, and worktree survival",
"externalWire": "Scripted fork-capable ACP"
},
"signals": {
"criticality": 5,
"boundaryRisk": 5,
"changeFrequency": 3,
"escapedDefect": true
},
"freshness": 5,
"scoutJourneys": ["session", "work"],
"blockedReason": null,
"estimatedMinutes": 6
},
{
"id": "LODY-PROJECT-001",
"state": "active",
Expand Down
4 changes: 2 additions & 2 deletions e2e/src/features/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Feature index

The active suite contains 15 scenarios: 4 `@P0` smoke journeys and 11 `@P1` deeper journeys.
The active suite contains 16 scenarios: 4 `@P0` smoke journeys and 12 `@P1` deeper journeys.

| Feature | Scope |
| ----------------------------- | --------------------------------------------------------------------------------- |
Expand All @@ -12,7 +12,7 @@ The active suite contains 15 scenarios: 4 `@P0` smoke journeys and 11 `@P1` deep
| `project-lifecycle.feature` | Local project add, selection, removal, and original-directory safety |
| `project-reopen.feature` | Cross-surface project switching and duplicate-folder identity preservation |
| `session-queue.feature` | Queued follow-up removal and ordered dispatch through a scripted ACP |
| `session-management.feature` | Session rename, pin, archive, restore, history, and permanent deletion |
| `session-management.feature` | Session metadata plus containment/provenance archive and deletion isolation |
| `session-read-state.feature` | Session unread marking, navigation-based read clearing, and permanent deletion |
| `session-fork.feature` | Completed Session fork, origin, independent worktree, and deletion isolation |
| `settings-appearance.feature` | Theme commit, preview isolation, cancellation, and settings-reopen persistence |
Expand Down
10 changes: 9 additions & 1 deletion e2e/src/features/session-management.feature
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# language: zh-CN
@lody @essence @P1 @runtime-simulator @LODY-SESSION-002
功能: 本地 Session 元数据与归档恢复

@lody @essence @P1 @runtime-simulator @LODY-SESSION-002
场景: 用户管理本地 Session 并保留其历史
假如 用户已在隔离桌面配置确定性 Agent
并且 用户从 New chat 创建了包含真实历史的 Session
Expand All @@ -11,3 +11,11 @@
那么 恢复后的 Session 保留标题、置顶状态和历史
当 用户永久删除恢复后的 Session
那么 Session 已从活动列表和 Archive 中清理

@lody @essence @P1 @runtime-simulator @LODY-SESSION-004
场景: opener 删除不销毁独立 opened Session
假如 已配置支持分叉的确定性 Agent 桌面
并且 已建立含 child Tab 和两个独立 worktree 的 Session 关系
当 用户归档并永久删除 opener Session
那么 child Tab 被删除而 opened Sessions 和 worktree 保留
并且 metadata 未完成 hydration 时精确删除 empty child Tab 仍成功
31 changes: 31 additions & 0 deletions e2e/src/steps/session-management.steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,34 @@ When('用户永久删除恢复后的 Session', async function (this: LodyWorld)
Then('Session 已从活动列表和 Archive 中清理', async function (this: LodyWorld) {
await this.sessionManagementPage!.expectDeletedFromLists();
});

Given('已建立含 child Tab 和两个独立 worktree 的 Session 关系', async function (this: LodyWorld) {
await this.sessionForkPage!.addProjectAndSelectAgent();
const firstFork = await this.sessionForkPage!.createCompletedSourceAndForkToWorktree();
const secondFork = await this.sessionForkPage!.createAdditionalWorktreeFork(firstFork);
this.sessionRelationLifecycleResources =
await this.sessionRelationLifecyclePage!.seedRelationLifecycle(firstFork, secondFork);
});

When('用户归档并永久删除 opener Session', async function (this: LodyWorld) {
await this.sessionRelationLifecyclePage!.archiveRelationRoot(
this.sessionRelationLifecycleResources!
);
await this.sessionRelationLifecyclePage!.permanentlyDeleteRelationRoot(
this.sessionRelationLifecycleResources!
);
});

Then('child Tab 被删除而 opened Sessions 和 worktree 保留', async function (this: LodyWorld) {
await this.sessionRelationLifecyclePage!.expectDanglingProvenanceAndCleanup(
this.sessionRelationLifecycleResources!
);
});

Then(
'metadata 未完成 hydration 时精确删除 empty child Tab 仍成功',
async function (this: LodyWorld) {
await this.sessionRelationLifecyclePage!.expectColdHydrationExactDelete();
await this.harness!.capturePostGcSnapshot();
}
);
Loading
Loading