Add governed form handoffs

This commit is contained in:
2026-08-01 20:57:26 +02:00
parent 9dc49fe27b
commit b4388b1e1e
15 changed files with 2236 additions and 89 deletions
+101
View File
@@ -19,6 +19,10 @@ export type EvidenceReference = {
version?: string | null;
};
export type FormCondition =
| { kind: "predicate"; field_key: string; operator: string; value?: unknown }
| { kind: "all" | "any" | "not"; conditions: FormCondition[] };
export type FormFieldDefinition = {
key: string;
label: string;
@@ -28,6 +32,7 @@ export type FormFieldDefinition = {
options: string[];
constraints: Record<string, unknown>;
default_value?: unknown;
visibility_condition?: FormCondition | null;
};
export type FormDefinition = {
@@ -43,6 +48,30 @@ export type FormDefinition = {
signature_requirement: "none" | "optional" | "required";
policy_refs: string[];
handoff_kinds: string[];
pages?: Array<{
key: string;
title: string;
description?: string | null;
visibility_condition?: FormCondition | null;
sections: Array<{
key: string;
title: string;
description?: string | null;
field_keys: string[];
visibility_condition?: FormCondition | null;
}>;
}>;
fallback_locale?: string | null;
localizations?: Array<{
locale: string;
title?: string | null;
description?: string | null;
field_labels: Record<string, string>;
field_help_texts: Record<string, string>;
option_labels: Record<string, Record<string, string>>;
page_titles: Record<string, string>;
section_titles: Record<string, string>;
}>;
};
export type ValidationResult = {
@@ -83,6 +112,24 @@ export type FormInstanceEvent = {
payload: Record<string, unknown>;
};
export type FormHandoff = {
effect_id: string;
instance_id: string;
instance_revision: number;
binding_kind: "case" | "workflow";
binding_reference: string;
provider_capability: string;
state: "requested" | "accepted" | "rejected" | "outcome_unknown" | "reconciled" | "compensated";
attempt_count: number;
requested_at: string;
resolved_at?: string | null;
target_ref?: InstitutionalReference | null;
href?: string | null;
evidence: EvidenceReference[];
last_error?: string | null;
metadata: Record<string, unknown>;
};
export function listFormInstances(
settings: ApiSettings,
options: { statuses?: string[]; definitionId?: string; offset?: number; limit?: number } = {},
@@ -169,3 +216,57 @@ export function submitFormInstance(
})
});
}
export function listFormHandoffs(
settings: ApiSettings,
instanceId: string,
signal?: AbortSignal
): Promise<{ handoffs: FormHandoff[] }> {
return apiFetch(settings, `/api/v1/forms-runtime/instances/${encodeURIComponent(instanceId)}/handoffs`, { signal });
}
export function startFormHandoff(
settings: ApiSettings,
instance: FormInstance,
bindingKind: "case" | "workflow",
bindingReference?: string
): Promise<{ handoff: FormHandoff; instance?: FormInstance | null }> {
return apiFetch(settings, `/api/v1/forms-runtime/instances/${encodeURIComponent(instance.instance_id)}/handoffs/native`, {
method: "POST",
body: JSON.stringify({
expected_revision: instance.revision,
binding_kind: bindingKind,
binding_reference: bindingReference?.trim() || null,
idempotency_key: crypto.randomUUID(),
requested_at: new Date().toISOString()
})
});
}
export function actOnFormHandoff(
settings: ApiSettings,
instanceId: string,
effectId: string,
action: "retry" | "reconcile"
): Promise<{ handoff: FormHandoff; instance?: FormInstance | null }> {
return apiFetch(settings, `/api/v1/forms-runtime/instances/${encodeURIComponent(instanceId)}/handoffs/${encodeURIComponent(effectId)}/${action}`, {
method: "POST",
body: JSON.stringify({ recorded_at: new Date().toISOString() })
});
}
export function compensateFormHandoff(
settings: ApiSettings,
instanceId: string,
effectId: string,
changeReason: string
): Promise<{ handoff: FormHandoff }> {
return apiFetch(settings, `/api/v1/forms-runtime/instances/${encodeURIComponent(instanceId)}/handoffs/${encodeURIComponent(effectId)}/compensate`, {
method: "POST",
body: JSON.stringify({
recorded_at: new Date().toISOString(),
confirmed_absent: true,
change_reason: changeReason
})
});
}