diff --git a/crates/protocol/src/lib.rs b/crates/protocol/src/lib.rs
index 0c4ac348..74ede90d 100644
--- a/crates/protocol/src/lib.rs
+++ b/crates/protocol/src/lib.rs
@@ -1377,7 +1377,7 @@ mod tests {
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(parsed["event"], "completions");
assert_eq!(parsed["data"]["kind"], "file");
- assert_eq!(parsed["data"]["entries"][0]["value"], "clear");
+ assert_eq!(parsed["data"]["entries"][0]["value"], "src/main.rs");
// is_dir defaults to false on inbound payloads that omit it.
let inbound =
diff --git a/crates/tools/tests/edge_cases.rs b/crates/tools/tests/edge_cases.rs
index 60a402fa..6e139d07 100644
--- a/crates/tools/tests/edge_cases.rs
+++ b/crates/tools/tests/edge_cases.rs
@@ -131,10 +131,11 @@ async fn symlink_to_outside_scope_is_rejected_for_write() {
assert!(
msg.contains("outside allowed read scope")
|| msg.contains("outside allowed write scope")
+ || msg.contains("outside allowed scope")
|| msg.contains("has not been read"),
"symlink escape not rejected: {msg}"
);
- if !msg.contains("has not been read") {
+ if msg.contains("outside allowed read scope") || msg.contains("outside allowed write scope") {
assert!(
msg.contains("add the symlink target"),
"symlink escape diagnostic should include remediation: {msg}"
@@ -233,12 +234,16 @@ async fn absolute_path_is_rejected() {
)
.await
.unwrap_err();
- assert!(format!("{err}").contains("invalid Workdir path"));
+ let msg = format!("{err}");
+ assert!(
+ msg.contains("invalid logical filesystem path"),
+ "absolute path was not rejected as invalid: {msg}"
+ );
}
#[tokio::test]
async fn directory_target_is_rejected_for_read() {
- let (dir, _spill, reg) = setup();
+ let (_dir, _spill, reg) = setup();
let read = reg.get("Read");
let err = read
.execute(&json!({ "file_path": "." }).to_string(), Default::default())
diff --git a/crates/tools/tests/integration.rs b/crates/tools/tests/integration.rs
index 4f8e8aa6..f8761aa8 100644
--- a/crates/tools/tests/integration.rs
+++ b/crates/tools/tests/integration.rs
@@ -191,7 +191,7 @@ async fn write_then_grep_finds_content() {
#[tokio::test]
async fn glob_finds_written_files() {
- let (dir, _spill, reg) = setup();
+ let (_dir, _spill, reg) = setup();
let write = reg.get("Write");
let glob = reg.get("Glob");
@@ -229,7 +229,10 @@ async fn absolute_path_is_rejected() {
.await;
// Absolute paths are rejected at the logical WorkdirSession boundary.
let msg = format!("{err}");
- assert!(msg.contains("invalid Workdir path"), "unexpected: {msg}");
+ assert!(
+ msg.contains("invalid logical filesystem path"),
+ "unexpected: {msg}"
+ );
}
#[tokio::test]
@@ -340,7 +343,7 @@ async fn tracker_recent_files_tracks_read_write_edit() {
));
let a = dir.path().join("a.txt");
- let b = dir.path().join("b.txt");
+ let _b = dir.path().join("b.txt");
std::fs::write(&a, "one\n").unwrap();
// Read `a` — should appear in recency.
diff --git a/crates/worker/tests/compact_events_test.rs b/crates/worker/tests/compact_events_test.rs
index 973f080f..0df09529 100644
--- a/crates/worker/tests/compact_events_test.rs
+++ b/crates/worker/tests/compact_events_test.rs
@@ -533,6 +533,9 @@ model_id = "test-model"
max_tokens = 100
[memory]
+workspace_id = "test-workspace"
+settings_revision = 1
+language = "English"
extract_threshold = 1
[compaction]
@@ -695,6 +698,9 @@ model_id = "test-model"
max_tokens = 100
[memory]
+workspace_id = "test-workspace"
+settings_revision = 1
+language = "English"
extract_threshold = 1
[[scope.allow]]
diff --git a/crates/worker/tests/session_metrics_test.rs b/crates/worker/tests/session_metrics_test.rs
index 53222f1b..11e46dcc 100644
--- a/crates/worker/tests/session_metrics_test.rs
+++ b/crates/worker/tests/session_metrics_test.rs
@@ -87,6 +87,7 @@ impl Tool for BigContentTool {
Ok(ToolOutput {
summary: self.summary.into(),
content: Some(self.content.clone()),
+ attachments: Vec::new(),
})
}
}
diff --git a/web/workspace/src/lib/workspace/console/worker-console.ui.test.ts b/web/workspace/src/lib/workspace/console/worker-console.ui.test.ts
index 009cce36..a46eb180 100644
--- a/web/workspace/src/lib/workspace/console/worker-console.ui.test.ts
+++ b/web/workspace/src/lib/workspace/console/worker-console.ui.test.ts
@@ -115,14 +115,12 @@ Deno.test("workspace Worker list lives on the dedicated Workers page", async ()
"top workspace page should not own the Worker list",
);
assert(
- workersPage.includes("workerConsoleHref(worker, data.workspaceId)") &&
- workersPage.includes('
') &&
- workersPage.includes(
- "workerDisplayName = worker.display_name || worker.label",
- ) &&
- workersPage.includes("worker {worker.worker_id}") &&
+ workersPage.includes("workerHref") &&
+ workersPage.includes("workers-table") &&
+ workersPage.includes("workerDisplayName") &&
+ workersPage.includes("worker.resource_key") &&
workersPage.includes("Delete ${workerDisplayName}"),
- "dedicated Workers page should expose a table, console link target, and icon actions per Worker",
+ "dedicated Workers page should expose a table, canonical Worker link target, and icon actions per Worker",
);
assert(
workersNav.includes("href={`/w/${workspaceId}/workers`}") &&
@@ -218,14 +216,16 @@ Deno.test("workspace Tickets surface provides Kanban and lifecycle controls", as
"Tickets and Objectives should each be a single sidebar link",
);
assert(
- ticketsLoad.includes("?limit=1000") &&
+ ticketsLoad.includes("Object.entries(LANE_STATES)") &&
+ ticketsLoad.includes('limit: "30"') &&
+ ticketsLoad.includes('states: states.join(",")') &&
+ ticketsLoad.includes("/tickets?${search}") &&
!ticketsLoad.includes("/tickets/query") &&
ticketsPage.includes('class="ticket-kanban"') &&
- ticketsPage.includes('class="ticket-lane-cards"') &&
- ticketsPage.includes("lane.tickets.slice(0, lane.visibleCount)") &&
- ticketsPage.includes("handleLaneScroll") &&
- ticketsPage.includes("revealNextTickets"),
- "Tickets list should fetch lightweight summaries once and incrementally reveal each Kanban lane",
+ ticketsPage.includes("laneState") &&
+ ticketsPage.includes("loadMore(lane.id)") &&
+ ticketsPage.includes("handleLaneScroll(event, lane.id)"),
+ "Tickets list should fetch lightweight paginated summaries for each Kanban lane",
);
assert(
ticketPanelModel.includes('label: "Ready + Planning"') &&
@@ -250,16 +250,17 @@ Deno.test("workspace Tickets surface provides Kanban and lifecycle controls", as
assert(
ticketDetailLoad.includes("/repositories") &&
ticketDetailPage.includes('mutate("state", "/state"') &&
- ticketDetailPage.includes('mutate("queue", "/queue"') &&
+ ticketDetailPage.includes("async function queueTicket") &&
+ ticketDetailPage.includes("`${ticketPath}/queue`") &&
!ticketDetailPage.includes("/merge-request/merge") &&
ticketDetailPage.includes("mergeRequest.selector_from") &&
- ticketDetailPage.includes("currentReview?.kind") &&
- ticketDetailPage.includes("mergeEvent?.kind") &&
- !ticketDetailPage.includes('mutate("review", "/review"') &&
+ ticketDetailPage.includes("mergeRequest.review_status") &&
+ ticketDetailPage.includes("mergeRequestPagePath") &&
ticketDetailPage.includes('mutate("close", "/close"') &&
- ticketDetailPage.includes("ticketWorkerLaunchHref") &&
+ ticketDetailPage.includes("mutateAssignment") &&
+ ticketDetailPage.includes("can_start_manual_coder") &&
ticketDetailPage.includes("ticket.relations.outgoing"),
- "Ticket detail should expose typed lifecycle actions, relations, target selection, and role Worker launch",
+ "Ticket detail should expose typed lifecycle actions, relations, target selection, assignments, and Merge Request navigation",
);
});
@@ -319,17 +320,19 @@ Deno.test("workspace Memory surfaces use read-only scoped memory APIs", async ()
);
});
-Deno.test("root layout does not keep legacy unscoped route compatibility", async () => {
+Deno.test("root layout keeps Workspace selection explicit", async () => {
const layoutLoad = await Deno.readTextFile(
new URL("./../../../routes/+layout.ts", import.meta.url),
);
assert(
- !layoutLoad.includes("scopedCompatibilityRoute") &&
- !layoutLoad.includes('pathname === "/runtimes"') &&
- !layoutLoad.includes("return workspaceRoute(workspaceId, pathname)") &&
- layoutLoad.includes("workspaceRoute(workspace.data.workspace_id)"),
- "root layout should bootstrap the workspace entry only, not preserve legacy unscoped routes",
+ layoutLoad.includes("export const load") &&
+ layoutLoad.includes("() => ({})") &&
+ !layoutLoad.includes("scopedCompatibilityRoute") &&
+ !layoutLoad.includes("/api/workspace") &&
+ !layoutLoad.includes("workspaceRoute") &&
+ !layoutLoad.includes("redirect("),
+ "root layout should not infer, bootstrap, or redirect through a singleton Workspace",
);
});
@@ -811,9 +814,11 @@ Deno.test("Account UI owns browser passkey session state without workspace autho
"SidebarOverride should register and clean up the child-provided sidebar snippet",
);
assert(
- rootLayoutLoad.includes('"/account"') &&
- rootLayoutLoad.includes('"/login/device"'),
- "Root layout should not redirect account and device-login public routes to a workspace",
+ rootLayoutLoad.includes("export const load") &&
+ rootLayoutLoad.includes("() => ({})") &&
+ !rootLayoutLoad.includes("workspaceRoute") &&
+ !rootLayoutLoad.includes("redirect("),
+ "Root layout should leave account and device-login routes public by avoiding Workspace redirects entirely",
);
});