-`project-bundle-reconcile-smoke.ps1` compiles the targeted `project-bundle-reconcile-smoke.ts` service-level smoke with the local desktop TypeScript toolchain, runs it under Node, and verifies stale bundle project removal, shared `skills/` cleanup, shared `cron/` cleanup, manifest pruning, and empty-inventory cleanup without recreating a local fallback project; `pnpm smoke:bundle-reconcile`
-`project-bundle-reconcile-smoke.ps1` compiles the targeted `project-bundle-reconcile-smoke.ts` service-level smoke with the local desktop TypeScript toolchain, runs it under Node, and verifies stale bundle project removal, shared `skills/` cleanup, shared `cron/` cleanup, manifest pruning, and empty-inventory cleanup without recreating a local fallback project; `pnpm smoke:bundle-reconcile`
-`project-bundle-freshness-smoke.ps1` compiles the targeted `project-bundle-freshness-smoke.ts` service-level smoke with the local desktop TypeScript toolchain, runs it under Node, and verifies that the same bundle URL plus unchanged `configVersion` still re-syncs when remote `ETag` / `Last-Modified` freshness metadata changes; `pnpm smoke:bundle-freshness`
-`project-bundle-freshness-smoke.ps1` compiles the targeted `project-bundle-freshness-smoke.ts` service-level smoke with the local desktop TypeScript toolchain, runs it under Node, and verifies that the same bundle URL plus unchanged `configVersion` still re-syncs when remote `ETag` / `Last-Modified` freshness metadata changes; `pnpm smoke:bundle-freshness`
-`project-bundle-replacement-smoke.ps1` compiles the targeted `project-bundle-replacement-smoke.ts` service-level smoke with the local desktop TypeScript toolchain, runs it under Node, and verifies same-project replacement, shared `skills/` and `cron/` ownership cleanup, rollback on an injected post-commit failure, and successful recovery on the next sync; `pnpm smoke:bundle-replacement`
-`project-bundle-replacement-smoke.ps1` compiles the targeted `project-bundle-replacement-smoke.ts` service-level smoke with the local desktop TypeScript toolchain, runs it under Node, and verifies same-project replacement, shared `skills/` and `cron/` ownership cleanup, rollback on an injected post-commit failure, and successful recovery on the next sync; `pnpm smoke:bundle-replacement`
-`project-isolation-smoke.ps1` runs the project-isolation regression smokes back to back, including the targeted default-chat, project-context refresh, and empty-project-inventory smokes; `pnpm smoke:project-isolation`
-`project-bundle-churn-smoke.ps1` compiles the targeted `project-bundle-churn-smoke.ts` service-level smoke with the local desktop TypeScript toolchain, runs it under Node, and verifies multi-project churn with stable survivors, same-project replacement, project removal, project addition, active-project fallback, and session survival inside unaffected projects; `pnpm smoke:bundle-churn`
-`project-isolation-smoke.ps1` runs the main project-isolation regression gate end to end, including workspace-entry, default-chat, cloud-bundle Electron lifecycle coverage, project-context refresh, empty-project inventory, bundle reconcile, bundle freshness, bundle replacement, and multi-project churn; `pnpm smoke:project-isolation`
throw new Error('Workspace-entry smoke did not report a workspace status label. history=' + JSON.stringify(statusLabels) + ' latest=' + latestStatusLabel);
const reportedSkillRouteLabel = statusLabels.some((label) => label.toLowerCase().includes('routing to skill '));
if (!reportedWorkspaceLabel && !(expectRemoteBundle === 'true' && reportedSkillRouteLabel)) {
throw new Error('Workspace-entry smoke did not report a workspace or routed-skill status label. history=' + JSON.stringify(statusLabels) + ' latest=' + latestStatusLabel);
}
}
if (!assistantContent.includes('desktop project-isolated workspace')) {
if (!assistantContent.includes('desktop project-isolated workspace')) {
throw new Error('Workspace-entry smoke did not echo the injected isolated workspace context.');
throw new Error('Workspace-entry smoke did not echo the injected isolated workspace context.');
...
@@ -305,7 +316,7 @@ if (expectWorkspaceEntry === 'true') {
...
@@ -305,7 +316,7 @@ if (expectWorkspaceEntry === 'true') {
if (!assistantContent.includes('Keep project context isolated to this project and session.')) {
if (!assistantContent.includes('Keep project context isolated to this project and session.')) {
throw new Error('Workspace-entry smoke did not preserve the project isolation instruction.');
throw new Error('Workspace-entry smoke did not preserve the project isolation instruction.');
}
}
if (selectedSkillId) {
if (selectedSkillId && expectRemoteBundle !== 'true') {
throw new Error('Workspace-entry smoke unexpectedly selected a skill: ' + selectedSkillId);
throw new Error('Workspace-entry smoke unexpectedly selected a skill: ' + selectedSkillId);
}
}
if (!String(sendResult.sessionId || streamSmoke.sessionId || '').startsWith(expectedSessionPrefix)) {
if (!String(sendResult.sessionId || streamSmoke.sessionId || '').startsWith(expectedSessionPrefix)) {
assert(materializedSkills.some((item)=>item.metadata.selected===true&&item.metadata.runtimeSkillName===prepared.runtimeSkillName),"Main orchestrator skill was not materialized as the selected runtime skill.");
assert(materializedSkills.some((item)=>item.skillBody.includes("name: xiaohongshu-writer")),"Writer dependency skill was not materialized with its original skill name.");
assert(materializedSkills.some((item)=>item.skillBody.includes("name: xiaohongshu-publisher")),"Publisher dependency skill was not materialized with its original skill name.");
assert(resolvedTarget.autoRouted,"Chat target resolver did not auto-route the default chat request.");
assert(resolvedTarget.sessionState.projectId===xiaohongshu.id,"Chat target resolver did not rebind the request into the Xiaohongshu workspace session.");
assert(resolvedTarget.sessionState.sessionId!==seedSession.id,"Chat target resolver should not reuse the original Douyin session for a Xiaohongshu request.");
assert(publisherRoute?.skillId==="xiaohongshu-publisher","Skill router did not choose the Xiaohongshu publisher skill for the existing-draft publish request.");
Status: Core execution chain, cloud-owned inventory, bundle freshness hardening, replacement/rollback hardening, and lifecycle smoke are implemented
Status: Core execution chain, cloud-owned inventory, bundle freshness hardening, replacement/rollback hardening, broader churn coverage, and lifecycle smoke are implemented
## 1. Scope and product constraints
## 1. Scope and product constraints
This document describes the current desktop implementation for project-isolated execution.
This document describes the current desktop implementation for project-isolated execution.
...
@@ -10,6 +10,10 @@ Confirmed product constraints:
...
@@ -10,6 +10,10 @@ Confirmed product constraints:
- The active bundle source is `skills[].skill.download_url`.
- The active bundle source is `skills[].skill.download_url`.
-`project_bundle_url` is not part of the current implementation.
-`project_bundle_url` is not part of the current implementation.
- Local project CRUD is not part of the target product shape.
- Local project CRUD is not part of the target product shape.
- The current backend contract returns a `skills` array.
- Each `skills[]` object currently treats `skill.download_url` as one project zip URL.
- Multiple projects are represented by multiple `skills[]` objects.
- Any future multi-value download URL contract must be handled as a separate compatibility change.
## 2. Implemented architecture
## 2. Implemented architecture
The implemented chain is:
The implemented chain is:
...
@@ -28,6 +32,7 @@ The implemented chain is:
...
@@ -28,6 +32,7 @@ The implemented chain is:
10. Bundle reconciliation removes stale bundle-managed projects, shared `skills/` entries, shared `cron/` entries, and stale manifest records when the expected cloud bundle set changes.
10. Bundle reconciliation removes stale bundle-managed projects, shared `skills/` entries, shared `cron/` entries, and stale manifest records when the expected cloud bundle set changes.
11. Bundle freshness probes the remote bundle with HTTP metadata before deciding whether an existing local materialization can be reused.
11. Bundle freshness probes the remote bundle with HTTP metadata before deciding whether an existing local materialization can be reused.
12. Same-`projectId` replacement now stages project/shared assets, commits them in order, and either finalizes or rolls back the whole replacement set.
12. Same-`projectId` replacement now stages project/shared assets, commits them in order, and either finalizes or rolls back the whole replacement set.
13. Broader churn coverage now verifies survivor, replacement, removal, and addition flows across multiple projects without changing the current cloud protocol.
## 3. Code-truth execution behavior
## 3. Code-truth execution behavior
Current behavior verified from code:
Current behavior verified from code:
...
@@ -97,8 +102,15 @@ Current smoke coverage in the repo:
...
@@ -97,8 +102,15 @@ Current smoke coverage in the repo:
- validates same-project replacement at the service layer
- validates same-project replacement at the service layer
- validates rollback after injected post-commit failure
- validates rollback after injected post-commit failure
- validates successful recovery on the next sync
- validates successful recovery on the next sync
-`project-bundle-churn-smoke.ps1`
- validates multi-project survivor behavior
- validates same-project replacement during broader churn
- validates project removal and project addition in the same sync window
- validates active-project fallback when the active project is removed
-`project-isolation-smoke.ps1` now aggregates reconcile, freshness, replacement, and multi-project churn coverage into the broader project-isolation gate
- Electron smoke validates workspace-agent status history instead of relying on the final status label only
- Electron smoke validates workspace-agent status history instead of relying on the final status label only
## 5. Important current limitations
## 5. Important current limitations
### Broad UI regression breadth is still selective
### Broad UI regression breadth is still selective
The targeted Electron lifecycle smoke for cloud bundle replacement now exists, but the full UI regression matrix is still intentionally selective rather than exhaustive.
The targeted Electron lifecycle smoke for cloud bundle replacement now exists, but the full UI regression matrix is still intentionally selective rather than exhaustive.
### Follow-up hardening can still expand
### Stress breadth can still expand
The current replacement lifecycle coverage is strong for the implemented path, but future changes may still need wider multi-project churn and stress coverage.
The current replacement and churn coverage is strong for deterministic paths, but future changes may still need larger randomized multi-project stress coverage.
## 6. What has been completed so far
## 6. What has been completed so far
Completed enough to count as real implementation:
Completed enough to count as real implementation:
...
@@ -95,17 +105,19 @@ Completed enough to count as real implementation:
...
@@ -95,17 +105,19 @@ Completed enough to count as real implementation:
- stale bundle-managed cleanup for project/skill/cron/manifest state
- stale bundle-managed cleanup for project/skill/cron/manifest state
- bundle freshness hardening using remote metadata probe
- bundle freshness hardening using remote metadata probe
- replacement / rollback hardening for same-project bundle updates
- replacement / rollback hardening for same-project bundle updates
- smoke coverage for empty inventory, removal, freshness, replacement, and Electron lifecycle validation
- broader service-level churn coverage for multi-project inventory transitions
- smoke coverage for empty inventory, removal, freshness, replacement, churn, and Electron lifecycle validation
This project is no longer at the design-only stage.
This project is no longer at the design-only stage.
## 7. What should happen next
## 7. What should happen next
Recommended next work:
Recommended next work:
1. Keep the current smoke set green as adjacent runtime work lands.
1. Keep the current smoke set green as adjacent runtime work lands.
2. Fold the new lifecycle replacement smoke into any broader release gate if needed.
2. Use `project-isolation-smoke.ps1` plus `cloud-bundle-smoke.ps1` as the main higher-level regression gate pair.
3. Expand coverage only when upcoming product changes introduce new isolation surfaces.
3. Expand breadth only when upcoming product changes introduce new isolation surfaces.
4. If backend protocol changes from single `download_url` to a multi-value form, handle it as a separate compatibility task instead of folding it into unrelated hardening work.
## 8. Final summary
## 8. Final summary
The single-instance plus task-isolation foundation is implemented.
The single-instance plus task-isolation foundation is implemented.
The earlier main gaps around cloud-owned inventory, replacement/rollback hardening, and lifecycle smoke have been closed.
The earlier main gaps around cloud-owned inventory, replacement/rollback hardening, and lifecycle smoke have been closed.
The remaining work is mostly broader regression breadth and routine maintenance.
The remaining work is mostly broader regression breadth and future protocol evolution, not missing core behavior.