Skip to content

refactor(workflow): move orchestration to the CLI - #167

Closed
Waishnav wants to merge 4 commits into
codex/dw-cli-contract-v3from
codex/dw-cli-orchestration-v3
Closed

refactor(workflow): move orchestration to the CLI#167
Waishnav wants to merge 4 commits into
codex/dw-cli-contract-v3from
codex/dw-cli-orchestration-v3

Conversation

@Waishnav

@Waishnav Waishnav commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Long-running orchestration should not depend on an MCP request timeout or session lifetime. Workflow run, status, cancel, list, and call inspection now use validated CLI options and workspace-scoped state; agent commands clean up their stores and detached prompt handoffs. MCP no longer registers separate workflow execution tools, so every host uses the same CLI lifecycle.

Summary by CodeRabbit

  • New Features
    • Workflow commands now support workspace-scoped launches, listings, and run management.
    • Workflow files and run records are restricted to the selected workspace.
  • Bug Fixes
    • Invalid command-line options now produce clear errors instead of being treated as prompt text.
    • Agent execution now handles failures more reliably and cleans up generated prompt files.
    • Agent commands are available when subagents or workflows are configured.
  • Security
    • Workflow commands validate run ownership and prevent access outside the active workspace.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR scopes workflow commands to the active workspace, rejects unknown CLI options, hardens agent store and prompt-file cleanup, enables agents for workflows, and removes server-side workflow tool registration.

Changes

CLI hardening

Layer / File(s) Summary
Workspace-scoped workflow commands
src/workflow-cli.ts
Workflow launches and run operations validate flags, constrain file paths, resolve workspace scope, and reject runs outside that scope.
Agent lifecycle and generated prompt cleanup
src/cli.ts
Agent commands support workflows, protect store access, handle lookup failures, and remove validated generated prompt files.
Argument validation and workflow tool wiring
src/local-agent-targets.ts, src/local-agent-targets.test.ts, src/server.ts
Local-agent parsing rejects unknown options with usage text. The server no longer registers workflow tools.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Operator
  participant WorkflowCLI
  participant WorkspaceScopeResolver
  participant WorkflowStore
  Operator->>WorkflowCLI: run or inspect workflow
  WorkflowCLI->>WorkspaceScopeResolver: resolve workspace scope
  WorkflowCLI->>WorkflowStore: retrieve workflow run
  WorkflowCLI->>WorkflowStore: validate workspace ownership
  WorkflowStore-->>WorkflowCLI: return scoped result
  WorkflowCLI-->>Operator: display status or operation result
Loading

Possibly related PRs

Poem

A rabbit checks each flag with care,
And keeps each workflow in its lair.
Stores close, prompt files flee,
Scoped runs stay where they should be.
“Hop!” says the CLI, clean and bright.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: moving workflow orchestration from MCP request handling to the CLI.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/dw-cli-orchestration-v3

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.

@Waishnav Waishnav changed the title codex/dw cli orchestration v3 refactor(workflow): move orchestration to the CLI Aug 9, 2026
@greptile-apps

greptile-apps Bot commented Aug 9, 2026

Copy link
Copy Markdown

Greptile Summary

This PR tightens workspace isolation and argument validation for workflow CLI operations, improves local-agent store and prompt-file cleanup, and removes workflow tools from the MCP server.

  • Resolves workflow launches and lookups against the current CLI workspace.
  • Rejects unknown local-agent and workflow options.
  • Closes local-agent stores consistently and deletes generated prompt files.
  • Removes MCP workflow execution and UI tool registration.

Confidence Score: 4/5

The workflow MCP surface must be restored or its remaining advertised contracts removed before merging; the temporary-directory leak is non-blocking.

The server no longer invokes the only workflow-tool registrar, so workflow-enabled MCP clients cannot execute or monitor workflows, while each agent run also leaves an empty generated prompt directory behind.

Files Needing Attention: src/server.ts, src/cli.ts

Important Files Changed

Filename Overview
src/server.ts Removes the sole registration path for the documented workflow MCP and UI tools.
src/workflow-cli.ts Adds workspace scoping, path containment, stale-run reaping, and unknown-option validation to workflow CLI operations.
src/cli.ts Allows agent tooling for workflow-enabled configurations and closes stores reliably, but leaves generated temporary directories behind.
src/local-agent-targets.ts Rejects unknown double-dash options instead of silently incorporating them into agent prompts.
src/local-agent-targets.test.ts Adds coverage for rejecting unknown local-agent run options.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Workflow-enabled MCP client] --> B[createMcpServer]
  B --> C[MCP server returned]
  C -. registration removed .-> D[run_workflow / status / cancel absent]
  D --> E[Unknown-tool failure]
  F[devspace workflow CLI] --> G[Resolve current workspace scope]
  G --> H[Launch or inspect scoped workflow run]
Loading

Reviews (1): Last reviewed commit: "refactor(mcp): remove workflow execution..." | Re-trigger Greptile

Comment thread src/server.ts
@@ -47,7 +47,6 @@ import { formatPathForPrompt } from "./skills.js";
import { createWorkspaceStore } from "./workspace-store.js";
import { formatAgentsPath, WorkspaceRegistry } from "./workspaces.js";
import { buildLocalAgentCatalog } from "./local-agent-catalog.js";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Workflow MCP tools are unregistered

When Dynamic Workflows are enabled, createMcpServer no longer calls the sole registerWorkflowTools registration function, so MCP calls to workflow execution, status, cancellation, and UI tools fail as unknown tools.

Comment thread src/cli.ts
Comment on lines +570 to +572
});
}
} finally {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Prompt directories remain after cleanup

The worker unlinks prompt.txt but leaves the unique directory created by mkdtempSync, so repeated agent runs permanently accumulate empty devspace-agent-prompt-* directories in the system temporary directory.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@Waishnav
Waishnav force-pushed the codex/dw-cli-orchestration-v3 branch from 7b38089 to e604f10 Compare August 9, 2026 11:53
@Waishnav
Waishnav force-pushed the codex/dw-cli-orchestration-v3 branch from e604f10 to 3020b71 Compare August 9, 2026 11:55
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

@Waishnav, I will perform a complete review of pull request #167.

⚠️ Action not completed

Review rate limited.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 57 minutes.

@Waishnav

Waishnav commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

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

⚠️ Outside diff range comments (1)
src/workflow-cli.ts (1)

109-115: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject unused positional arguments.

These commands parse the documented arguments but ignore trailing positional tokens. Validate exact positional arity so invalid invocations do not run a different command than the user requested.

  • src/workflow-cli.ts#L109-L115: reject all positional tokens for workflow run.
  • src/workflow-cli.ts#L195-L198: reject positionals after <runId>.
  • src/workflow-cli.ts#L228-L230: reject positionals after <runId>.
  • src/workflow-cli.ts#L266-L268: reject positionals after <runId>.
  • src/workflow-cli.ts#L292-L295: reject positionals after <runId> <callIndex>.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/workflow-cli.ts` around lines 109 - 115, Validate exact positional
argument arity in src/workflow-cli.ts: for workflow run at lines 109-115 reject
all positional tokens; at lines 195-198, 228-230, and 266-268 reject any tokens
after <runId>; and at lines 292-295 reject any tokens after <runId> <callIndex>.
Apply the checks in the corresponding command handlers before execution so
trailing positionals cannot be ignored.
🧹 Nitpick comments (1)
src/local-agent-targets.test.ts (1)

84-88: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the usage guidance in the error contract.

The parser appends USAGE to unknown-option errors, but this test checks only Unknown option: --unknown. Extend the assertion to verify Usage: devspace agents run or assert the complete error message.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/local-agent-targets.test.ts` around lines 84 - 88, Update the
assert.throws expectation for parseLocalAgentRunArgs to include the appended
usage guidance, specifically matching “Usage: devspace agents run” or the
complete expected error message alongside the unknown-option text.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/cli.ts`:
- Around line 573-576: Update the cleanup block after isGeneratedPromptFile to
remove the temporary directory created by writeAgentPromptFile after unlinking
promptFile. Preserve the existing best-effort cleanup behavior and ensure the
generated directory is removed only after the prompt file cleanup completes.
- Around line 536-540: Update runAgentsWorker after retrieving the record and
before store.update to call assertAgentInScope with the record and
resolveCurrentWorkspaceScope(config). Preserve workspace scoping for all
operations, using the workspaceId returned by open_workspace as the opaque scope
handle.
- Around line 78-84: Wrap the `agents` switch case body containing the `config`
declaration and `runAgentsCommand(args)` in braces, ensuring `config` is scoped
only to that case while preserving the existing validation and command
execution.

In `@src/local-agent-targets.ts`:
- Around line 67-69: Update the option-value parsing branches in the local-agent
argument parser so model, effort, and thinking values beginning with “--” are
rejected before assignment, for both separated and equals forms. Preserve valid
value handling, and add regression tests covering each affected form, including
parseLocalAgentRunArgs with separated model and effort arguments.

In `@src/workflow-cli.ts`:
- Around line 485-493: Update resolveWorkflowFilePath to canonicalize both the
resolved workflow path and workspaceRoot before the containment check, while
retaining the existing lexical isPathInsideRoot validation. Enforce canonical
containment before returning the path or launching the workflow, and throw
InvalidWorkflowInputError with code "invalid_path" when either check fails.
- Around line 496-512: Update src/workflow-cli.ts lines 496-512 in
assertWorkflowInScope to require an exact workspaceId match whenever the active
scope provides one, rejecting runs with missing or different IDs. Update
src/workflow-cli.ts lines 253-254 to filter the store query by workspaceId
before applying the result limit, and define an explicit migration or
compatibility path for ID-less records; all workflow operations must remain
scoped by the opaque ID returned from open_workspace.

---

Outside diff comments:
In `@src/workflow-cli.ts`:
- Around line 109-115: Validate exact positional argument arity in
src/workflow-cli.ts: for workflow run at lines 109-115 reject all positional
tokens; at lines 195-198, 228-230, and 266-268 reject any tokens after <runId>;
and at lines 292-295 reject any tokens after <runId> <callIndex>. Apply the
checks in the corresponding command handlers before execution so trailing
positionals cannot be ignored.

---

Nitpick comments:
In `@src/local-agent-targets.test.ts`:
- Around line 84-88: Update the assert.throws expectation for
parseLocalAgentRunArgs to include the appended usage guidance, specifically
matching “Usage: devspace agents run” or the complete expected error message
alongside the unknown-option text.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a7e237d2-039d-4865-9d5b-022f30f59c00

📥 Commits

Reviewing files that changed from the base of the PR and between d2f1562 and 3020b71.

📒 Files selected for processing (5)
  • src/cli.ts
  • src/local-agent-targets.test.ts
  • src/local-agent-targets.ts
  • src/server.ts
  • src/workflow-cli.ts
💤 Files with no reviewable changes (1)
  • src/server.ts

Comment thread src/cli.ts
Comment on lines +78 to 84
const config = loadConfig();
if (!config.subagents && !config.workflows) {
throw new Error(
"Subagents are disabled. Set DEVSPACE_SUBAGENTS=1 to enable the experimental feature.",
"Subagents and Dynamic Workflows are disabled. Set DEVSPACE_SUBAGENTS=1 or DEVSPACE_WORKFLOWS=1 to enable agent tooling.",
);
}
await runAgentsCommand(args);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Scope the agents case declaration with braces.

Biome reports noSwitchDeclarations for config. Wrap this case body in braces so its declaration cannot be visible to other switch clauses.

Proposed fix
-    case "agents":
+    case "agents": {
       const config = loadConfig();
       if (!config.subagents && !config.workflows) {
         throw new Error(
           "Subagents and Dynamic Workflows are disabled. Set DEVSPACE_SUBAGENTS=1 or DEVSPACE_WORKFLOWS=1 to enable agent tooling.",
         );
       }
       await runAgentsCommand(args);
       return;
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const config = loadConfig();
if (!config.subagents && !config.workflows) {
throw new Error(
"Subagents are disabled. Set DEVSPACE_SUBAGENTS=1 to enable the experimental feature.",
"Subagents and Dynamic Workflows are disabled. Set DEVSPACE_SUBAGENTS=1 or DEVSPACE_WORKFLOWS=1 to enable agent tooling.",
);
}
await runAgentsCommand(args);
case "agents": {
const config = loadConfig();
if (!config.subagents && !config.workflows) {
throw new Error(
"Subagents and Dynamic Workflows are disabled. Set DEVSPACE_SUBAGENTS=1 or DEVSPACE_WORKFLOWS=1 to enable agent tooling.",
);
}
await runAgentsCommand(args);
return;
}
🧰 Tools
🪛 Biome (2.5.6)

[error] 78-78: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

(lint/correctness/noSwitchDeclarations)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli.ts` around lines 78 - 84, Wrap the `agents` switch case body
containing the `config` declaration and `runAgentsCommand(args)` in braces,
ensuring `config` is scoped only to that case while preserving the existing
validation and command execution.

Source: Linters/SAST tools

Comment thread src/cli.ts
Comment on lines +536 to 540
const record = store.get(id);
if (!record) throw new Error(`Unknown subagent id: ${id}`);

store.update(record.id, { status: "running", error: undefined });
const profiles = await loadLocalAgentProfiles(config, record.workspaceRoot);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Enforce workspace scope before starting the worker.

runAgentsWorker can load and execute any stored agent ID. A direct agents __worker invocation can therefore mutate and run an agent record from another workspace. Call assertAgentInScope(record, resolveCurrentWorkspaceScope(config)) before changing its status.

As per coding guidelines: treat every operation as workspace-scoped and use workspaceId as the opaque handle returned by open_workspace.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli.ts` around lines 536 - 540, Update runAgentsWorker after retrieving
the record and before store.update to call assertAgentInScope with the record
and resolveCurrentWorkspaceScope(config). Preserve workspace scoping for all
operations, using the workspaceId returned by open_workspace as the opaque scope
handle.

Source: Coding guidelines

Comment thread src/cli.ts
Comment on lines +573 to +576
if (isGeneratedPromptFile(promptFile)) {
await unlink(promptFile).catch(() => undefined);
}
store.close();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Remove the generated prompt directory after cleanup.

writeAgentPromptFile creates a unique temporary directory. This block removes only prompt.txt, so every worker leaves an empty devspace-agent-prompt-* directory behind. Remove the empty generated directory after unlinking the prompt file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli.ts` around lines 573 - 576, Update the cleanup block after
isGeneratedPromptFile to remove the temporary directory created by
writeAgentPromptFile after unlinking promptFile. Preserve the existing
best-effort cleanup behavior and ensure the generated directory is removed only
after the prompt file cleanup completes.

Comment on lines +67 to +69
if (part?.startsWith("--")) {
throw new Error(`Unknown option: ${part}\n${USAGE}`);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject option-like tokens before consuming option values.

The new check runs after known options parse their values. Therefore, parseLocalAgentRunArgs(["codex", "--model", "--unknown", "hello"]) stores "--unknown" as model, and --effort=--unknown stores it as effort.

If model and effort values cannot start with --, reject such values in every model, effort, and thinking value branch. Add regression tests for separated and = forms.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/local-agent-targets.ts` around lines 67 - 69, Update the option-value
parsing branches in the local-agent argument parser so model, effort, and
thinking values beginning with “--” are rejected before assignment, for both
separated and equals forms. Preserve valid value handling, and add regression
tests covering each affected form, including parseLocalAgentRunArgs with
separated model and effort arguments.

Comment thread src/workflow-cli.ts
Comment on lines +485 to +493
function resolveWorkflowFilePath(path: string, workspaceRoot: string): string {
const resolvedPath = resolve(workspaceRoot, path);
if (!isPathInsideRoot(resolvedPath, workspaceRoot)) {
throw new InvalidWorkflowInputError({
code: "invalid_path",
message: `Workflow file must be inside the workspace: ${workspaceRoot}`,
});
}
return resolvedPath;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Resolve symlinks before accepting a workflow file.

isPathInsideRoot only checks lexical paths. A symlink inside the workspace can point outside the workspace and still pass this check. Canonicalize both paths and enforce containment after canonicalization before launch.

Based on learnings: enforce lexical and canonical containment for workflow script paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/workflow-cli.ts` around lines 485 - 493, Update resolveWorkflowFilePath
to canonicalize both the resolved workflow path and workspaceRoot before the
containment check, while retaining the existing lexical isPathInsideRoot
validation. Enforce canonical containment before returning the path or launching
the workflow, and throw InvalidWorkflowInputError with code "invalid_path" when
either check fails.

Source: Learnings

Comment thread src/workflow-cli.ts
Comment on lines +496 to +512
function assertWorkflowInScope(
run: Pick<WorkflowRunRecord, "workspaceRoot" | "workspaceId">,
scope: { workspaceRoot: string; workspaceId?: string },
): void {
if (resolve(run.workspaceRoot) !== resolve(scope.workspaceRoot)) {
throw new InvalidWorkflowInputError({
code: "invalid_argument",
message: `Workflow run belongs to a different workspace: ${scope.workspaceRoot}`,
});
}
if (scope.workspaceId && run.workspaceId && run.workspaceId !== scope.workspaceId) {
throw new InvalidWorkflowInputError({
code: "invalid_argument",
message: `Workflow run belongs to a different workspaceId: ${scope.workspaceId}`,
});
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Apply workspaceId consistently to workflow ownership.

When the active scope has a workspaceId, Line 506 permits a run with no workspaceId. Line 254 also lists every run for the root without an ID filter. This exposes legacy or other-scope records within the same root.

  • src/workflow-cli.ts#L496-L512: reject a run unless its workspaceId exactly matches the active workspaceId when one is present.
  • src/workflow-cli.ts#L253-L254: extend the store query to filter by workspaceId before applying the result limit. Define an explicit migration or compatibility path for ID-less records.

As per coding guidelines: treat every operation as workspace-scoped and use workspaceId as the opaque handle returned by open_workspace.

📍 Affects 1 file
  • src/workflow-cli.ts#L496-L512 (this comment)
  • src/workflow-cli.ts#L253-L254
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/workflow-cli.ts` around lines 496 - 512, Update src/workflow-cli.ts lines
496-512 in assertWorkflowInScope to require an exact workspaceId match whenever
the active scope provides one, rejecting runs with missing or different IDs.
Update src/workflow-cli.ts lines 253-254 to filter the store query by
workspaceId before applying the result limit, and define an explicit migration
or compatibility path for ID-less records; all workflow operations must remain
scoped by the opaque ID returned from open_workspace.

Source: Coding guidelines

@Waishnav Waishnav closed this Aug 9, 2026
@Waishnav

Waishnav commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

Closing in favor of #142. The Sol implementation follows the CLI-only architecture through to completion: it removes the obsolete MCP workflow execution surface and its dependent UI/tooling instead of leaving the old contract partially alive. It also avoids the cross-workspace resume and secondary-entry-point issues found during review.

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