chore: sync the frontend template from 4dc033c to 8157ad4
The product was materialized from the template at `4dc033c` and has stayed on it through 43 template commits, so it was missing all three rounds of adapter remediation — including files it never had, such as the shared `abortable-operation` primitive and the `exact-snapshot` decoder that later fixes are written against. Taking only the newest round was not possible for that reason: the delta is coherent only as a whole. The product had not touched `src/adapters` at all since materialization, so the 140-file delta applied with a three-way merge and no conflicts. `package.json` was the single overlap and merged cleanly: the product owns `name`, the template contributed `check:adapter-inventory`, `check:remediation-ledger` and the image-resolve-signal type fixture. All 24 product-owned files — README, index.html, CI workflow, i18n catalog, home page, generated schemas, evidence scripts, component and visual snapshots — are byte-identical to `main`. `template.lock.json` now pins the synced revision and tree. Verified in this repository, not inherited from the template: six type projects, lint, nine gates (adapter inventory, remediation ledger, registries, diagnostics, realtime boundaries, architecture, browser file/storage boundaries, optional recipes, documentation), the production build, and 2,054 of 2,073 tests. The 19 failures are all in `tests/unit/ci-artifact-contract.test.ts` and are the same pre-existing sandbox RLIMIT, EMFILE, umask and `/tmp` permission behaviour the template records; four suites that failed once under parallel load pass in isolation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
002ba3624e
commit
4bff9ca151
@@ -6,7 +6,17 @@ import {
|
||||
} from "../adapters/auth/external-session-adapter.ts";
|
||||
import { createDiagnosticsAdapter } from "../adapters/diagnostics/bounded-diagnostics.ts";
|
||||
import { createHttpClient } from "../adapters/http/client.ts";
|
||||
import { createContractHttpExecutor } from "../adapters/http/http-execution-v3.ts";
|
||||
import {
|
||||
createContractHttpExecutor,
|
||||
type HttpExecutionObservation,
|
||||
} from "../adapters/http/http-execution-v3.ts";
|
||||
import {
|
||||
attemptBucket,
|
||||
durationBucket,
|
||||
statusGroup,
|
||||
type DiagnosticRecordInput,
|
||||
} from "../contracts/diagnostics.ts";
|
||||
import type { TelemetryEventName } from "../contracts/telemetry.ts";
|
||||
import { createBrowserCrossContextInvalidationFromHost } from "../adapters/cross-context-invalidation/index.ts";
|
||||
import {
|
||||
createTanStackCacheCoordinator,
|
||||
@@ -19,7 +29,10 @@ import { createBrowserMutationIntentFactory } from "../adapters/platform/browser
|
||||
import { createTelemetryAdapter } from "../adapters/telemetry/best-effort-telemetry.ts";
|
||||
import type { AuthSessionPort } from "../application/ports/auth-session-port.ts";
|
||||
import type { ReleaseInfo } from "../application/ports/release-info-port.ts";
|
||||
import { createRestProviderProfile } from "../contracts/rest-profiles.ts";
|
||||
import {
|
||||
createRestProviderProfile,
|
||||
INSTALLED_REST_AUTH_PROFILES,
|
||||
} from "../contracts/rest-profiles.ts";
|
||||
import type { ClockPort } from "../application/ports/clock-port.ts";
|
||||
import type { MutationIntent } from "../contracts/mutation-intent.ts";
|
||||
import { createInstalledFeatureInputs } from "../features/installed-feature-adapters.ts";
|
||||
@@ -162,6 +175,89 @@ export function createRuntimeHttpClient(
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* VD-07. Exactly one diagnostic per logical V3 execution and exactly one
|
||||
* `api.request.failed` telemetry event per terminal non-abort failure.
|
||||
*
|
||||
* The projection is closed: only registered context keys and bucketed values
|
||||
* reach the sinks, and neither sink can change the HTTP outcome, because the
|
||||
* caller invokes this inside the executor's isolated observation boundary.
|
||||
*/
|
||||
export function createHttpObservationProjector(
|
||||
sinks: Readonly<{
|
||||
diagnostics: Readonly<{ record(input: DiagnosticRecordInput): void }>;
|
||||
telemetry: Readonly<{
|
||||
emit(
|
||||
eventName: TelemetryEventName,
|
||||
attributes: Record<string, unknown>,
|
||||
): void;
|
||||
}>;
|
||||
}>,
|
||||
): (observation: HttpExecutionObservation) => void {
|
||||
return (observation) => {
|
||||
const safeAttributes = {
|
||||
route_id: observation.routeId,
|
||||
operation_id: observation.operationId,
|
||||
error_kind: observation.errorKind,
|
||||
http_status_group: statusGroup(observation.status),
|
||||
attempt_count_bucket: attemptBucket(observation.attemptCount),
|
||||
duration_bucket: durationBucket(observation.durationMs),
|
||||
};
|
||||
try {
|
||||
sinks.diagnostics.record({
|
||||
level: observation.outcome === "SUCCESS" ? "info" : "warn",
|
||||
eventId: "http.request.completed",
|
||||
context: {
|
||||
...safeAttributes,
|
||||
operation: observation.diagnosticsOperation,
|
||||
outcome: observation.outcome,
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
// Diagnostics cannot change a contract execution outcome.
|
||||
}
|
||||
if (!isTerminalNonAbortFailure(observation)) return;
|
||||
try {
|
||||
sinks.telemetry.emit("api.request.failed", { ...safeAttributes });
|
||||
} catch {
|
||||
// Telemetry cannot change a contract execution outcome.
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* LIVE-05. Cancellation and scope fencing are caller- or generation-owned
|
||||
* decisions, not API failures: they produce a diagnostic once and never
|
||||
* `api.request.failed`.
|
||||
*
|
||||
* A `DEADLINE` owner is the opposite case. Nobody asked for it — the API did
|
||||
* not answer inside the contract's own budget — so excluding it would hide
|
||||
* exactly the outage this event exists to report.
|
||||
*/
|
||||
const CALLER_OWNED_CANCELLATION: ReadonlySet<string> = new Set([
|
||||
"CALLER",
|
||||
"ROUTE_TRANSITION",
|
||||
"SCOPE_FENCE",
|
||||
"APPLICATION_SHUTDOWN",
|
||||
]);
|
||||
|
||||
function isTerminalNonAbortFailure(
|
||||
observation: HttpExecutionObservation,
|
||||
): boolean {
|
||||
if (observation.outcome === "SUCCESS") return false;
|
||||
if (observation.outcome === "CANCELLED") return false;
|
||||
if (
|
||||
observation.cancellationOwner !== undefined &&
|
||||
CALLER_OWNED_CANCELLATION.has(observation.cancellationOwner)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return !(
|
||||
observation.outcome === "CONTRACT_VIOLATION" &&
|
||||
observation.errorKind === "SCOPE_FENCED"
|
||||
);
|
||||
}
|
||||
|
||||
export async function createRuntimeAdapters(
|
||||
context: RuntimeAdaptersContext,
|
||||
) {
|
||||
@@ -299,7 +395,10 @@ export async function createRuntimeAdapters(
|
||||
baseUrl: config.API_BASE_URL,
|
||||
maxRetryAttempts: config.MAX_RETRY_ATTEMPTS,
|
||||
fetcher: context.fetcher,
|
||||
async attachCredentials(operation) {
|
||||
// §7.7. The installed registry owns Fetch credentials and the exact
|
||||
// credential-header sets; this collaborator only supplies proof headers.
|
||||
authProfiles: INSTALLED_REST_AUTH_PROFILES,
|
||||
async attachCredentials(operation, authContext) {
|
||||
if (serverStateScope.getPhase() !== "READY") {
|
||||
return Object.freeze({ kind: "SCOPE_FENCED" as const });
|
||||
}
|
||||
@@ -311,49 +410,36 @@ export async function createRuntimeAdapters(
|
||||
return Object.freeze({ kind: "UNAUTHENTICATED" as const });
|
||||
}
|
||||
try {
|
||||
const patch = await authSession.credentialPatch({
|
||||
origin: new URL(config.API_BASE_URL).origin,
|
||||
method: operation.method,
|
||||
operationId: operation.operationId,
|
||||
});
|
||||
const patch = await authSession.credentialPatch(
|
||||
{
|
||||
origin: new URL(config.API_BASE_URL).origin,
|
||||
method: operation.method,
|
||||
operationId: operation.operationId,
|
||||
},
|
||||
authContext,
|
||||
);
|
||||
if (serverStateScope.getPhase() !== "READY") {
|
||||
return Object.freeze({ kind: "SCOPE_FENCED" as const });
|
||||
}
|
||||
return Object.freeze({
|
||||
kind: "READY" as const,
|
||||
headers: patch.headers,
|
||||
credentials: "omit" as const,
|
||||
});
|
||||
} catch {
|
||||
return Object.freeze({ kind: "UNAVAILABLE" as const });
|
||||
}
|
||||
},
|
||||
observe(observation) {
|
||||
try {
|
||||
diagnostics.record({
|
||||
level:
|
||||
observation.outcome === "SUCCESS" ? "info" : "warn",
|
||||
eventId: "http.request.completed",
|
||||
context: {
|
||||
operation_id: observation.diagnosticsOperation,
|
||||
outcome: observation.outcome,
|
||||
attempts: observation.attempts,
|
||||
certainty: observation.certainty,
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
// Diagnostics cannot change a contract execution outcome.
|
||||
}
|
||||
},
|
||||
observe: createHttpObservationProjector({ diagnostics, telemetry }),
|
||||
});
|
||||
const contractOperations = Object.freeze({
|
||||
async execute(
|
||||
operationId: string,
|
||||
input: unknown,
|
||||
executionContext: Readonly<{
|
||||
routeId: string;
|
||||
signal?: AbortSignal;
|
||||
intent?: MutationIntent;
|
||||
}> = {},
|
||||
}>,
|
||||
) {
|
||||
const operation =
|
||||
COMPOSED_CONTRACT_CONTRIBUTIONS.httpByOperationId.get(operationId);
|
||||
@@ -368,6 +454,7 @@ export async function createRuntimeAdapters(
|
||||
});
|
||||
}
|
||||
const outcome = await contractHttp.execute(operation, input, {
|
||||
routeId: executionContext.routeId,
|
||||
scope: serverStateScope.getSnapshot(),
|
||||
...(executionContext.signal === undefined
|
||||
? {}
|
||||
@@ -410,6 +497,10 @@ export async function createRuntimeAdapters(
|
||||
crossContextInvalidationStatus: () =>
|
||||
serverStateGeneration.getSnapshot().crossContextStatus(),
|
||||
dispose() {
|
||||
// N-04. Telemetry is torn down first: it must stop scheduling and
|
||||
// delivering before the diagnostics and state dependencies it observes
|
||||
// are destroyed.
|
||||
telemetry.dispose();
|
||||
conditionalValidators.clear();
|
||||
serverStateScope.dispose();
|
||||
serverStateGeneration.dispose();
|
||||
|
||||
Reference in New Issue
Block a user