fix: complete state-contract naming cleanup

This commit is contained in:
2026-10-10 17:48:59 +09:00
parent 3263d50125
commit 6a12b6cc34
109 changed files with 3696 additions and 576 deletions
@@ -1,6 +1,6 @@
// @generated by yoi-companion-openapi-typescript@1. Do not edit.
// Input: openapi/server-api.json
// Input contract: 0.1.0 (sha256:07cfe53bfdd700cad76d00695a031255ffa76a6062110a05596fa8b64ae372c4)
// Input contract: 0.1.0 (sha256:fc25104c077ab8c5c43f77611f505171585d2d40fd54c6538774ac17aae6cce3)
// Options: companion-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
// Output: web/workspace/src/lib/generated/companion-api.ts
// Regenerate/check: cargo run -q -p server-api --example generate_companion_api_types -- --check
@@ -63,7 +63,6 @@ export type WorkspaceDeletionState =
export type WorkspaceDeletionBlockerKind =
| "last_accessible_workspace"
| "revision_conflict"
| "dirty_workdir"
| "worker_removal_blocked"
| "workdir_removal_blocked"
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
// @generated by yoi-repository-openapi-typescript@1. Do not edit.
// Input: openapi/server-api.json
// Input contract: 0.1.0 (sha256:07cfe53bfdd700cad76d00695a031255ffa76a6062110a05596fa8b64ae372c4)
// Input contract: 0.1.0 (sha256:fc25104c077ab8c5c43f77611f505171585d2d40fd54c6538774ac17aae6cce3)
// Options: repository-operation-schema-closure,openapi-3.1,format:uint64=safe-number,strict-safe-integers,sorted-output
// Output: web/workspace/src/lib/generated/repository-api.ts
// Regenerate/check: cargo run -q -p server-api --example generate_repository_openapi_types -- --check
@@ -1,6 +1,6 @@
// @generated by yoi-runtime-openapi-typescript@1. Do not edit.
// Input: openapi/server-api.json
// Input contract: 0.1.0 (sha256:07cfe53bfdd700cad76d00695a031255ffa76a6062110a05596fa8b64ae372c4)
// Input contract: 0.1.0 (sha256:fc25104c077ab8c5c43f77611f505171585d2d40fd54c6538774ac17aae6cce3)
// Options: runtime-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
// Output: web/workspace/src/lib/generated/runtime-api.ts
// Regenerate/check: cargo run -q -p server-api --example generate_runtime_api_types -- --check
@@ -1,6 +1,6 @@
// @generated by yoi-ticket-browser-openapi-typescript@1. Do not edit.
// Input: openapi/server-api.json
// Input contract: 0.1.0 (sha256:07cfe53bfdd700cad76d00695a031255ffa76a6062110a05596fa8b64ae372c4)
// Input contract: 0.1.0 (sha256:fc25104c077ab8c5c43f77611f505171585d2d40fd54c6538774ac17aae6cce3)
// Options: ticket-objective-merge-request-browser-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
// Output: web/workspace/src/lib/generated/ticket-api.ts
// Regenerate/check: cargo run -q -p server-api --example generate_ticket_api_types -- --check
@@ -1,6 +1,6 @@
// @generated by yoi-workdir-openapi-typescript@1. Do not edit.
// Input: openapi/server-api.json
// Input contract: 0.1.0 (sha256:07cfe53bfdd700cad76d00695a031255ffa76a6062110a05596fa8b64ae372c4)
// Input contract: 0.1.0 (sha256:fc25104c077ab8c5c43f77611f505171585d2d40fd54c6538774ac17aae6cce3)
// Options: workdir-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
// Output: web/workspace/src/lib/generated/workdir-api.ts
// Regenerate/check: cargo run -q -p server-api --example generate_workdir_api_types -- --check
@@ -1,6 +1,6 @@
// @generated by yoi-worker-openapi-typescript@1. Do not edit.
// Input: openapi/server-api.json
// Input contract: 0.1.0 (sha256:07cfe53bfdd700cad76d00695a031255ffa76a6062110a05596fa8b64ae372c4)
// Input contract: 0.1.0 (sha256:fc25104c077ab8c5c43f77611f505171585d2d40fd54c6538774ac17aae6cce3)
// Options: worker-operation-schema-closure,openapi-3.1,strict-integer-ranges,sorted-output
// Output: web/workspace/src/lib/generated/worker-launch-api.ts
// Regenerate/check: cargo run -q -p server-api --example generate_worker_launch_api_types -- --check
@@ -135,7 +135,7 @@ function repositoryArray(value: unknown, path: string): unknown[] {
return parsed;
}
function repositorySourceRevision(value: unknown, path: string): number {
function nonnegativeSafeInteger(value: unknown, path: string): number {
const parsed = integer(value, path);
if (parsed < 0) {
throw new Error(`${path} must be between 0 and Number.MAX_SAFE_INTEGER`);
@@ -833,7 +833,7 @@ export function parseRepositorySshConnectionProbeResponse(
}
const WORKSPACE_DELETION_MAX_OPERATION_ID_BYTES = 128;
const WORKSPACE_DELETION_MAX_REVISION_BYTES = 128;
const WORKSPACE_DELETION_MAX_UPDATED_AT_BYTES = 128;
const WORKSPACE_DELETION_MAX_BLOCKERS = 1024;
const WORKSPACE_DELETION_MAX_CHILD_OPERATION_IDS = 4096;
const WORKSPACE_DELETION_MAX_RESOURCE_VALUE_BYTES = 128;
@@ -872,7 +872,6 @@ const deletionStates = new Set<WorkspaceDeletionState>([
]);
const deletionBlockerKinds = new Set<WorkspaceDeletionBlockerKind>([
"last_accessible_workspace",
"revision_conflict",
"dirty_workdir",
"worker_removal_blocked",
"workdir_removal_blocked",
@@ -981,7 +980,7 @@ export function parseWorkspaceDeletionPreflightResponse(
expected_workspace_updated_at: deletionBoundedString(
item.expected_workspace_updated_at,
"Workspace deletion preflight.expected_workspace_updated_at",
WORKSPACE_DELETION_MAX_REVISION_BYTES,
WORKSPACE_DELETION_MAX_UPDATED_AT_BYTES,
),
can_delete: boolean(
item.can_delete,
@@ -1089,7 +1088,7 @@ export function parseHostListResponse(value: unknown): HostListResponse {
["workspace_id", "limit", "items", "source", "diagnostics"],
"host list response",
);
const limit = repositorySourceRevision(
const limit = nonnegativeSafeInteger(
response.limit,
"host list response.limit",
);
@@ -1278,7 +1278,6 @@ Deno.test("Worker Console route resolves logical Worker authority before Runtime
consolePage.includes('sendWorkerControl("cancel")') &&
consolePage.includes("lifecycleMethod(command)") &&
consolePage.includes("command_id: commandId") &&
!consolePage.includes("expected_worker_state_revision") &&
!consolePage.includes("expected_execution_generation") &&
consolePage.includes("onsubmit={handleComposerSubmit}") &&
consolePage.includes("disabled={!composerEditable}") &&
@@ -177,7 +177,7 @@ Deno.test("Merge Request routes distinguish recorded approval from live source o
),
);
assertEquals(detail.includes("Recorded integration approval"), true);
assertEquals(detail.includes("Live source revision"), true);
assertEquals(detail.includes("Live source ref"), true);
assertEquals(detail.includes("Source observation error"), true);
});
@@ -92,7 +92,7 @@ export function sourceReviewFreshness(
return `Fresh source review required: no effective verdict exists for ${source}.`;
}
// Source/integration approval can survive rescope; only this exact revision
// Source/integration approval can survive rescope; only this exact item snapshot
// and linked-result attestation approves the current Ticket requirements.
export function currentRequirementApprovalStatus(
evidence: TicketEvidenceSummary,
@@ -1,6 +1,6 @@
import type { DriveEntryRef } from "#lib/generated/drive-api.ts";
/** Human-facing latest-only link: name, path, revision and bearer tokens are absent. */
/** Human-facing latest-only link: name, path, content digest and bearer tokens are absent. */
export function driveHref(entry: DriveEntryRef): string {
if (
!/^[1-9][0-9]{0,18}$/.test(entry.node_id) ||
@@ -97,7 +97,7 @@ function optionalBoundedString(
return boundedStringValue(value, context, maxBytes);
}
function optionalRevision(
function optionalByteCount(
value: unknown,
context: string,
): number | null | undefined {
@@ -250,7 +250,7 @@ function parseWorkspaceProfileSourceSummary(
502,
);
}
const sizeBytes = optionalRevision(item.size_bytes, "Profile source catalog");
const sizeBytes = optionalByteCount(item.size_bytes, "Profile source catalog");
if (sizeBytes === undefined || sizeBytes === null) {
throw new ProfileApiError(
"Profile source catalog returned an invalid response.",
@@ -53,9 +53,9 @@ Deno.test("Git Workdir with a selector shows selector at current ref", () => {
Deno.test("non-Git Workdir does not receive Git hash formatting", () => {
assertEquals(
formatCurrentWorkdirCheckout(
workdir("snapshot", "revision-value"),
workdir("snapshot", "snapshot-id"),
"archive",
),
"snapshot · revision-value",
"snapshot · snapshot-id",
);
});
@@ -47,7 +47,7 @@ function attachment(
};
}
Deno.test("logical config sidebar label is not fabricated as a repository revision", () => {
Deno.test("logical config sidebar label is not fabricated as a repository checkout", () => {
const meta = sidebarWorkdirMeta([attachment("workspace-config", {
display_name: "Workspace configuration",
source: { kind: "workspace_config", access: "read_write", content_path: "/workspace-config", purpose: "Configuration" },
@@ -87,6 +87,17 @@ Deno.test("Worker sidebar Workdir label uses the creation branch before observat
assertEquals(meta.text, "main:develop");
});
Deno.test("Worker sidebar Workdir label shows the creation ref before a current checkout is observed", () => {
const meta = sidebarWorkdirMeta([
attachment("checkout", {
creation_selector: "refs/tags/release",
creation_ref: "0123456789abcdef0123456789abcdef01234567",
}),
]);
assertEquals(meta.text, "main:creation-ref@0123456789ab");
});
Deno.test("Worker sidebar Workdir label marks detached HEAD instead of reusing creation branch", () => {
const meta = sidebarWorkdirMeta([
attachment("checkout", {
@@ -38,10 +38,10 @@ function attachmentLabel(attachment: SidebarWorkdirAttachment): string {
const repository = workdir?.source.kind === "repository"
? clean(workdir.source.repository_key)
: clean(attachment.repository_key);
return `${repository ?? "unknown-repo"}:${revisionLabel(attachment)}`;
return `${repository ?? "unknown-repo"}:${checkoutLabel(attachment)}`;
}
function revisionLabel(attachment: SidebarWorkdirAttachment): string {
function checkoutLabel(attachment: SidebarWorkdirAttachment): string {
const workdir = attachment.working_directory;
if (!workdir) {
return `unknown@${shortReference(attachment.working_directory_id)}`;
@@ -66,7 +66,7 @@ function revisionLabel(attachment: SidebarWorkdirAttachment): string {
const creationReference = clean(workdir.creation_ref);
if (creationSelector || creationReference) {
return explicitReferenceFallback(
"revision",
"creation-ref",
creationReference ?? creationSelector,
);
}
@@ -84,7 +84,7 @@ function branchName(selector: string | null): string | null {
}
function explicitReferenceFallback(
kind: "detached" | "revision",
kind: "detached" | "creation-ref",
reference: string | null,
): string {
return reference ? `${kind}@${shortReference(reference)}` : kind;
@@ -75,7 +75,7 @@ test("renders the subject index as the Memory product entry", () => {
.toBeNull();
expect(screen.getByText("subject-1")).not.toBeNull();
expect(screen.getByText("Not connected")).not.toBeNull();
expect(screen.queryByText("Store revision", { exact: true })).toBeNull();
expect(screen.queryByText("Memory fingerprint", { exact: true })).toBeNull();
expect(screen.getByText("1 shown · more available")).not.toBeNull();
expect(screen.getByRole("link", { name: "First page" }).getAttribute("href"))
.toBe("/w/workspace-1/memory");
@@ -114,7 +114,6 @@ test("saved settings have no editable form, badges or raw data expanded; each te
);
expect(document.querySelectorAll("details[open]")).toHaveLength(0);
expect(document.querySelector(".badge")).toBeNull();
expect(screen.queryByText("Revision", { exact: true })).toBeNull();
expect(
screen.getByText(identityFixture().public_bundle!.public_key_fingerprint),
).toBeTruthy();
+2 -2
View File
@@ -197,12 +197,12 @@ Deno.test("config source API rejects mismatched entry map paths", async () => {
);
});
Deno.test("config source API rejects removed snapshot revision fields and oversized entry content", () => {
Deno.test("config source API rejects unknown snapshot fields and oversized entry content", () => {
assertThrows(
() =>
parseWorkspaceConfigTreeResponse({
...tree,
snapshot: { ...snapshot, revision: 7 },
snapshot: { ...snapshot, unexpected_field: true },
}),
ConfigSourceApiError,
"invalid response",
@@ -169,7 +169,6 @@ Deno.test("generated WASM diagnostics carry snapshot provenance", () => {
}>;
assertEquals(diagnostics[0].path, "workspace.dcdl");
assertEquals(diagnostics[0].tree_digest, fixtureSnapshot(snapshot).digest);
assertEquals("revision" in diagnostics[0], false);
assertEquals(diagnostics[0].kind, "syntax");
});
@@ -938,7 +937,6 @@ Deno.test("generated WASM returns to the same content digest and enforces entry
content: "{ broken = ; }",
}]) as ConfigTreeSnapshot;
assertEquals(changed.digest === base.digest, false);
assertEquals("revision" in changed, false);
const diagnostics = analyze_snapshot(changed, "main.dcdl", undefined) as Array<{ tree_digest: string }>;
assertEquals(diagnostics.length > 0, true);
assertEquals(diagnostics.every((diagnostic) => diagnostic.tree_digest === changed.digest), true);
@@ -74,7 +74,7 @@ Deno.test("Repository Access distinguishes permission, conflict, field, and miss
repositoryAccessRequestError(409, "rotate the key").message.includes(
"Reload",
),
"409 should explain revision recovery",
"409 should explain conflict recovery",
);
assert(
repositoryAccessRequestError(422, "rotate the key").message.includes(
+1 -1
View File
@@ -43,7 +43,7 @@ capture error is expected. To repeat dark captures, use a scenario copy with
## Implementer-owned visual review
- Baseline revision: `e939cd4e4b1f6b63b81411fd7766062f2d69ad0d`.
- Baseline commit: `e939cd4e4b1f6b63b81411fd7766062f2d69ad0d`.
- Before: `.svelte-kit/t717/visual/before-final-light` and `before-dark`.
These use an isolated build under `.svelte-kit/t717/baseline-source`, with the
changed production form, catalog API, model, and CSS restored from baseline
+3 -4
View File
@@ -700,15 +700,14 @@ Deno.test("Memory changes preserve server order without interpreting opaque iden
assertEquals(parseSubjektivMemoryReadResponse({ ...detailFixture(), change_id: "z/earlier", current_change_id: "a/current" }).change_id, "z/earlier");
});
Deno.test("Memory identities reject numeric counters, empty and oversized strings, and legacy fields", () => {
Deno.test("Memory identities reject numeric counters, empty and oversized strings, and unknown fields", () => {
for (const value of [3, "", "x".repeat(513)]) {
assertThrows(() => parseSubjektivMemoryReadResponse({ ...detailFixture(), change_id: value }), "bounded identifier");
assertThrows(() => parseSubjektivMemoryReadResponse({ ...detailFixture(), current_change_id: value }), "bounded identifier");
assertThrows(() => parseSubjektivSubjectResponse({ ...subject(), memory_fingerprint: value }), "bounded identifier");
}
assertThrows(() => parseSubjektivMemoryReadResponse({ ...detailFixture(), revision: 2 }), "unknown field");
assertThrows(() => parseSubjektivSubjectResponse({ ...subject(), behavior_revision: 2 }), "unknown field");
assertThrows(() => parseSubjektivSubjectResponse({ ...subject(), store_revision: 12 }), "unknown field");
assertThrows(() => parseSubjektivMemoryReadResponse({ ...detailFixture(), unexpected_field: true }), "unknown field");
assertThrows(() => parseSubjektivSubjectResponse({ ...subject(), unexpected_field: true }), "unknown field");
});
Deno.test("Behavior CAS sends the observed text byte-for-byte including empty content", async () => {
+15 -3
View File
@@ -93,6 +93,19 @@ Deno.test("profile settings requests use scoped API and strictly validate respon
}
});
Deno.test("profile source byte counts accept nonnegative safe integers and reject missing or malformed values", () => {
for (const sizeBytes of [0, 128, Number.MAX_SAFE_INTEGER]) {
const value = profileSettingsFixture();
(value.sources as Record<string, unknown>[])[0].size_bytes = sizeBytes;
assertEquals(parseProfileSettingsResponse(value).sources[0].size_bytes, sizeBytes);
}
for (const sizeBytes of [undefined, null, -1, 1.5, Number.MAX_SAFE_INTEGER + 1, "128"]) {
const value = profileSettingsFixture();
(value.sources as Record<string, unknown>[])[0].size_bytes = sizeBytes;
assertThrows(() => parseProfileSettingsResponse(value), ProfileApiError);
}
});
Deno.test("workspace metadata requests use generated DTO shapes", async () => {
const originalFetch = globalThis.fetch;
const requests: Array<{ url: string; init?: RequestInit }> = [];
@@ -247,8 +260,7 @@ Deno.test("Workspace signing identity parser validates active and pending public
value.private_material_ref = "must-not-be-accepted";
},
(value: Record<string, unknown>) => {
(value.identity as Record<string, unknown>).revision =
Number.MAX_SAFE_INTEGER + 1;
(value.identity as Record<string, unknown>).unexpected_field = true;
},
(value: Record<string, unknown>) => {
(value.identity as Record<string, unknown>).public_key = "x".repeat(
@@ -287,7 +299,7 @@ Deno.test("workspace metadata parser rejects incomplete or stale response fields
workspace_id: "workspace-test",
display_name: "Workspace",
created_at: "2026-01-01T00:00:00Z",
revision: "sha256:metadata",
updated_at: "2026-01-02T00:00:00Z",
source: "workspace-config",
diagnostics: [],
workspace_path: "/legacy/path",
@@ -199,10 +199,10 @@ Deno.test("Runtime validators reject unknown object keys and enum variants", ()
});
Deno.test("Runtime validators reject invalid binding identities and bounded collection overflow", () => {
const unsafeRevision = structuredClone(detail());
Object.assign(unsafeRevision.trust_key, { binding_id: 1 });
const invalidBinding = structuredClone(detail());
Object.assign(invalidBinding.trust_key, { binding_id: 1 });
assertThrows(
() => parseWorkspaceRuntimeDetail(unsafeRevision),
() => parseWorkspaceRuntimeDetail(invalidBinding),
"must be a string",
);
@@ -506,11 +506,11 @@ Deno.test("Runtime trust replacement preserves binding conflicts without retryin
}
});
Deno.test("Runtime trust replacement rejects legacy counter conflict responses", async () => {
Deno.test("Runtime trust replacement rejects unknown fields and missing binding identity in conflict responses", async () => {
const fetchImpl = (() => Promise.resolve(Response.json({
error: "stale_binding",
message: "Stale trust",
current_revision: 3,
unexpected_field: true,
}, { status: 409 }))) as typeof fetch;
try {
await createRemoteRuntime("workspace-a", {
+4 -4
View File
@@ -161,11 +161,11 @@ Deno.test("Skill parser fails closed on unknown fields and newer enum values", (
);
});
Deno.test("Skill parser rejects removed revision fields and oversized collections or strings", () => {
const legacyRevision = catalogFixture();
(legacyRevision.projection as Record<string, unknown>).config_revision = 1;
Deno.test("Skill parser rejects unknown projection fields and oversized collections or strings", () => {
const legacyCounter = catalogFixture();
(legacyCounter.projection as Record<string, unknown>).unexpected_field = 1;
assertContractError(
() => parseSkillCatalogResponse(legacyRevision),
() => parseSkillCatalogResponse(legacyCounter),
"unknown fields",
);
+19 -4
View File
@@ -45,7 +45,7 @@ Deno.test("workspace creation rejects missing and malformed repository response
for (
const value of [undefined, false, {}, {
...currentInitialRepository,
source_revision: -1,
source_fingerprint: -1,
}]
) {
assertThrows(
@@ -152,13 +152,13 @@ Deno.test("stale repository aliases fail closed at the JSON boundary", () => {
);
});
Deno.test("repository JSON rejects removed source revision counters", () => {
Deno.test("repository JSON rejects unknown fields", () => {
const stale = structuredClone(repositoryList) as Record<string, unknown>;
const items = stale.items as Array<Record<string, unknown>>;
items[0].source_revision = 1;
items[0].unexpected_field = 1;
assertThrows(
() => parseRepositoryListResponse(stale),
".source_revision is not part of the wire contract",
".unexpected_field is not part of the wire contract",
);
});
@@ -322,6 +322,21 @@ Deno.test("Workspace deletion DTOs fail closed and preserve durable operation st
}),
"unexpected is not part",
);
assertThrows(
() =>
parseWorkspaceDeletionPreflightResponse({
...preflight,
expected_workspace_updated_at: "x".repeat(129),
}),
".expected_workspace_updated_at is too long",
);
for (const parse of [parseWorkspaceDeletionPreflightResponse, parseWorkspaceDeletionOperationResponse]) {
const value = parse === parseWorkspaceDeletionPreflightResponse ? preflight : operation;
assertThrows(
() => parse({ ...value, blockers: [{ ...operation.blockers[0], kind: "unknown_blocker" }] }),
".kind is invalid",
);
}
assertThrows(
() =>
parseWorkspaceDeletionOperationResponse({