Skip to content

feat(mutils): Add workspace-id persistence across Auto Compact#20

Merged
masseater merged 2 commits intomasterfrom
feature/workspace-id-persistence
Mar 3, 2026
Merged

feat(mutils): Add workspace-id persistence across Auto Compact#20
masseater merged 2 commits intomasterfrom
feature/workspace-id-persistence

Conversation

@masseater
Copy link
Owner

@masseater masseater commented Mar 3, 2026

Summary

  • Rename session-id to workspace-id to avoid confusion with Claude Code's internal session_id
  • Add DB persistence in generate.ts for workspace-id (saves to .agents/mutils/mutils.db)
  • Add workspace-id-persist.ts hook for SessionStart (compact/resume) to restore workspace-id via additionalContext
  • Update ccs-handoff to use workspace-id terminology
  • Update project-context.md with workspace-id terminology

Changes

Architecture Simplification

  • Removed PostToolUse hook - Instead of capturing output via hook, generate.ts now saves directly to DB
  • Renamed session-id → workspace-id - Clearer naming to avoid confusion with Claude Code's internal session_id

New Files

  • plugins/mutils/skills/workspace-id/generate.ts - Generates and persists workspace-id
  • plugins/mutils/skills/workspace-id/patterns.ts - Shared regex patterns
  • plugins/mutils/skills/workspace-id/SKILL.md - Skill documentation
  • plugins/mutils/hooks/workspace-id-persist.ts - SessionStart hook for restoration

Modified Files

  • plugins/mutils/hooks/hooks.json - Updated hooks configuration
  • plugins/mutils/skills/ccs-handoff/ccs-handoff.ts - Updated to use workspace-id
  • .claude/rules/ai-generated/project-context.md - Updated terminology

Test plan

  • Run bun run check in plugins/mutils
  • Run bun run typecheck in plugins/mutils
  • Test workspace-id generation: ./plugins/mutils/skills/workspace-id/generate.ts test-feature
  • Verify DB entry created in .agents/mutils/mutils.db
  • Test restoration after compact (manual)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Replaced session-based tracking with a workspace-id system for project organization.
    • Added a workspace-id generator and automatic workspace mapping persistence/restoration on startup.
  • Documentation

    • Added a comprehensive workspace-id spec with format rules, examples, and directory conventions.
  • Chores

    • Bumped plugin version to 0.14.0.

- Rename session-id to workspace-id to avoid confusion with Claude Code's internal session_id
- Add DB persistence in generate.ts for workspace-id
- Add workspace-id-persist.ts hook for SessionStart (compact/resume)
- Update ccs-handoff to use workspace-id
- Update project-context.md with workspace-id terminology

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 3, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cad7f95 and 6d39c17.

📒 Files selected for processing (1)
  • plugins/mutils/hooks/session-id-persist.ts
💤 Files with no reviewable changes (1)
  • plugins/mutils/hooks/session-id-persist.ts

📝 Walkthrough

Walkthrough

Migrates mutils from session-id to workspace-id: adds workspace-id spec, validation, generator, and persistence hook; replaces session paths/usages with workspace equivalents; removes session-id hook; and bumps plugin version to 0.14.0.

Changes

Cohort / File(s) Summary
Version Management
/.claude-plugin/marketplace.json, plugins/mutils/plugin.json
Bumped mutils plugin version from 0.13.0 to 0.14.0.
Workspace-ID Specification & Tools
plugins/mutils/skills/workspace-id/SKILL.md, plugins/mutils/skills/workspace-id/patterns.ts, plugins/mutils/skills/workspace-id/generate.ts
Adds canonical workspace-id spec, regex validation/parsing helpers, and a CLI generator that creates workspace-ids and upserts mappings into MutilsDB.
Hook System Migration
plugins/mutils/hooks/hooks.json, plugins/mutils/hooks/workspace-id-persist.ts
Rewired SessionStart to use workspace-id-persist.ts; removed the PostToolUse session-id persistence hook. New hook restores latest workspace mapping (from DB) and returns JSON context with .agents/workspaces/{workspace_id}/.
Removed Session Hook
plugins/mutils/hooks/session-id-persist.ts
Deleted: previous session-id persistence/restore hook (DB schema, save/get mapping, PostToolUse generate.ts handling, and SessionStart restore logic) was removed.
Code Migration
plugins/mutils/skills/ccs-handoff/ccs-handoff.ts
Replaced session-id parsing/storage paths with workspace-id equivalents (parseWorkspaceId, .agents/workspaces/*), and renamed identifiers accordingly.
Rules / Docs
.claude/rules/ai-generated/project-context.md
Replaced session-id terminology and paths with workspace-id equivalents throughout examples, headers, and spec notes; clarified workspace-id scope relative to rounds/structure.

Sequence Diagram(s)

sequenceDiagram
  participant Claude as Claude (agent)
  participant Hook as workspace-id-persist.ts
  participant DB as MutilsDB
  participant FS as Git + .agents

  rect rgba(200,230,255,0.5)
  Claude->>Hook: SessionStart event (source: compact/resume)
  end

  Hook->>FS: locate Git root and check `.agents` exists
  FS-->>Hook: .agents present
  Hook->>DB: query latest workspace_mappings
  DB-->>Hook: returns latest mapping (workspace_id)
  Hook->>Claude: respond with JSON context including `.agents/workspaces/{workspace_id}/`
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I dug new paths where IDs now grow,

yyyymmdd-HHmm — a tidy row,
From generator's hum to DB's keep,
Workspaces wake from restful sleep,
Hop onward — maps make projects glow! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(mutils): Add workspace-id persistence across Auto Compact' directly describes the main change: adding workspace-id persistence functionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/workspace-id-persistence

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

The hook was renamed to workspace-id-persist.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
plugins/mutils/skills/ccs-handoff/ccs-handoff.ts (1)

457-493: ⚠️ Potential issue | 🟠 Major

Complete the workspace-id migration in handoff output.

Extraction now reads from .agents/workspaces, but the markdown still instructs session-id semantics (e.g., Line 592 uses .agents/sessions/...). This will guide users to the wrong path.

🔧 Proposed fix
-interface UserSessionId {
+interface UserWorkspaceId {
 	fullId: string;
 	featureName: string;
 }

-function extractUserSessionId(projectPath: string): UserSessionId | null {
+function extractUserWorkspaceId(projectPath: string): UserWorkspaceId | null {
 	const workspacesDir = path.join(projectPath, ".agents", "workspaces");
 	// ...
 }

 function formatMarkdown(
 	info: SessionInfo,
 	messages: ParsedMessage[],
 	jsonlSummary: string | null,
 	tasks: PendingTask[],
 	todos: PendingTodo[],
 	hasSubagents: boolean,
 	planFiles: string[],
-	userSessionId: UserSessionId | null,
+	userWorkspaceId: UserWorkspaceId | null,
 ): string {
 	// ...
-	lines.push("## User Session ID");
+	lines.push("## Workspace ID");
 	lines.push("");
-	if (userSessionId) {
-		lines.push(`**Session ID**: \`${userSessionId.fullId}\``);
+	if (userWorkspaceId) {
+		lines.push(`**Workspace ID**: \`${userWorkspaceId.fullId}\``);
 		lines.push("");
-		lines.push(`**Feature Name**: ${userSessionId.featureName}`);
+		lines.push(`**Feature Name**: ${userWorkspaceId.featureName}`);
 		lines.push("");
-		lines.push("To continue using this session-id, create the session directory:");
+		lines.push("To continue, use this workspace directory:");
 		lines.push("");
 		lines.push("```bash");
-		lines.push(`mkdir -p .agents/sessions/${userSessionId.fullId}`);
+		lines.push(`mkdir -p .agents/workspaces/${userWorkspaceId.fullId}`);
 		lines.push("```");
 	} else {
-		lines.push("No user session-id found in .agents/sessions/");
+		lines.push("No workspace-id found in .agents/workspaces/");
 	}
 }

-const userSessionId = sessionInfo.projectPath
-	? extractUserSessionId(sessionInfo.projectPath)
+const userWorkspaceId = sessionInfo.projectPath
+	? extractUserWorkspaceId(sessionInfo.projectPath)
 	: null;

 // ...
-	userSessionId,
+	userWorkspaceId,

Also applies to: 579-596, 687-700

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/mutils/skills/ccs-handoff/ccs-handoff.ts` around lines 457 - 493, The
handoff output still references ".agents/sessions" and uses names like
userSessionId even though extraction now reads from ".agents/workspaces"; update
the function and call-sites to reflect workspace semantics: rename or duplicate
extractUserSessionId to extractUserWorkspaceId (and update its internal behavior
if needed) and replace all uses of userSessionId with userWorkspaceId; change
any generated markdown lines that do mkdir -p .agents/sessions/${...} to mkdir
-p .agents/workspaces/${userWorkspaceId.fullId} and update the "No user
session-id found" message to "No workspace-id found in .agents/workspaces/";
apply these renames/strings consistently for the other occurrences mentioned
(the later handoff output blocks) and ensure parseWorkspaceId and returned
fields (fullId, featureName) are used with the new variable names.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@plugins/mutils/skills/workspace-id/generate.ts`:
- Around line 31-33: The code currently silently skips persistence when the
.agents directory is missing or a DB write fails (in the block guarded by
gitRoot and agentsDir in generate.ts), so update the logic in the function that
generates the workspace id to detect and surface these failures: if
existsSync(agentsDir) is false, log an explicit error or throw (using the
existing logger like processLogger or by rejecting the promise) instead of
silently continuing; likewise wrap the DB write/save call (the persistence
routine inside the 35-60 region) with try/catch and on any error log the
detailed error and propagate it (throw or return a non-success result) so
generation does not report success while persistence failed. Ensure you
reference and update the agentsDir check and the DB write/save method so callers
can handle failure and restoration will not fail silently.

---

Outside diff comments:
In `@plugins/mutils/skills/ccs-handoff/ccs-handoff.ts`:
- Around line 457-493: The handoff output still references ".agents/sessions"
and uses names like userSessionId even though extraction now reads from
".agents/workspaces"; update the function and call-sites to reflect workspace
semantics: rename or duplicate extractUserSessionId to extractUserWorkspaceId
(and update its internal behavior if needed) and replace all uses of
userSessionId with userWorkspaceId; change any generated markdown lines that do
mkdir -p .agents/sessions/${...} to mkdir -p
.agents/workspaces/${userWorkspaceId.fullId} and update the "No user session-id
found" message to "No workspace-id found in .agents/workspaces/"; apply these
renames/strings consistently for the other occurrences mentioned (the later
handoff output blocks) and ensure parseWorkspaceId and returned fields (fullId,
featureName) are used with the new variable names.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7d962e6 and cad7f95.

📒 Files selected for processing (9)
  • .claude-plugin/marketplace.json
  • .claude/rules/ai-generated/project-context.md
  • plugins/mutils/hooks/hooks.json
  • plugins/mutils/hooks/workspace-id-persist.ts
  • plugins/mutils/plugin.json
  • plugins/mutils/skills/ccs-handoff/ccs-handoff.ts
  • plugins/mutils/skills/workspace-id/SKILL.md
  • plugins/mutils/skills/workspace-id/generate.ts
  • plugins/mutils/skills/workspace-id/patterns.ts

Comment on lines +31 to +33
if (gitRoot) {
const agentsDir = path.join(gitRoot, ".agents");
if (existsSync(agentsDir)) {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid silent persistence failure paths.

If .agents is missing or DB write fails, persistence can be skipped without any signal. That makes restore behavior fail silently even though generation “succeeds”.

🔧 Proposed fix
-import { existsSync } from "node:fs";
+import { mkdirSync } from "node:fs";
 import path from "node:path";
 import { findGitRoot, MutilsDB } from "@r_masseater/cc-plugin-lib";
 import { isValidFeatureName } from "./patterns.js";

 // ...

 if (gitRoot) {
-	const agentsDir = path.join(gitRoot, ".agents");
-	if (existsSync(agentsDir)) {
-		try {
-			using db = MutilsDB.open(gitRoot);
-			const table = db.table<{
-				workspace_id: string;
-				feature_name: string;
-				created_at: string;
-			}>("workspace_mappings", {
-				columns: `
-						id INTEGER PRIMARY KEY AUTOINCREMENT,
-						workspace_id TEXT NOT NULL UNIQUE,
-						feature_name TEXT NOT NULL,
-						created_at TEXT NOT NULL
-					`,
-				indexes: [
-					"CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_mappings_workspace_id ON workspace_mappings(workspace_id)",
-				],
-			});
-
-			const timestamp = now.toISOString();
-			table.upsert({
-				workspace_id: workspaceId,
-				feature_name: featureName,
-				created_at: timestamp,
-			});
-		} catch {
-			// Fail silently - workspace-id generation should still work
-		}
-	}
+	try {
+		mkdirSync(path.join(gitRoot, ".agents", "mutils"), { recursive: true });
+		using db = MutilsDB.open(gitRoot);
+		const table = db.table<{
+			workspace_id: string;
+			feature_name: string;
+			created_at: string;
+		}>("workspace_mappings", {
+			columns: `
+					id INTEGER PRIMARY KEY AUTOINCREMENT,
+					workspace_id TEXT NOT NULL UNIQUE,
+					feature_name TEXT NOT NULL,
+					created_at TEXT NOT NULL
+				`,
+			indexes: [
+				"CREATE UNIQUE INDEX IF NOT EXISTS idx_workspace_mappings_workspace_id ON workspace_mappings(workspace_id)",
+			],
+		});
+
+		const timestamp = now.toISOString();
+		table.upsert({
+			workspace_id: workspaceId,
+			feature_name: featureName,
+			created_at: timestamp,
+		});
+	} catch (error) {
+		process.stderr.write(
+			`Warning: failed to persist workspace-id: ${error instanceof Error ? error.message : String(error)}\n`,
+		);
+	}
 }

Also applies to: 35-60

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/mutils/skills/workspace-id/generate.ts` around lines 31 - 33, The
code currently silently skips persistence when the .agents directory is missing
or a DB write fails (in the block guarded by gitRoot and agentsDir in
generate.ts), so update the logic in the function that generates the workspace
id to detect and surface these failures: if existsSync(agentsDir) is false, log
an explicit error or throw (using the existing logger like processLogger or by
rejecting the promise) instead of silently continuing; likewise wrap the DB
write/save call (the persistence routine inside the 35-60 region) with try/catch
and on any error log the detailed error and propagate it (throw or return a
non-success result) so generation does not report success while persistence
failed. Ensure you reference and update the agentsDir check and the DB
write/save method so callers can handle failure and restoration will not fail
silently.

@masseater masseater merged commit 5f63dfb into master Mar 3, 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.

1 participant