feat(workspace): allow creation without an initial repository

Keep bootstrap, signing identity, config and resource-key allocation atomic with optional Repository intent. Preserve repository-present fingerprints for saved receipts; replay reads current authority at the initial key and never recreates missing assets.

Provide explicit repository-free CLI creation, optional TUI/Web creation inputs, nullable generated contracts, and valid empty catalog/Worker paths. Retain existing authenticated non-owner viewing policy pending the T-717 authorization decision.

Validation: root cargo check; server tests 639 lib + 11 bin; server-api 70 default / 77 typescript; client 52 lib + 2 integration; tui 337; yoi 11 lib + 120 bin. OpenAPI/TS freshness, cargo fmt and diff checks pass. Web check/build, 18 model tests, 268 component tests and Chromium 8 width/theme combinations pass; before/after visual evidence is documented in web/workspace/test/visual-review. Full Web suite has one independently reproduced baseline Ticket source-policy failure.
This commit is contained in:
2026-10-08 18:34:44 +09:00
parent e939cd4e4b
commit 7919616db5
37 changed files with 2335 additions and 354 deletions
@@ -8,12 +8,57 @@ import {
parseRepositoryApiError,
parseRepositoryListApiResult,
parseRepositoryListResponse,
parseWorkspaceCreateResponse,
parseWorkspaceDeletionOperationResponse,
parseWorkspaceDeletionPreflightResponse,
parseWorkspaceResponse,
REPOSITORY_API_LIMITS,
} from "../src/lib/workspace/api/workspace-model.ts";
import {
creationResponse,
currentInitialRepository,
} from "../src/lib/workspace/api/workspace-catalog.test-fixtures.ts";
Deno.test("workspace creation decodes absent and deleted initial repositories as explicit null", () => {
for (const replayed of [false, true]) {
const parsed = parseWorkspaceCreateResponse(
JSON.parse(JSON.stringify(creationResponse(null, replayed))),
);
if (parsed.repository !== null || parsed.replayed !== replayed) {
throw new Error("nullable creation response was not preserved");
}
}
});
Deno.test("workspace creation replay preserves the returned current repository record", () => {
const parsed = parseWorkspaceCreateResponse(
creationResponse(currentInitialRepository, true),
);
if (
JSON.stringify(parsed.repository) !==
JSON.stringify(currentInitialRepository)
) throw new Error("current repository record was replaced or lost");
});
Deno.test("workspace creation rejects missing and malformed repository response fields", () => {
for (
const value of [undefined, false, {}, {
...currentInitialRepository,
source_revision: -1,
}]
) {
assertThrows(
() =>
parseWorkspaceCreateResponse({
...creationResponse(),
repository: value,
}),
"workspace create response.repository",
);
}
});
function assertThrows(operation: () => unknown, expected: string): void {
try {
operation();