fix: complete Ticket post integration and expose targets

Preserve ordered targets in Workspace ShowTicket and native reads. Complete authenticated post-edit transport, shared dispatch, generated contracts and regression fixtures. Keep canonical content-edit delivery inputs transactional and separate from discussion posts.
This commit is contained in:
2026-10-14 14:49:20 +09:00
parent 8611078058
commit da378fc902
22 changed files with 1311 additions and 178 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:73f7d252fbedf2efbc7d36a36259c72850cfaaede4693009800fb1bec3f295cb)
// Input contract: 0.1.0 (sha256:334f540d268db97ec52ce67092f4d672a5757f11df3589ac54153cd595e95904)
// 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
@@ -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:1d6615147b8d7143484b9a2aaf163da6916fcabb7560c675c5b7bf4d94aa2214)
// Input contract: 0.1.0 (sha256:334f540d268db97ec52ce67092f4d672a5757f11df3589ac54153cd595e95904)
// 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:73f7d252fbedf2efbc7d36a36259c72850cfaaede4693009800fb1bec3f295cb)
// Input contract: 0.1.0 (sha256:334f540d268db97ec52ce67092f4d672a5757f11df3589ac54153cd595e95904)
// 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:1d6615147b8d7143484b9a2aaf163da6916fcabb7560c675c5b7bf4d94aa2214)
// Input contract: 0.1.0 (sha256:334f540d268db97ec52ce67092f4d672a5757f11df3589ac54153cd595e95904)
// 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-worker-openapi-typescript@1. Do not edit.
// Input: openapi/server-api.json
// Input contract: 0.1.0 (sha256:73f7d252fbedf2efbc7d36a36259c72850cfaaede4693009800fb1bec3f295cb)
// Input contract: 0.1.0 (sha256:334f540d268db97ec52ce67092f4d672a5757f11df3589ac54153cd595e95904)
// 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
@@ -54,23 +54,6 @@ function reads(path: string, row: TicketAssignmentTrackingProjection) {
if (path.includes("/assignment-tracking/assignments")) {
return Response.json({ items: [row], next_assignment_id: null });
}
if (path.includes("/assignment-diagnostics")) {
return Response.json({
assignment_id: row.assignment_id,
current: true,
actionable: true,
input_origin_known: true,
inputs: [{
input_event_id: "input",
delivery_id: "delivery",
notification: "request_persisted",
acceptance_outcome_unknown: true,
}],
starts: [],
next_input_event_id: null,
next_start_operation_id: null,
});
}
throw new Error(`Unexpected read ${path}`);
}
test("an answer response loss keeps exact assignment/event/key/body for retry even when observed responsibility changes", async () => {
@@ -126,9 +109,11 @@ test("an answer response loss keeps exact assignment/event/key/body for retry ev
test("a done reissue persists before a failed refresh and does not pretend delivery or execution succeeded", async () => {
const row = assignment("delegation-1", "done");
const sent: unknown[] = [];
const paths: string[] = [];
vi.stubGlobal(
"fetch",
vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
paths.push(String(input));
if (init?.method === "PUT") {
sent.push(JSON.parse(String(init.body)));
return Response.json({
@@ -164,13 +149,14 @@ test("a done reissue persists before a failed refresh and does not pretend deliv
expect(sent).toHaveLength(1);
expect(sent[0]).toMatchObject({ expected_assignment_id: "delegation-1" });
expect(view.queryByText("Retry exact operation")).toBeNull();
expect(view.container.textContent).toContain("Outcome unknown");
expect(view.container.textContent).toContain(
"Outcome unknown — may already be accepted",
);
expect(view.container.textContent).toContain(
"No work start records observed",
await waitFor(() =>
expect(view.getByRole("alert").textContent).toContain(
"Operation saved; latest projection unavailable",
)
);
expect(view.container.querySelector(".ticket-delivery")).toBeNull();
expect(view.queryByText("Delivery and execution")).toBeNull();
expect(paths.some((path) => path.includes("assignment-diagnostics"))).toBe(false);
expect(view.container.textContent).toContain("Self-reported handling: done");
});
test("classification conflict keeps stable IDs and the original observed set across an exact retry", async () => {