() => T extends B ? 1 : 2 ? true : false;
+type GetPaths = { [K in keyof P]: P[K] extends { get: unknown } ? K : never }[keyof P];
+type GetAt
= P[K] extends { get: infer G } ? G : never;
+type GetOperationNames
= {
+ [N in keyof O]: true extends {
+ [K in GetPaths
]: Same>;
+ }[GetPaths]
+ ? N
+ : never;
+}[keyof O];
+type PairChecks
= {
+ [K in keyof C]: K extends keyof P
+ ? C[K] extends keyof O
+ ? Same, O[C[K]]>
+ : false
+ : false;
+}[keyof C];
+
+/** Fails closed on missing, overlapping, extra or incorrectly paired GET ownership. */
+export type GetContractOwnership = false extends
+ | Same, keyof Public | keyof Authenticated>
+ | Same, never>
+ | Same, Public[keyof Public] | Authenticated[keyof Authenticated]>
+ | Same, never>
+ | PairChecks
+ | PairChecks
+ ? false
+ : true;
diff --git a/frontend/packages/sdk/src/contract-ownership-compiler.test.ts b/frontend/packages/sdk/src/contract-ownership-compiler.test.ts
new file mode 100644
index 00000000..361bba30
--- /dev/null
+++ b/frontend/packages/sdk/src/contract-ownership-compiler.test.ts
@@ -0,0 +1,53 @@
+import * as ts from 'typescript';
+import { expect, it } from 'vitest';
+
+const serverPath = ts.sys.resolvePath('src/server.ts');
+const schemaPath = ts.sys.resolvePath('src/generated/schema.d.ts');
+const server = ts.sys.readFile(serverPath);
+const schema = ts.sys.readFile(schemaPath);
+if (server === undefined || schema === undefined) throw new Error('Missing SDK compiler subjects');
+
+function errors(source: string, generated: string): string[] {
+ const options: ts.CompilerOptions = {
+ target: ts.ScriptTarget.ES2022,
+ module: ts.ModuleKind.ESNext,
+ moduleResolution: ts.ModuleResolutionKind.Bundler,
+ strict: true,
+ noEmit: true,
+ skipLibCheck: true,
+ types: [],
+ };
+ const host = ts.createCompilerHost(options);
+ const read = host.readFile.bind(host);
+ host.readFile = (path) =>
+ path === serverPath ? source : path === schemaPath ? generated : read(path);
+ const program = ts.createProgram([serverPath], options, host);
+ return ts
+ .getPreEmitDiagnostics(program)
+ .map((diagnostic) => ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'));
+}
+
+it('the production ownership binding rejects an unowned generated GET, and removing it loses that protection', () => {
+ expect(server).toContain('true satisfies GetContractOwnership<');
+ expect(errors(server, schema)).toEqual([]);
+ const unowned =
+ schema +
+ "\nexport interface paths { '/api/v1/unowned': { get: operations['GetAccountSite'] }; }\n";
+ expect(errors(server, unowned)).toEqual([
+ expect.stringContaining("Type 'true' does not satisfy the expected type 'false'"),
+ ]);
+ const unchecked = server.replace(/true satisfies GetContractOwnership<[\s\S]*?>;/, 'true;');
+ expect(unchecked).not.toBe(server);
+ expect(errors(unchecked, unowned)).toEqual([]);
+});
+
+it('the production wrapper map refuses a valid SDK method paired with the wrong operation', () => {
+ const wrong = server.replace(
+ "'/api/v1/public/site': 'getSite'",
+ "'/api/v1/public/site': 'getCourses'",
+ );
+ expect(wrong).not.toBe(server);
+ expect(errors(wrong, schema)).toEqual([
+ expect.stringContaining('Type \'"getCourses"\' is not assignable to type \'"getSite"\''),
+ ]);
+});
diff --git a/frontend/packages/sdk/src/generated/schema.d.ts b/frontend/packages/sdk/src/generated/schema.d.ts
index 854b1fdf..97efd389 100644
--- a/frontend/packages/sdk/src/generated/schema.d.ts
+++ b/frontend/packages/sdk/src/generated/schema.d.ts
@@ -4,6 +4,23 @@
*/
export interface paths {
+ "/api/v1/account/site": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /** @description Authenticated host-backed read through the physical read-only pipeline. Returns only the validated actor UUID, resolved tenant/organization UUIDs and existing public site projection. Accepts no query parameters or client-selected identifiers. No response caching or validators. */
+ get: operations["GetAccountSite"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
"/api/v1/public/courses": {
parameters: {
query?: never;
@@ -11,13 +28,13 @@ export interface paths {
path?: never;
cookie?: never;
};
- /** @description Anonymous host-scoped read through the read-only pipeline. Unknown query keys and repeated single-valued keys are refused. No response caching or validators. */
+ /** @description Anonymous host-scoped read through the read-only pipeline. Present invalid credentials are refused rather than treated as anonymous. Unknown query keys and repeated single-valued keys are refused. No response caching or validators. */
get: operations["GetPublicCourses"];
put?: never;
post?: never;
delete?: never;
options?: never;
- /** @description Anonymous host-scoped read through the read-only pipeline. Unknown query keys and repeated single-valued keys are refused. No response caching or validators. */
+ /** @description Anonymous host-scoped read through the read-only pipeline. Present invalid credentials are refused rather than treated as anonymous. Unknown query keys and repeated single-valued keys are refused. No response caching or validators. */
head: operations["HeadPublicCourses"];
patch?: never;
trace?: never;
@@ -29,13 +46,13 @@ export interface paths {
path?: never;
cookie?: never;
};
- /** @description Anonymous host-scoped read through the read-only pipeline. Unknown query keys and repeated single-valued keys are refused. No response caching or validators. */
+ /** @description Anonymous host-scoped read through the read-only pipeline. Present invalid credentials are refused rather than treated as anonymous. Unknown query keys and repeated single-valued keys are refused. No response caching or validators. */
get: operations["GetPublicCourse"];
put?: never;
post?: never;
delete?: never;
options?: never;
- /** @description Anonymous host-scoped read through the read-only pipeline. Unknown query keys and repeated single-valued keys are refused. No response caching or validators. */
+ /** @description Anonymous host-scoped read through the read-only pipeline. Present invalid credentials are refused rather than treated as anonymous. Unknown query keys and repeated single-valued keys are refused. No response caching or validators. */
head: operations["HeadPublicCourse"];
patch?: never;
trace?: never;
@@ -47,13 +64,13 @@ export interface paths {
path?: never;
cookie?: never;
};
- /** @description Anonymous host-scoped read through the read-only pipeline. Unknown query keys and repeated single-valued keys are refused. No response caching or validators. */
+ /** @description Anonymous host-scoped read through the read-only pipeline. Present invalid credentials are refused rather than treated as anonymous. Unknown query keys and repeated single-valued keys are refused. No response caching or validators. */
get: operations["GetPublicLesson"];
put?: never;
post?: never;
delete?: never;
options?: never;
- /** @description Anonymous host-scoped read through the read-only pipeline. Unknown query keys and repeated single-valued keys are refused. No response caching or validators. */
+ /** @description Anonymous host-scoped read through the read-only pipeline. Present invalid credentials are refused rather than treated as anonymous. Unknown query keys and repeated single-valued keys are refused. No response caching or validators. */
head: operations["HeadPublicLesson"];
patch?: never;
trace?: never;
@@ -65,13 +82,13 @@ export interface paths {
path?: never;
cookie?: never;
};
- /** @description Anonymous host-scoped read through the read-only pipeline. Unknown query keys and repeated single-valued keys are refused. No response caching or validators. */
+ /** @description Anonymous host-scoped read through the read-only pipeline. Present invalid credentials are refused rather than treated as anonymous. Unknown query keys and repeated single-valued keys are refused. No response caching or validators. */
get: operations["GetPublicSite"];
put?: never;
post?: never;
delete?: never;
options?: never;
- /** @description Anonymous host-scoped read through the read-only pipeline. Unknown query keys and repeated single-valued keys are refused. No response caching or validators. */
+ /** @description Anonymous host-scoped read through the read-only pipeline. Present invalid credentials are refused rather than treated as anonymous. Unknown query keys and repeated single-valued keys are refused. No response caching or validators. */
head: operations["HeadPublicSite"];
patch?: never;
trace?: never;
@@ -80,6 +97,14 @@ export interface paths {
export type webhooks = Record;
export interface components {
schemas: {
+ AccountSite: {
+ actorId: components["schemas"]["UserId"];
+ organizationId: null | components["schemas"]["OrganizationId"];
+ site: components["schemas"]["PublicSite"];
+ tenantId: components["schemas"]["TenantId"];
+ };
+ /** Format: uuid */
+ OrganizationId: string;
PageInfo: {
hasNext: boolean;
hasPrevious: boolean;
@@ -194,6 +219,10 @@ export interface components {
muted: string;
primary: string;
};
+ /** Format: uuid */
+ TenantId: string;
+ /** Format: uuid */
+ UserId: string;
};
responses: never;
parameters: never;
@@ -203,6 +232,113 @@ export interface components {
}
export type $defs = Record;
export interface operations {
+ GetAccountSite: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description OK */
+ 200: {
+ headers: {
+ "Cache-Control": "no-store";
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["AccountSite"];
+ };
+ };
+ /** @description validation_failed; no query parameters are accepted, and malformed or repeated assertion headers are refused. */
+ 400: {
+ headers: {
+ "Cache-Control": "no-store";
+ [name: string]: unknown;
+ };
+ content: {
+ "application/problem+json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description unauthorized; present invalid or undecidable credentials on a known host. Bare Bearer challenge; no credential or provider details. */
+ 401: {
+ headers: {
+ "Cache-Control": "no-store";
+ "WWW-Authenticate": "Bearer";
+ [name: string]: unknown;
+ };
+ content: {
+ "application/problem+json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description not_found for absent credentials, unknown hosts, membership refusals or missing host-backed authority; tenant_mismatch for authenticated host/claim or header disagreement, without disclosure. */
+ 404: {
+ headers: {
+ "Cache-Control": "no-store";
+ [name: string]: unknown;
+ };
+ content: {
+ "application/problem+json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description method_not_allowed from routing after credential and host-backed authority admission; this operation admits GET only. Absent credentials remain masked as 404. */
+ 405: {
+ headers: {
+ Allow: "GET";
+ "Cache-Control": "no-store";
+ [name: string]: unknown;
+ };
+ content: {
+ "application/problem+json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description payload_too_large; the decoded body bound precedes host classification and authentication. Kestrel framing/backstop refusals may occur outside the application response policy and omit these headers/body. */
+ 413: {
+ headers: {
+ "Cache-Control"?: "no-store";
+ [name: string]: unknown;
+ };
+ content: {
+ "application/problem+json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description rate_limited from early visitor/peer admission, validated-subject quota or subject-registry capacity. */
+ 429: {
+ headers: {
+ "Cache-Control": "no-store";
+ /** @description Suggested retry delay in seconds for the first refusing admission boundary. A capacity hint does not promise an available registry slot; quota hints do not guarantee admission. */
+ "Retry-After": number;
+ [name: string]: unknown;
+ };
+ content: {
+ "application/problem+json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description internal_error from unexpected failures; no exception or query details. */
+ 500: {
+ headers: {
+ "Cache-Control": "no-store";
+ [name: string]: unknown;
+ };
+ content: {
+ "application/problem+json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description dependency_unavailable from invalid stored public configuration or a sticky subject-registry cleanup fault. */
+ 503: {
+ headers: {
+ "Cache-Control": "no-store";
+ /** @description Suggested dependency retry delay in seconds; not a recovery guarantee. */
+ "Retry-After": number;
+ [name: string]: unknown;
+ };
+ content: {
+ "application/problem+json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ };
+ };
GetPublicCourses: {
parameters: {
query: {
@@ -239,7 +375,18 @@ export interface operations {
"application/problem+json": components["schemas"]["ProblemDetails"];
};
};
- /** @description not_found; unavailable host or hidden/untranslated/ineligible resource, without disclosure. */
+ /** @description unauthorized; present invalid or undecidable credentials on a known host. Bare Bearer challenge; no credential or provider details. */
+ 401: {
+ headers: {
+ "Cache-Control": "no-store";
+ "WWW-Authenticate": "Bearer";
+ [name: string]: unknown;
+ };
+ content: {
+ "application/problem+json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description not_found for unavailable host or hidden/untranslated/ineligible resource; tenant_mismatch for authenticated host/claim or header disagreement, without disclosure. */
404: {
headers: {
"Cache-Control": "no-store";
@@ -252,6 +399,7 @@ export interface operations {
/** @description method_not_allowed from routing. */
405: {
headers: {
+ Allow: "GET, HEAD";
"Cache-Control": "no-store";
[name: string]: unknown;
};
@@ -259,10 +407,22 @@ export interface operations {
"application/problem+json": components["schemas"]["ProblemDetails"];
};
};
- /** @description rate_limited from the anonymous limiter. */
+ /** @description payload_too_large; the decoded body bound precedes host classification and authentication. Kestrel framing/backstop refusals may occur outside the application response policy and omit these headers/body. */
+ 413: {
+ headers: {
+ "Cache-Control"?: "no-store";
+ [name: string]: unknown;
+ };
+ content: {
+ "application/problem+json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description rate_limited from early visitor/peer admission, validated-subject quota or subject-registry capacity. */
429: {
headers: {
"Cache-Control": "no-store";
+ /** @description Suggested retry delay in seconds for the first refusing admission boundary. A capacity hint does not promise an available registry slot; quota hints do not guarantee admission. */
+ "Retry-After": number;
[name: string]: unknown;
};
content: {
@@ -279,10 +439,12 @@ export interface operations {
"application/problem+json": components["schemas"]["ProblemDetails"];
};
};
- /** @description dependency_unavailable from invalid stored public configuration. */
+ /** @description dependency_unavailable from invalid stored public configuration or a sticky subject-registry cleanup fault. */
503: {
headers: {
"Cache-Control": "no-store";
+ /** @description Suggested dependency retry delay in seconds; not a recovery guarantee. */
+ "Retry-After": number;
[name: string]: unknown;
};
content: {
@@ -323,7 +485,16 @@ export interface operations {
};
content?: never;
};
- /** @description not_found; unavailable host or hidden/untranslated/ineligible resource, without disclosure. */
+ /** @description unauthorized; present invalid or undecidable credentials on a known host. Bare Bearer challenge; no credential or provider details. */
+ 401: {
+ headers: {
+ "Cache-Control": "no-store";
+ "WWW-Authenticate": "Bearer";
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description not_found for unavailable host or hidden/untranslated/ineligible resource; tenant_mismatch for authenticated host/claim or header disagreement, without disclosure. */
404: {
headers: {
"Cache-Control": "no-store";
@@ -334,15 +505,26 @@ export interface operations {
/** @description method_not_allowed from routing. */
405: {
headers: {
+ Allow: "GET, HEAD";
"Cache-Control": "no-store";
[name: string]: unknown;
};
content?: never;
};
- /** @description rate_limited from the anonymous limiter. */
+ /** @description payload_too_large; the decoded body bound precedes host classification and authentication. Kestrel framing/backstop refusals may occur outside the application response policy and omit these headers/body. */
+ 413: {
+ headers: {
+ "Cache-Control"?: "no-store";
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description rate_limited from early visitor/peer admission, validated-subject quota or subject-registry capacity. */
429: {
headers: {
"Cache-Control": "no-store";
+ /** @description Suggested retry delay in seconds for the first refusing admission boundary. A capacity hint does not promise an available registry slot; quota hints do not guarantee admission. */
+ "Retry-After": number;
[name: string]: unknown;
};
content?: never;
@@ -355,10 +537,12 @@ export interface operations {
};
content?: never;
};
- /** @description dependency_unavailable from invalid stored public configuration. */
+ /** @description dependency_unavailable from invalid stored public configuration or a sticky subject-registry cleanup fault. */
503: {
headers: {
"Cache-Control": "no-store";
+ /** @description Suggested dependency retry delay in seconds; not a recovery guarantee. */
+ "Retry-After": number;
[name: string]: unknown;
};
content?: never;
@@ -404,7 +588,18 @@ export interface operations {
"application/problem+json": components["schemas"]["ProblemDetails"];
};
};
- /** @description not_found; unavailable host or hidden/untranslated/ineligible resource, without disclosure. */
+ /** @description unauthorized; present invalid or undecidable credentials on a known host. Bare Bearer challenge; no credential or provider details. */
+ 401: {
+ headers: {
+ "Cache-Control": "no-store";
+ "WWW-Authenticate": "Bearer";
+ [name: string]: unknown;
+ };
+ content: {
+ "application/problem+json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description not_found for unavailable host or hidden/untranslated/ineligible resource; tenant_mismatch for authenticated host/claim or header disagreement, without disclosure. */
404: {
headers: {
"Cache-Control": "no-store";
@@ -417,6 +612,7 @@ export interface operations {
/** @description method_not_allowed from routing. */
405: {
headers: {
+ Allow: "GET, HEAD";
"Cache-Control": "no-store";
[name: string]: unknown;
};
@@ -424,10 +620,22 @@ export interface operations {
"application/problem+json": components["schemas"]["ProblemDetails"];
};
};
- /** @description rate_limited from the anonymous limiter. */
+ /** @description payload_too_large; the decoded body bound precedes host classification and authentication. Kestrel framing/backstop refusals may occur outside the application response policy and omit these headers/body. */
+ 413: {
+ headers: {
+ "Cache-Control"?: "no-store";
+ [name: string]: unknown;
+ };
+ content: {
+ "application/problem+json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description rate_limited from early visitor/peer admission, validated-subject quota or subject-registry capacity. */
429: {
headers: {
"Cache-Control": "no-store";
+ /** @description Suggested retry delay in seconds for the first refusing admission boundary. A capacity hint does not promise an available registry slot; quota hints do not guarantee admission. */
+ "Retry-After": number;
[name: string]: unknown;
};
content: {
@@ -444,10 +652,12 @@ export interface operations {
"application/problem+json": components["schemas"]["ProblemDetails"];
};
};
- /** @description dependency_unavailable from invalid stored public configuration. */
+ /** @description dependency_unavailable from invalid stored public configuration or a sticky subject-registry cleanup fault. */
503: {
headers: {
"Cache-Control": "no-store";
+ /** @description Suggested dependency retry delay in seconds; not a recovery guarantee. */
+ "Retry-After": number;
[name: string]: unknown;
};
content: {
@@ -491,7 +701,16 @@ export interface operations {
};
content?: never;
};
- /** @description not_found; unavailable host or hidden/untranslated/ineligible resource, without disclosure. */
+ /** @description unauthorized; present invalid or undecidable credentials on a known host. Bare Bearer challenge; no credential or provider details. */
+ 401: {
+ headers: {
+ "Cache-Control": "no-store";
+ "WWW-Authenticate": "Bearer";
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description not_found for unavailable host or hidden/untranslated/ineligible resource; tenant_mismatch for authenticated host/claim or header disagreement, without disclosure. */
404: {
headers: {
"Cache-Control": "no-store";
@@ -502,15 +721,26 @@ export interface operations {
/** @description method_not_allowed from routing. */
405: {
headers: {
+ Allow: "GET, HEAD";
"Cache-Control": "no-store";
[name: string]: unknown;
};
content?: never;
};
- /** @description rate_limited from the anonymous limiter. */
+ /** @description payload_too_large; the decoded body bound precedes host classification and authentication. Kestrel framing/backstop refusals may occur outside the application response policy and omit these headers/body. */
+ 413: {
+ headers: {
+ "Cache-Control"?: "no-store";
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description rate_limited from early visitor/peer admission, validated-subject quota or subject-registry capacity. */
429: {
headers: {
"Cache-Control": "no-store";
+ /** @description Suggested retry delay in seconds for the first refusing admission boundary. A capacity hint does not promise an available registry slot; quota hints do not guarantee admission. */
+ "Retry-After": number;
[name: string]: unknown;
};
content?: never;
@@ -523,10 +753,12 @@ export interface operations {
};
content?: never;
};
- /** @description dependency_unavailable from invalid stored public configuration. */
+ /** @description dependency_unavailable from invalid stored public configuration or a sticky subject-registry cleanup fault. */
503: {
headers: {
"Cache-Control": "no-store";
+ /** @description Suggested dependency retry delay in seconds; not a recovery guarantee. */
+ "Retry-After": number;
[name: string]: unknown;
};
content?: never;
@@ -570,7 +802,18 @@ export interface operations {
"application/problem+json": components["schemas"]["ProblemDetails"];
};
};
- /** @description not_found; unavailable host or hidden/untranslated/ineligible resource, without disclosure. */
+ /** @description unauthorized; present invalid or undecidable credentials on a known host. Bare Bearer challenge; no credential or provider details. */
+ 401: {
+ headers: {
+ "Cache-Control": "no-store";
+ "WWW-Authenticate": "Bearer";
+ [name: string]: unknown;
+ };
+ content: {
+ "application/problem+json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description not_found for unavailable host or hidden/untranslated/ineligible resource; tenant_mismatch for authenticated host/claim or header disagreement, without disclosure. */
404: {
headers: {
"Cache-Control": "no-store";
@@ -583,6 +826,7 @@ export interface operations {
/** @description method_not_allowed from routing. */
405: {
headers: {
+ Allow: "GET, HEAD";
"Cache-Control": "no-store";
[name: string]: unknown;
};
@@ -590,10 +834,22 @@ export interface operations {
"application/problem+json": components["schemas"]["ProblemDetails"];
};
};
- /** @description rate_limited from the anonymous limiter. */
+ /** @description payload_too_large; the decoded body bound precedes host classification and authentication. Kestrel framing/backstop refusals may occur outside the application response policy and omit these headers/body. */
+ 413: {
+ headers: {
+ "Cache-Control"?: "no-store";
+ [name: string]: unknown;
+ };
+ content: {
+ "application/problem+json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description rate_limited from early visitor/peer admission, validated-subject quota or subject-registry capacity. */
429: {
headers: {
"Cache-Control": "no-store";
+ /** @description Suggested retry delay in seconds for the first refusing admission boundary. A capacity hint does not promise an available registry slot; quota hints do not guarantee admission. */
+ "Retry-After": number;
[name: string]: unknown;
};
content: {
@@ -610,10 +866,12 @@ export interface operations {
"application/problem+json": components["schemas"]["ProblemDetails"];
};
};
- /** @description dependency_unavailable from invalid stored public configuration. */
+ /** @description dependency_unavailable from invalid stored public configuration or a sticky subject-registry cleanup fault. */
503: {
headers: {
"Cache-Control": "no-store";
+ /** @description Suggested dependency retry delay in seconds; not a recovery guarantee. */
+ "Retry-After": number;
[name: string]: unknown;
};
content: {
@@ -655,7 +913,16 @@ export interface operations {
};
content?: never;
};
- /** @description not_found; unavailable host or hidden/untranslated/ineligible resource, without disclosure. */
+ /** @description unauthorized; present invalid or undecidable credentials on a known host. Bare Bearer challenge; no credential or provider details. */
+ 401: {
+ headers: {
+ "Cache-Control": "no-store";
+ "WWW-Authenticate": "Bearer";
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description not_found for unavailable host or hidden/untranslated/ineligible resource; tenant_mismatch for authenticated host/claim or header disagreement, without disclosure. */
404: {
headers: {
"Cache-Control": "no-store";
@@ -666,15 +933,26 @@ export interface operations {
/** @description method_not_allowed from routing. */
405: {
headers: {
+ Allow: "GET, HEAD";
"Cache-Control": "no-store";
[name: string]: unknown;
};
content?: never;
};
- /** @description rate_limited from the anonymous limiter. */
+ /** @description payload_too_large; the decoded body bound precedes host classification and authentication. Kestrel framing/backstop refusals may occur outside the application response policy and omit these headers/body. */
+ 413: {
+ headers: {
+ "Cache-Control"?: "no-store";
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description rate_limited from early visitor/peer admission, validated-subject quota or subject-registry capacity. */
429: {
headers: {
"Cache-Control": "no-store";
+ /** @description Suggested retry delay in seconds for the first refusing admission boundary. A capacity hint does not promise an available registry slot; quota hints do not guarantee admission. */
+ "Retry-After": number;
[name: string]: unknown;
};
content?: never;
@@ -687,10 +965,12 @@ export interface operations {
};
content?: never;
};
- /** @description dependency_unavailable from invalid stored public configuration. */
+ /** @description dependency_unavailable from invalid stored public configuration or a sticky subject-registry cleanup fault. */
503: {
headers: {
"Cache-Control": "no-store";
+ /** @description Suggested dependency retry delay in seconds; not a recovery guarantee. */
+ "Retry-After": number;
[name: string]: unknown;
};
content?: never;
@@ -716,7 +996,7 @@ export interface operations {
"application/json": components["schemas"]["PublicSite"];
};
};
- /** @description validation_failed; the site bootstrap accepts no query parameters. */
+ /** @description validation_failed; the site bootstrap accepts no query parameters, and malformed or repeated assertion headers are refused. */
400: {
headers: {
"Cache-Control": "no-store";
@@ -726,7 +1006,18 @@ export interface operations {
"application/problem+json": components["schemas"]["ProblemDetails"];
};
};
- /** @description not_found; unavailable host or hidden/untranslated/ineligible resource, without disclosure. */
+ /** @description unauthorized; present invalid or undecidable credentials on a known host. Bare Bearer challenge; no credential or provider details. */
+ 401: {
+ headers: {
+ "Cache-Control": "no-store";
+ "WWW-Authenticate": "Bearer";
+ [name: string]: unknown;
+ };
+ content: {
+ "application/problem+json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description not_found for unavailable host or hidden/untranslated/ineligible resource; tenant_mismatch for authenticated host/claim or header disagreement, without disclosure. */
404: {
headers: {
"Cache-Control": "no-store";
@@ -739,6 +1030,7 @@ export interface operations {
/** @description method_not_allowed from routing. */
405: {
headers: {
+ Allow: "GET, HEAD";
"Cache-Control": "no-store";
[name: string]: unknown;
};
@@ -746,10 +1038,22 @@ export interface operations {
"application/problem+json": components["schemas"]["ProblemDetails"];
};
};
- /** @description rate_limited from the anonymous limiter. */
+ /** @description payload_too_large; the decoded body bound precedes host classification and authentication. Kestrel framing/backstop refusals may occur outside the application response policy and omit these headers/body. */
+ 413: {
+ headers: {
+ "Cache-Control"?: "no-store";
+ [name: string]: unknown;
+ };
+ content: {
+ "application/problem+json": components["schemas"]["ProblemDetails"];
+ };
+ };
+ /** @description rate_limited from early visitor/peer admission, validated-subject quota or subject-registry capacity. */
429: {
headers: {
"Cache-Control": "no-store";
+ /** @description Suggested retry delay in seconds for the first refusing admission boundary. A capacity hint does not promise an available registry slot; quota hints do not guarantee admission. */
+ "Retry-After": number;
[name: string]: unknown;
};
content: {
@@ -766,10 +1070,12 @@ export interface operations {
"application/problem+json": components["schemas"]["ProblemDetails"];
};
};
- /** @description dependency_unavailable from invalid stored public configuration. */
+ /** @description dependency_unavailable from invalid stored public configuration or a sticky subject-registry cleanup fault. */
503: {
headers: {
"Cache-Control": "no-store";
+ /** @description Suggested dependency retry delay in seconds; not a recovery guarantee. */
+ "Retry-After": number;
[name: string]: unknown;
};
content: {
@@ -795,7 +1101,7 @@ export interface operations {
};
content?: never;
};
- /** @description validation_failed; the site bootstrap accepts no query parameters. */
+ /** @description validation_failed; the site bootstrap accepts no query parameters, and malformed or repeated assertion headers are refused. */
400: {
headers: {
"Cache-Control": "no-store";
@@ -803,7 +1109,16 @@ export interface operations {
};
content?: never;
};
- /** @description not_found; unavailable host or hidden/untranslated/ineligible resource, without disclosure. */
+ /** @description unauthorized; present invalid or undecidable credentials on a known host. Bare Bearer challenge; no credential or provider details. */
+ 401: {
+ headers: {
+ "Cache-Control": "no-store";
+ "WWW-Authenticate": "Bearer";
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description not_found for unavailable host or hidden/untranslated/ineligible resource; tenant_mismatch for authenticated host/claim or header disagreement, without disclosure. */
404: {
headers: {
"Cache-Control": "no-store";
@@ -814,15 +1129,26 @@ export interface operations {
/** @description method_not_allowed from routing. */
405: {
headers: {
+ Allow: "GET, HEAD";
"Cache-Control": "no-store";
[name: string]: unknown;
};
content?: never;
};
- /** @description rate_limited from the anonymous limiter. */
+ /** @description payload_too_large; the decoded body bound precedes host classification and authentication. Kestrel framing/backstop refusals may occur outside the application response policy and omit these headers/body. */
+ 413: {
+ headers: {
+ "Cache-Control"?: "no-store";
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description rate_limited from early visitor/peer admission, validated-subject quota or subject-registry capacity. */
429: {
headers: {
"Cache-Control": "no-store";
+ /** @description Suggested retry delay in seconds for the first refusing admission boundary. A capacity hint does not promise an available registry slot; quota hints do not guarantee admission. */
+ "Retry-After": number;
[name: string]: unknown;
};
content?: never;
@@ -835,10 +1161,12 @@ export interface operations {
};
content?: never;
};
- /** @description dependency_unavailable from invalid stored public configuration. */
+ /** @description dependency_unavailable from invalid stored public configuration or a sticky subject-registry cleanup fault. */
503: {
headers: {
"Cache-Control": "no-store";
+ /** @description Suggested dependency retry delay in seconds; not a recovery guarantee. */
+ "Retry-After": number;
[name: string]: unknown;
};
content?: never;
diff --git a/frontend/packages/sdk/src/index.ts b/frontend/packages/sdk/src/index.ts
index 6689c9f6..55a2d06a 100644
--- a/frontend/packages/sdk/src/index.ts
+++ b/frontend/packages/sdk/src/index.ts
@@ -1,2 +1,8 @@
/** Generated API types. Server transport is available only through @learnstack/sdk/server. */
export type { paths, components, operations } from './generated/schema';
+export { AUTHENTICATED_GET_CONTRACTS } from './authenticated-contract';
+export type {
+ AccountSite,
+ AccountSiteSuccess,
+ AuthenticatedGetOperation,
+} from './authenticated-contract';
diff --git a/frontend/packages/sdk/src/server-refusal.test.ts b/frontend/packages/sdk/src/server-refusal.test.ts
new file mode 100644
index 00000000..daa5f2e0
--- /dev/null
+++ b/frontend/packages/sdk/src/server-refusal.test.ts
@@ -0,0 +1,45 @@
+import { expect, it, vi } from 'vitest';
+
+import * as root from './index';
+import { createServerSdk, PublicRequestRefusalError } from './server';
+import type { PublicFetch } from './server';
+
+it('keeps a typed local transport refusal distinct from network failure without retrying', async () => {
+ const error = new PublicRequestRefusalError();
+ expect(error.message).toBe('Public request refused');
+ expect(root).not.toHaveProperty('PublicRequestRefusalError');
+ const transport = vi.fn().mockRejectedValue(error);
+ await expect(createServerSdk(transport).getSite()).resolves.toEqual({ kind: 'invalid-request' });
+ expect(transport).toHaveBeenCalledOnce();
+});
+
+it.each([
+ new Error('Public request refused'),
+ Object.assign(new Error('Public request refused'), { name: 'PublicRequestRefusalError' }),
+ { name: 'PublicRequestRefusalError', message: 'Public request refused' },
+ new TypeError('Network failed'),
+])('does not classify provider text or lookalike errors as local refusal (%#)', async (error) => {
+ const transport = vi.fn().mockRejectedValue(error);
+ await expect(createServerSdk(transport).getSite()).resolves.toEqual({ kind: 'transport-error' });
+ expect(transport).toHaveBeenCalledOnce();
+});
+
+it('retains cancellation precedence when transport refuses the same operation', async () => {
+ const controller = new AbortController();
+ const transport = vi.fn().mockImplementation(async () => {
+ controller.abort();
+ throw new PublicRequestRefusalError();
+ });
+ await expect(createServerSdk(transport).getSite({ signal: controller.signal })).resolves.toEqual({
+ kind: 'cancelled',
+ });
+ expect(transport).toHaveBeenCalledOnce();
+});
+
+it('does not treat a response-body failure as local operation construction refusal', async () => {
+ const response = new Response(null);
+ vi.spyOn(response, 'json').mockRejectedValue(new PublicRequestRefusalError());
+ await expect(createServerSdk(async () => response).getSite()).resolves.toEqual({
+ kind: 'transport-error',
+ });
+});
diff --git a/frontend/packages/sdk/src/server.test.ts b/frontend/packages/sdk/src/server.test.ts
index 9acd7da3..6e01bff0 100644
--- a/frontend/packages/sdk/src/server.test.ts
+++ b/frontend/packages/sdk/src/server.test.ts
@@ -1,8 +1,14 @@
import { describe, expect, expectTypeOf, it, vi } from 'vitest';
-import type { operations, paths } from './generated/schema';
+import type { operations } from './generated/schema';
import { createServerSdk, PUBLIC_GET_WRAPPERS } from './server';
-import type { ApiResult, PublicFetch, RequestOptions, ServerSdk } from './server';
+import type {
+ ApiResult,
+ PublicFetch,
+ RequestOptions,
+ ServerSdk,
+ PUBLIC_GET_CONTRACTS,
+} from './server';
const pageInfo = { nextCursor: null, previousCursor: null, hasNext: false, hasPrevious: false };
const course = {
@@ -68,10 +74,12 @@ describe('public GET transport', () => {
'/api/v1/public/courses/{slug}': 'getCourse',
'/api/v1/public/courses/{slug}/lessons/{lessonSlug}': 'getLesson',
});
- type GetPaths = {
- [P in keyof paths]: paths[P]['get'] extends undefined ? never : P;
- }[keyof paths];
- expectTypeOf().toEqualTypeOf();
+ expectTypeOf().toEqualTypeOf<
+ keyof typeof PUBLIC_GET_CONTRACTS
+ >();
+ expectTypeOf()
+ .extract<'/api/v1/account/site'>()
+ .toEqualTypeOf();
expectTypeOf[0]>().toEqualTypeOf<
operations['GetPublicCourses']['parameters']['query']
>();
diff --git a/frontend/packages/sdk/src/server.ts b/frontend/packages/sdk/src/server.ts
index f8c60330..fd95356e 100644
--- a/frontend/packages/sdk/src/server.ts
+++ b/frontend/packages/sdk/src/server.ts
@@ -1,3 +1,4 @@
+import type { AUTHENTICATED_GET_CONTRACTS, GetContractOwnership } from './authenticated-contract';
import type { operations, paths } from './generated/schema';
import { appError, isSuccess, parseProblem } from './response-policy';
import type { ApiProblem, AppError, PublicGetOperation, PublicSuccess } from './response-policy';
@@ -7,6 +8,15 @@ export type { ApiProblem, AppError } from './response-policy';
/** The supplied Fetch-compatible transport resolves relative URLs. P02d-5 owns the trusted server hop. */
export type PublicFetch = (url: string, init: RequestInit) => Promise;
export type RequestOptions = { readonly signal?: AbortSignal };
+
+/** An injected transport refused local operation construction before network I/O. */
+export class PublicRequestRefusalError extends Error {
+ constructor() {
+ super('Public request refused');
+ this.name = 'PublicRequestRefusalError';
+ }
+}
+
export type ApiResult =
| { readonly kind: 'success'; readonly status: 200; readonly data: T }
| {
@@ -24,6 +34,7 @@ export type ApiResult =
| 'unexpected-status';
}
| { readonly kind: 'transport-error' }
+ /** Local SDK/transport construction or validation refusal; not an HTTP 400. */
| { readonly kind: 'invalid-request' }
| { readonly kind: 'cancelled' };
@@ -47,16 +58,37 @@ export type ServerSdk = {
): Promise>>;
};
-/** A non-empty census that stops compiling when generation introduces an unwrapped GET path. */
-export const PUBLIC_GET_WRAPPERS = {
+/** The exact public subset; generated GETs never acquire public ownership implicitly. */
+export const PUBLIC_GET_CONTRACTS = Object.freeze({
+ '/api/v1/public/site': 'GetPublicSite',
+ '/api/v1/public/courses': 'GetPublicCourses',
+ '/api/v1/public/courses/{slug}': 'GetPublicCourse',
+ '/api/v1/public/courses/{slug}/lessons/{lessonSlug}': 'GetPublicLesson',
+} as const satisfies Record);
+
+export type PublicGetPath = keyof typeof PUBLIC_GET_CONTRACTS;
+
+/** Both path and operation ownership must remain disjoint, correctly paired and exhaustive. */
+export const GET_CONTRACT_OWNERSHIP = true satisfies GetContractOwnership<
+ paths,
+ operations,
+ typeof PUBLIC_GET_CONTRACTS,
+ typeof AUTHENTICATED_GET_CONTRACTS
+>;
+
+/** Every declared public GET has a wrapper; authenticated GETs have their own contract. */
+type PublicWrappers = {
+ [P in PublicGetPath]: (typeof PUBLIC_GET_CONTRACTS)[P] extends `GetPublic${infer Resource}`
+ ? Uncapitalize<`Get${Resource}`> & keyof ServerSdk
+ : never;
+};
+
+export const PUBLIC_GET_WRAPPERS = Object.freeze({
'/api/v1/public/site': 'getSite',
'/api/v1/public/courses': 'getCourses',
'/api/v1/public/courses/{slug}': 'getCourse',
'/api/v1/public/courses/{slug}/lessons/{lessonSlug}': 'getLesson',
-} as const satisfies Record<
- { [P in keyof paths]: paths[P]['get'] extends undefined ? never : P }[keyof paths],
- keyof ServerSdk
->;
+} as const satisfies PublicWrappers);
function queryString(query: Record): string {
const parameters = new URLSearchParams();
@@ -93,8 +125,11 @@ export function createServerSdk(transport: PublicFetch): ServerSdk {
headers: { Accept: 'application/json' },
signal: options?.signal,
});
- } catch {
- return options?.signal?.aborted ? { kind: 'cancelled' } : { kind: 'transport-error' };
+ } catch (error) {
+ if (options?.signal?.aborted) return { kind: 'cancelled' };
+ return error instanceof PublicRequestRefusalError
+ ? { kind: 'invalid-request' }
+ : { kind: 'transport-error' };
}
if (options?.signal?.aborted) return { kind: 'cancelled' };
if (response.status < 400 && response.status !== 200)
diff --git a/scripts/ci-test-shards-plan.json b/scripts/ci-test-shards-plan.json
index 1551db5b..ad7b5881 100644
--- a/scripts/ci-test-shards-plan.json
+++ b/scripts/ci-test-shards-plan.json
@@ -49,38 +49,73 @@
"sourceSha": "3553ff16e509a1c2978edc13c1f68e592e30b247",
"runId": "39148006960",
"note": "Completed-only, rounded Architecture console prefix; maps only exact unambiguous TRX test names to actual TestMethod metadata. Advisory, censored remainder unknown."
+ },
+ "hosted-architecture-39417319998-2-0": {
+ "kind": "hosted_trx",
+ "artifactSha256": "59d80797f359ad0aec494654e77cac2e6187ad1e744ab3e8abde3695345cfe90",
+ "sourceSha": "d84ac3009f8ba84a339d91e023f7f6f5f9269cc0",
+ "runId": "39417319998",
+ "note": "Run attempt 2; owner architecture-0; PR head 00a7877e6b578c471fc52ae9b2c1e5d092e3f2f9; merge checkout d84ac3009f8ba84a339d91e023f7f6f5f9269cc0; artifact ID 12262603636. Exact completed hosted TRX case durations summed per actual assembly/selector in milliseconds (ceil, minimum 1); advisory only, not wall bounds."
+ },
+ "hosted-architecture-39417319998-2-1": {
+ "kind": "hosted_trx",
+ "artifactSha256": "c183ef8d7fc84a8e55c417b2c2e370c32a7688f7bbae49eb0d69fee16439f3d4",
+ "sourceSha": "d84ac3009f8ba84a339d91e023f7f6f5f9269cc0",
+ "runId": "39417319998",
+ "note": "Run attempt 2; owner architecture-1; PR head 00a7877e6b578c471fc52ae9b2c1e5d092e3f2f9; merge checkout d84ac3009f8ba84a339d91e023f7f6f5f9269cc0; artifact ID 12263044967. Exact completed hosted TRX case durations summed per actual assembly/selector in milliseconds (ceil, minimum 1); advisory only, not wall bounds."
+ },
+ "hosted-architecture-39417319998-2-2": {
+ "kind": "hosted_trx",
+ "artifactSha256": "8a0fda0db91affc86b8aa91568015a638f7d8bf981dc337cbca57dc9d761c002",
+ "sourceSha": "d84ac3009f8ba84a339d91e023f7f6f5f9269cc0",
+ "runId": "39417319998",
+ "note": "Run attempt 2; owner architecture-2; PR head 00a7877e6b578c471fc52ae9b2c1e5d092e3f2f9; merge checkout d84ac3009f8ba84a339d91e023f7f6f5f9269cc0; artifact ID 12264330218. Exact completed hosted TRX case durations summed per actual assembly/selector in milliseconds (ceil, minimum 1); advisory only, not wall bounds."
+ },
+ "hosted-architecture-39417319998-2-4": {
+ "kind": "hosted_trx",
+ "artifactSha256": "3ab8c70efadd50bdcbd40b92beb61a41f393400422293bafc4828d8001a2c9e0",
+ "sourceSha": "d84ac3009f8ba84a339d91e023f7f6f5f9269cc0",
+ "runId": "39417319998",
+ "note": "Run attempt 2; owner architecture-4; PR head 00a7877e6b578c471fc52ae9b2c1e5d092e3f2f9; merge checkout d84ac3009f8ba84a339d91e023f7f6f5f9269cc0; artifact ID 12262614196. Exact completed hosted TRX case durations summed per actual assembly/selector in milliseconds (ceil, minimum 1); advisory only, not wall bounds."
+ },
+ "hosted-architecture-39417319998-2-5": {
+ "kind": "hosted_trx",
+ "artifactSha256": "af2b09dfb114850f854e81ddcf12df9e9b995f00fb2f56051f71940bd7fa00ec",
+ "sourceSha": "d84ac3009f8ba84a339d91e023f7f6f5f9269cc0",
+ "runId": "39417319998",
+ "note": "Run attempt 2; owner architecture-5; PR head 00a7877e6b578c471fc52ae9b2c1e5d092e3f2f9; merge checkout d84ac3009f8ba84a339d91e023f7f6f5f9269cc0; artifact ID 12262804978. Exact completed hosted TRX case durations summed per actual assembly/selector in milliseconds (ceil, minimum 1); advisory only, not wall bounds."
}
},
"weights": [
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AggregateWriteTests.Cross_Aggregate_Census_Catches_Fused_Separate_Notification_And_Internal_Constructors",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 42,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AggregateWriteTests.Cross_Aggregate_Census_Catches_Integration_Handlers_And_Inherited_Contracts",
- "milliseconds": 15,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 78,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AggregateWriteTests.Cross_Aggregate_Writes_Are_Confined_To_Tenant_Provisioning",
- "milliseconds": 25,
- "source": "local-architecture"
+ "milliseconds": 122,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AggregateWriteTests.Every_Write_Port_Is_Countable_Or_Enumerated",
- "milliseconds": 43,
- "source": "local-architecture"
+ "milliseconds": 57,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AggregateWriteTests.Write_Port_Census_Distinguishes_Typed_Read_Keys_From_Wrapped_Domain_Writes",
- "milliseconds": 2,
- "source": "local-architecture"
+ "milliseconds": 4,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
@@ -91,158 +126,158 @@
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.ApiConventionTests.Forwarded_Headers_Are_Not_Wired",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 3,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.ApiConventionTests.Live_Majors_Are_At_Most_Two_Adjacent",
- "milliseconds": 7,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 10,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.ApiConventionTests.Unversioned_Route_Prefixes_Are_Declared_Once",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 16,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditConventionTests.AuditEntry_Inherits_Entity_Not_AuditableEntity",
"milliseconds": 1,
- "source": "local-architecture"
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditConventionTests.AuditEntry_Is_AppendOnly",
- "milliseconds": 196,
- "source": "hosted-architecture-current"
+ "milliseconds": 217,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditConventionTests.Audit_Closed_Set_Columns_Store_What_Their_Check_Admits",
- "milliseconds": 257,
- "source": "local-architecture"
+ "milliseconds": 121,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditConventionTests.Every_Module_Has_An_AuditCoverage_Matrix",
- "milliseconds": 4,
- "source": "local-architecture"
+ "milliseconds": 81,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditConventionTests.IAuditStore_Exposes_No_Update_Method",
"milliseconds": 1,
- "source": "local-architecture"
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditConventionTests.Modules_Do_Not_Write_AuditLog_Directly",
- "milliseconds": 940,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 679,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditConventionTests.No_Set_Based_Write_Bypasses_The_Audit_Capture",
- "milliseconds": 589,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 356,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditConventionTests.OperationType_Enum_Matches_Catalog",
- "milliseconds": 3,
- "source": "local-architecture"
+ "milliseconds": 7,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditConventionTests.The_AppendOnly_Sweep_Can_Actually_Fail",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 2,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditConventionTests.The_AuditLog_Write_Scan_Can_Actually_Fail",
- "milliseconds": 4,
- "source": "local-architecture"
+ "milliseconds": 28,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditConventionTests.The_Matrix_Sweep_Can_Actually_Fail",
"milliseconds": 1,
- "source": "local-architecture"
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditConventionTests.The_Set_Based_Write_Sweep_Can_Actually_Fail",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 5,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditCoverageTests.A_slug_classified_twice_fails_whichever_row_comes_first",
- "milliseconds": 5,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 13,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditCoverageTests.An_off_path_marker_has_to_match_how_the_operation_is_registered",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 36,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditCoverageTests.Every_Matrix_Row_Whose_Command_Exists_Is_Registered",
- "milliseconds": 70,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 78,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditCoverageTests.Every_Module_With_An_Aggregate_Or_A_Request_Has_A_Matrix",
- "milliseconds": 35,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 18,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditCoverageTests.Every_Shipped_Request_Is_Registered",
- "milliseconds": 46,
- "source": "local-architecture"
+ "milliseconds": 28,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditCoverageTests.Every_TenantOwned_Command_HasAuditCoverage",
- "milliseconds": 112,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 37,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditCoverageTests.Integration_Audit_Census_Accepts_Explicit_Off_For_Both_Handlers",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 10,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditCoverageTests.Integration_Audit_Census_Catches_An_Unregistered_Handler_For_A_Shared_Event",
- "milliseconds": 6,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 37,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditCoverageTests.Integration_Audit_Census_Rejects_Event_Only_Classification",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 8,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditCoverageTests.Integration_Only_Module_Requires_An_Audit_Matrix",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 4,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditCoverageTests.The_Forward_Sweep_Reads_Only_The_Registering_Module_s_Matrix",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 8,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
@@ -253,170 +288,224 @@
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditCoverageTests.The_Matrix_Sweep_Reads_Every_Slug_And_Can_Actually_Fail",
- "milliseconds": 50,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 49,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditCoverageTests.The_Module_Sweep_Can_Actually_Fail",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 2,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.AuditCoverageTests.The_Request_Sweep_Can_Actually_Fail",
- "milliseconds": 13,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 2,
+ "source": "hosted-architecture-39417319998-2-2"
+ },
+ {
+ "assembly": "learnstack.tests.architecture",
+ "method": "LearnStack.Tests.Architecture.AuthenticatedHttpSurfaceTests.Authenticated_Http_Surface_Is_Enumerated_And_ReadOnly",
+ "milliseconds": 1414,
+ "source": "hosted-architecture-39417319998-2-4"
+ },
+ {
+ "assembly": "learnstack.tests.architecture",
+ "method": "LearnStack.Tests.Architecture.AuthenticatedHttpSurfaceTests.Authenticated_http_census_detects_extra_routes_mutations_and_missing_subjects",
+ "milliseconds": 24010,
+ "source": "hosted-architecture-39417319998-2-1"
+ },
+ {
+ "assembly": "learnstack.tests.architecture",
+ "method": "LearnStack.Tests.Architecture.AuthenticatedHttpSurfaceTests.Authenticated_http_census_follows_production_helpers_and_nested_dispatch",
+ "milliseconds": 42406,
+ "source": "hosted-architecture-39417319998-2-4"
+ },
+ {
+ "assembly": "learnstack.tests.architecture",
+ "method": "LearnStack.Tests.Architecture.AuthenticationAuthorityTests.Authority_census_closes_shape_and_approved_owner_exports",
+ "milliseconds": 15693,
+ "source": "hosted-architecture-39417319998-2-0"
+ },
+ {
+ "assembly": "learnstack.tests.architecture",
+ "method": "LearnStack.Tests.Architecture.AuthenticationAuthorityTests.Authority_census_detects_missing_subjects_mutability_and_extra_trust_writes",
+ "milliseconds": 12726,
+ "source": "hosted-architecture-39417319998-2-4"
+ },
+ {
+ "assembly": "learnstack.tests.architecture",
+ "method": "LearnStack.Tests.Architecture.AuthenticationAuthorityTests.Authority_census_rejects_actual_identity_forwarding_through_opaque_storage",
+ "milliseconds": 4453,
+ "source": "hosted-architecture-39417319998-2-0"
+ },
+ {
+ "assembly": "learnstack.tests.architecture",
+ "method": "LearnStack.Tests.Architecture.AuthenticationAuthorityTests.Authority_census_rejects_actual_identity_forwarding_to_opaque_helpers",
+ "milliseconds": 5288,
+ "source": "hosted-architecture-39417319998-2-5"
+ },
+ {
+ "assembly": "learnstack.tests.architecture",
+ "method": "LearnStack.Tests.Architecture.AuthenticationAuthorityTests.Authority_census_rejects_compiled_claim_role_and_projection_bypasses",
+ "milliseconds": 48741,
+ "source": "hosted-architecture-39417319998-2-2"
+ },
+ {
+ "assembly": "learnstack.tests.architecture",
+ "method": "LearnStack.Tests.Architecture.AuthenticationAuthorityTests.Keycloak_Roles_Are_Not_Application_Authority",
+ "milliseconds": 2946,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CorpusConsistencyTests.Every_Implemented_Rule_Names_A_Test_That_Exists",
- "milliseconds": 369,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 1138,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CorpusConsistencyTests.No_Architecture_Test_Is_Skippable",
- "milliseconds": 2,
- "source": "local-architecture"
+ "milliseconds": 6,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CorpusConsistencyTests.Standard_Status_Headers_Match_The_Index",
- "milliseconds": 16,
- "source": "local-architecture"
+ "milliseconds": 15,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CorpusConsistencyTests.The_Catalogue_Counts_Its_Own_Rules",
- "milliseconds": 91,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 124,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CorpusConsistencyTests.The_Corpus_Guards_Can_Actually_Fail",
- "milliseconds": 13,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 20,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CrossCuttingFoundationTests.Adapters_Wrap_Provider_Exceptions",
- "milliseconds": 1000,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 1634,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CrossCuttingFoundationTests.Domain_Methods_Do_Not_Throw_For_Expected_Cases",
- "milliseconds": 14000,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 14278,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CrossCuttingFoundationTests.Handlers_Return_Result",
- "milliseconds": 28,
- "source": "local-architecture"
+ "milliseconds": 44,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CrossCuttingFoundationTests.IErrorTrackingProvider_Is_Singleton",
- "milliseconds": 194,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 152,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CrossCuttingFoundationTests.IExceptionHandler_Registered_AtStartup",
- "milliseconds": 51,
- "source": "local-architecture"
+ "milliseconds": 14,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CrossCuttingFoundationTests.Integration_Event_TopicNames_FollowConvention",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 13,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CrossCuttingFoundationTests.JsonSchema_Net_Types_NotImportedOutsideInfrastructure",
- "milliseconds": 327,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 659,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CrossCuttingFoundationTests.LearnStack_Modules_DoNotReference_Hub",
- "milliseconds": 226,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 252,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CrossCuttingFoundationTests.Logging_Goes_Through_Microsoft_Extensions_Logging",
- "milliseconds": 168,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 355,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CrossCuttingFoundationTests.MediatR_Pipeline_Order_Matches_Canonical_Sequence",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 3,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CrossCuttingFoundationTests.Modules_Do_Not_Inject_IEventBus_Directly",
- "milliseconds": 556,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 714,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CrossCuttingFoundationTests.Modules_Do_Not_Inject_Valkey_Directly",
- "milliseconds": 151,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 213,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CrossCuttingFoundationTests.Modules_Do_Not_Reference_DeploymentMode",
- "milliseconds": 208,
- "source": "hosted-architecture-current"
+ "milliseconds": 310,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CrossCuttingFoundationTests.Modules_Do_Not_Reference_Sentry_SDK_Directly",
- "milliseconds": 158,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 202,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CrossCuttingFoundationTests.OTel_Pipeline_Includes_TenantContextSpanProcessor",
- "milliseconds": 76,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 717,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CrossCuttingFoundationTests.Registering_The_Pipeline_Twice_Registers_It_Once",
- "milliseconds": 3,
- "source": "local-architecture"
+ "milliseconds": 21,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CrossCuttingFoundationTests.The_Direct_Client_Bans_Can_Actually_Fail",
- "milliseconds": 8,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 106,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CrossCuttingFoundationTests.The_Domain_Exception_Report_Can_Actually_Fail",
- "milliseconds": 824,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 4317,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CustomizationProjectionTests.Customization_Projection_Does_Not_Validate_On_Read",
- "milliseconds": 90,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 241,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CustomizationProjectionTests.Projection_validator_guard_detects_direct_and_helper_dependencies",
- "milliseconds": 11,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 49,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
@@ -427,14 +516,14 @@
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CustomizationRegistryTests.Composite_Renderer_Keys_Match_The_Frontend_Registry",
- "milliseconds": 5,
- "source": "hosted-architecture-current"
+ "milliseconds": 4,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.CustomizationRegistryTests.Generic_Primitives_Only_In_Renderer",
- "milliseconds": 10,
- "source": "hosted-architecture-current"
+ "milliseconds": 8,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
@@ -445,110 +534,110 @@
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.DomainEventHandlerTests.Domain_handler_census_recognizes_inherited_generic_contracts_and_excludes_other_notifications",
- "milliseconds": 22,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 16,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.DomainEventHandlerTests.Domain_handler_guard_accepts_translation_and_pure_helpers_without_traversing_outbox_implementations",
- "milliseconds": 45,
- "source": "hosted-architecture-current"
+ "milliseconds": 16,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.DomainEventHandlerTests.Domain_handler_guard_follows_helpers_interfaces_inheritance_async_and_lambdas",
- "milliseconds": 436,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 493,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.DomainEventHandlerTests.Domain_handler_guard_reaches_planted_roots_and_helpers_in_every_production_assembly",
- "milliseconds": 1000,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 736,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.DomainEventHandlerTests.Domain_handler_guard_rejects_each_forbidden_dependency",
- "milliseconds": 497,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 899,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.DomainEventHandlerTests.Domain_handler_guard_rejects_further_raise_and_direct_notification_dispatch",
- "milliseconds": 43,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 50,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.DomainGenericityTests.Core_Modules_HaveNo_DomainSpecific_Names",
- "milliseconds": 1667,
- "source": "local-architecture"
+ "milliseconds": 1181,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.DomainGenericityTests.Demo_Identity_Projection_Is_Complete_And_The_Frontend_Snapshot_Matches",
- "milliseconds": 13,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 62,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.DomainGenericityTests.Demo_Literal_Fence_Detects_Decoded_Identities_And_Admits_Platform_Data",
- "milliseconds": 214,
- "source": "local-architecture"
+ "milliseconds": 98,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.DomainGenericityTests.Production_Code_Does_Not_Branch_On_Demo_Tenant_Literals",
- "milliseconds": 24000,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 28364,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.DomainGenericityTests.Production_Projects_Do_Not_Reference_The_Seeder",
- "milliseconds": 54,
- "source": "local-architecture"
+ "milliseconds": 31,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.DomainGenericityTests.Seeder_Reference_Fence_Detects_Compiled_And_Restored_Edges",
- "milliseconds": 2,
- "source": "local-architecture"
+ "milliseconds": 4,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.DomainGenericityTests.The_Domain_Term_Scan_Can_Actually_Fail",
- "milliseconds": 126,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 85,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.DomainModelTests.Aggregate_Roots_Use_StronglyTypedId",
- "milliseconds": 22,
- "source": "local-architecture"
+ "milliseconds": 26,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.DomainModelTests.Aggregates_Do_Not_Redeclare_Entity_Equality",
- "milliseconds": 21,
- "source": "local-architecture"
+ "milliseconds": 20,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.DomainModelTests.The_Aggregate_Shape_Rules_Can_Actually_Fail",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 4,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.EntitlementConventionTests.Modules_Do_Not_Read_Entitlement_Cache_Directly",
- "milliseconds": 922,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 1015,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.EntitlementConventionTests.The_Entitlement_Cache_Scan_Can_Actually_Fail",
- "milliseconds": 13,
- "source": "hosted-architecture-current"
+ "milliseconds": 16,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
@@ -559,86 +648,86 @@
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.EntitlementKeyTests.PlanProjected_Keys_NotInTenantFlags",
- "milliseconds": 466,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 1224,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.EntitlementKeyTests.The_Entitlement_Key_Scans_Can_Actually_Fail",
- "milliseconds": 287,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 255,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.IntegrationEventContractTests.Integration_Event_Declares_PartitionKey",
- "milliseconds": 176,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 332,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.IntegrationEventContractTests.Integration_Event_Payloads_Contain_Only_Identifiers",
- "milliseconds": 60,
- "source": "local-architecture"
+ "milliseconds": 156,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.IntegrationEventContractTests.Integration_Events_Inherit_From_IntegrationEventBase",
- "milliseconds": 39,
- "source": "local-architecture"
+ "milliseconds": 106,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.ModuleDependencyTests.CoreApplication_DoesNotDependOn_Any_Infrastructure_Or_Module",
- "milliseconds": 4,
- "source": "local-architecture"
+ "milliseconds": 1,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.ModuleDependencyTests.CoreInfrastructure_DoesNotDependOn_AnyModule",
- "milliseconds": 31,
- "source": "local-architecture"
+ "milliseconds": 56,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.ModuleDependencyTests.Domain_Does_Not_Depend_On_Microsoft_EntityFrameworkCore_Except_Vogen_Emitted_Converters",
- "milliseconds": 195,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 108,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.ModuleDependencyTests.Meta_NetArchTest_DetectsAPlantedViolation",
- "milliseconds": 248,
- "source": "local-architecture"
+ "milliseconds": 421,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.ModuleDependencyTests.ModuleApplication_References_Only_Its_Own_Layers_And_Other_Contracts",
- "milliseconds": 12,
- "source": "local-architecture"
+ "milliseconds": 9,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.ModuleDependencyTests.ModuleContracts_DoNotDependOn_AnyModuleDomain",
- "milliseconds": 172,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 175,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.ModuleDependencyTests.ModuleContracts_Reference_Only_SharedKernel",
- "milliseconds": 9,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 10,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.ModuleDependencyTests.ModuleDomain_DoesNotDependOn_AnyApplicationOrInfrastructure",
- "milliseconds": 7,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 3,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.ModuleDependencyTests.ModuleDomain_DoesNotDependOn_OtherModuleDomain",
- "milliseconds": 100,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 56,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
@@ -655,8 +744,8 @@
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.ModuleDependencyTests.The_Domain_EF_Core_Rule_Can_Actually_Fail",
- "milliseconds": 656,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 597,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
@@ -679,32 +768,32 @@
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests+JobExecutionMutationTests.Background_job_owner_census_detects_actual_removals_extra_subtypes_async_calls_and_delegates",
- "milliseconds": 291000,
- "source": "hosted-architecture-current"
+ "milliseconds": 391105,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests+JobExecutionMutationTests.External_job_orchestration_rejects_missing_seams_helpers_delegates_signatures_and_resource_carriers",
- "milliseconds": 295000,
- "source": "hosted-architecture-current"
+ "milliseconds": 327614,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests+JobLifecycleMutationTests.Background_job_capacity_census_rejects_forged_cleanup_recovery_and_checkpoint_owners",
- "milliseconds": 495000,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 375316,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests+JobLifecycleMutationTests.Background_job_recipient_census_detects_extra_authority_and_missing_real_fences",
- "milliseconds": 306000,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 160981,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests+JobLifecycleMutationTests.Hangfire_runtime_census_rejects_fatal_and_constructor_escapes",
- "milliseconds": 203000,
- "source": "hosted-architecture-current"
+ "milliseconds": 236768,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
@@ -721,20 +810,20 @@
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.BackgroundJob_Definitions_Stay_In_Their_Owning_Module",
- "milliseconds": 8172,
- "source": "local-architecture"
+ "milliseconds": 12851,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.BackgroundJob_External_Work_Has_No_Business_Transaction",
- "milliseconds": 40000,
- "source": "hosted-architecture-first"
+ "milliseconds": 100965,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.BackgroundJob_Fanout_Uses_Recipient_Bound_Tenant_Admission",
- "milliseconds": 482,
- "source": "hosted-architecture-first"
+ "milliseconds": 630,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
@@ -745,68 +834,68 @@
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.BackgroundJob_Storage_And_Credentials_Are_Confined",
- "milliseconds": 27051,
- "source": "local-architecture"
+ "milliseconds": 47779,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.BackgroundJob_Terminal_Audit_Is_Row_Bound",
- "milliseconds": 545,
- "source": "hosted-architecture-first"
+ "milliseconds": 340,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.BackgroundJob_Worker_Pools_Reserve_Independent_Capacity",
- "milliseconds": 390000,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 195171,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Background_job_metric_callbacks_follow_the_actual_transitive_memory_graph_and_refuse_persistence_helpers",
- "milliseconds": 27507,
- "source": "local-architecture"
+ "milliseconds": 70110,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Background_job_metric_census_rejects_bait_callbacks_hidden_registration_and_skipped_binding",
- "milliseconds": 20450,
- "source": "local-architecture"
+ "milliseconds": 30878,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Background_job_metric_registration_requires_the_actual_singleton_descriptor_and_rejects_substitutions",
- "milliseconds": 2,
- "source": "local-architecture"
+ "milliseconds": 16,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Background_job_orchestration_has_closed_finite_owners_and_rejects_extra_callers_and_lost_retention",
- "milliseconds": 56000,
- "source": "hosted-architecture-first"
+ "milliseconds": 47325,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Background_job_recipient_field_and_record_controls_detect_the_exact_owner_and_opcode",
- "milliseconds": 5000,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 6509,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Background_job_recipient_history_controls_reject_detached_cleanup_and_unfenced_status",
- "milliseconds": 2784,
- "source": "hosted-architecture-first"
+ "milliseconds": 3776,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Background_job_recipient_shared_insert_controls_preserve_successful_production",
- "milliseconds": 12216,
- "source": "hosted-architecture-first"
+ "milliseconds": 11345,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Background_job_recurrence_runtime_controls_reject_changed_authority_and_cleanup",
- "milliseconds": 215000,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 208191,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
@@ -817,188 +906,188 @@
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Boundary_censuses_preserve_unkeyed_services_unrelated_publishers_and_both_real_keys",
- "milliseconds": 1043,
- "source": "local-architecture"
+ "milliseconds": 2186,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Bridge_credential_census_rejects_changed_signatures_captures_and_missing_subjects",
- "milliseconds": 204000,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 157058,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Bridge_credential_census_rejects_extra_acquisition_forwarding_and_store_consumers",
- "milliseconds": 99000,
- "source": "hosted-architecture-first"
+ "milliseconds": 127690,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Cleanup_owner_census_rejects_extra_acquisition_disposal_capture_and_observation_paths",
- "milliseconds": 115000,
- "source": "hosted-architecture-first"
+ "milliseconds": 97302,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Credential_census_rejects_compiled_connection_string_indexers_and_composed_keys",
- "milliseconds": 19481,
- "source": "local-architecture"
+ "milliseconds": 38008,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Credential_census_rejects_crossed_keys_changed_closures_and_missing_subjects",
- "milliseconds": 74000,
- "source": "hosted-architecture-first"
+ "milliseconds": 119269,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Credential_registration_detector_rejects_missing_duplicate_wrong_key_and_non_singleton_sources",
- "milliseconds": 2,
- "source": "local-architecture"
+ "milliseconds": 34,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Delivery_Execution_And_Observation_Are_Bounded",
- "milliseconds": 539,
- "source": "hosted-architecture-first"
+ "milliseconds": 572,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Delivery_SQL_census_rejects_quoted_CTE_helper_overload_missing_and_payload_paths",
- "milliseconds": 3657,
- "source": "local-architecture"
+ "milliseconds": 5431,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Delivery_SQL_writers_and_dynamic_helpers_are_closed",
- "milliseconds": 389,
- "source": "local-architecture"
+ "milliseconds": 469,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Delivery_Terminal_Audit_Identity_Is_Row_Bound",
- "milliseconds": 350,
- "source": "hosted-architecture-first"
+ "milliseconds": 485,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Delivery_credentials_reject_extra_store_locators_forwarding_overloads_and_wrong_keys",
- "milliseconds": 56285,
- "source": "local-architecture"
+ "milliseconds": 81202,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Delivery_execution_census_rejects_uncontrolled_direct_delegate_async_and_missing_paths",
- "milliseconds": 4000,
- "source": "hosted-architecture-first"
+ "milliseconds": 5388,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Delivery_observation_walk_rejects_concrete_interface_inherited_async_and_callback_persistence",
- "milliseconds": 5300,
- "source": "local-architecture"
+ "milliseconds": 7650,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Delivery_owner_and_inbox_capabilities_are_closed",
- "milliseconds": 355,
- "source": "local-architecture"
+ "milliseconds": 512,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Delivery_owner_census_rejects_extra_direct_delegate_async_and_inherited_capabilities",
- "milliseconds": 10000,
- "source": "hosted-architecture-first"
+ "milliseconds": 11383,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Delivery_registration_census_rejects_missing_duplicate_replaced_and_detached_subjects",
- "milliseconds": 335,
- "source": "local-architecture"
+ "milliseconds": 20,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Hangfire_Bootstrap_Uses_Pinned_Schema_And_Confined_Role",
- "milliseconds": 906,
- "source": "hosted-architecture-first"
+ "milliseconds": 1226,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Integration_Event_Handlers_Use_InboxGuard",
- "milliseconds": 1021,
- "source": "local-architecture"
+ "milliseconds": 1405,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Integration_delivery_lifecycle_census_rejects_typed_helper_async_delegate_and_missing_paths",
- "milliseconds": 81000,
- "source": "hosted-architecture-first"
+ "milliseconds": 106416,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Integration_delivery_lifecycle_stays_with_its_enumerated_owners",
- "milliseconds": 430,
- "source": "local-architecture"
+ "milliseconds": 491,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Integration_handler_census_rejects_missing_assemblies_layers_and_production_subjects",
- "milliseconds": 5735,
- "source": "local-architecture"
+ "milliseconds": 2160,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Integration_handler_discovery_sees_inherited_generic_and_explicit_contracts",
- "milliseconds": 49,
- "source": "local-architecture"
+ "milliseconds": 24,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Integration_invocation_census_rejects_all_layer_direct_interface_helper_async_and_delegate_bypasses",
- "milliseconds": 65000,
- "source": "hosted-architecture-first"
+ "milliseconds": 83219,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Integration_invocation_compiled_controls_distinguish_helpers_inheritance_async_and_clean_methods",
- "milliseconds": 561,
- "source": "hosted-architecture-first"
+ "milliseconds": 516,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Integration_manifest_census_rejects_missing_rebound_duplicate_and_unscoped_bindings",
- "milliseconds": 493,
- "source": "local-architecture"
+ "milliseconds": 231,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Job_terminal_census_detects_missing_subjects_extra_subtypes_factories_and_all_caller_escape_shapes",
- "milliseconds": 16443,
- "source": "local-architecture"
+ "milliseconds": 25195,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Job_trace_source_and_actual_Activity_ownership_refuse_chained_Start_and_hidden_source_creation",
- "milliseconds": 4194,
- "source": "local-architecture"
+ "milliseconds": 6739,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.LearnStack_OutboxAdmin_Role_OnlyUsedBy_OutboxProcessor",
- "milliseconds": 1025,
- "source": "local-architecture"
+ "milliseconds": 2189,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Observation_registration_control_detects_missing_duplicate_or_replaced_worker_and_check",
- "milliseconds": 507,
- "source": "local-architecture"
+ "milliseconds": 69,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Observation_walk_detects_persistence_locators_async_and_polymorphic_helpers",
- "milliseconds": 3469,
- "source": "local-architecture"
+ "milliseconds": 4303,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
@@ -1009,98 +1098,98 @@
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Outbox_Health_And_Metrics_Are_Bounded",
- "milliseconds": 164,
- "source": "local-architecture"
+ "milliseconds": 153,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Outbox_Retry_And_Terminal_Transitions_Are_Fenced",
- "milliseconds": 506,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 649,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Outbox_SQL_census_detects_quoted_schema_CTE_extra_and_missing_writer_paths",
- "milliseconds": 3000,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 3260,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Outbox_SQL_census_preserves_reads_and_unrelated_table_writes",
- "milliseconds": 488,
- "source": "hosted-architecture-first"
+ "milliseconds": 598,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Outbox_Terminal_Audit_Identity_Is_Row_Bound",
- "milliseconds": 441,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 627,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Outbox_Type_Resolution_Is_Declared_And_Bounded",
- "milliseconds": 2272,
- "source": "local-architecture"
+ "milliseconds": 2884,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Publication_census_rejects_interface_concrete_delegate_async_and_unowned_attempt_paths",
- "milliseconds": 5837,
- "source": "local-architecture"
+ "milliseconds": 9171,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Resolution_walk_detects_loading_construction_async_and_polymorphic_helpers",
- "milliseconds": 118063,
- "source": "local-architecture"
+ "milliseconds": 85598,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Resolution_walk_preserves_declared_type_metadata_and_clean_helpers",
- "milliseconds": 4000,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 5232,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Subscriber_terminal_census_rejects_extra_mint_paths_crossed_owners_and_missing_subjects",
- "milliseconds": 1544,
- "source": "local-architecture"
+ "milliseconds": 2114,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Terminal_census_detects_extra_derivers_constructors_and_mint_callers",
- "milliseconds": 9767,
- "source": "local-architecture"
+ "milliseconds": 22280,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Terminal_census_detects_interface_concrete_delegate_async_and_extra_writer_paths",
- "milliseconds": 2434,
- "source": "local-architecture"
+ "milliseconds": 2422,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.Terminal_census_detects_mutability_accessibility_missing_subjects_and_friend_access",
- "milliseconds": 3000,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 2934,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxDispatchConventionTests.The_API_application_source_has_one_owned_lazy_alias_named_factory_and_closed_actual_completion_pin",
- "milliseconds": 2000,
- "source": "hosted-architecture-first"
+ "milliseconds": 5306,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxProducerConventionTests.Outbox_Enqueue_Uses_The_Announced_Transaction",
- "milliseconds": 447,
- "source": "hosted-architecture-current"
+ "milliseconds": 260,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.OutboxProducerConventionTests.Producer_walk_detects_connection_commit_publication_and_interface_helpers",
- "milliseconds": 1000,
- "source": "hosted-architecture-current"
+ "milliseconds": 1534,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
@@ -1111,56 +1200,56 @@
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PersistenceConventionTests.Aggregates_With_Optimistic_Concurrency_Map_RowVersion",
- "milliseconds": 5000,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 1414,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PersistenceConventionTests.Every_Database_Test_Carries_The_Docker_Trait",
- "milliseconds": 20000,
- "source": "hosted-architecture-current"
+ "milliseconds": 13715,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PersistenceConventionTests.Migrate_Target_Applies_The_Tenancy_Chain_First",
- "milliseconds": 27,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 17,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PersistenceConventionTests.Migrate_Target_Covers_Every_Migration_Chain",
- "milliseconds": 37,
- "source": "local-architecture"
+ "milliseconds": 13,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PersistenceConventionTests.Migrate_Target_Reads_The_Role_Through_A_Quoted_Value",
- "milliseconds": 127,
- "source": "local-architecture"
+ "milliseconds": 31,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PersistenceConventionTests.Migrate_Target_Redacts_A_Quoted_Value_Whole",
- "milliseconds": 541,
- "source": "local-architecture"
+ "milliseconds": 107,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PersistenceConventionTests.Migrate_Target_Refuses_A_Uri_Without_Echoing_Its_Userinfo",
- "milliseconds": 121,
- "source": "local-architecture"
+ "milliseconds": 31,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PersistenceConventionTests.Migrate_Target_Refuses_An_Aliased_Runtime_Credential",
- "milliseconds": 500,
- "source": "local-architecture"
+ "milliseconds": 55,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PersistenceConventionTests.Migration_Startup_Project_References_EntityFrameworkCore_Design",
- "milliseconds": 5,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 2,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
@@ -1171,116 +1260,116 @@
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PersistenceConventionTests.Modules_Do_Not_Parallelize_Over_The_Ambient_Connection",
- "milliseconds": 2000,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 724,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PersistenceConventionTests.Recurrence_parallel_allowance_is_limited_to_exact_owned_expressions_and_publication",
- "milliseconds": 748,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 139,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PersistenceConventionTests.The_Docker_Trait_Sweep_Can_Actually_Fail",
- "milliseconds": 294,
- "source": "hosted-architecture-current"
+ "milliseconds": 284,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PersistenceConventionTests.The_Migration_Order_Guard_Rejects_Each_Dependent_Chain_First",
- "milliseconds": 283,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 30,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PersistenceConventionTests.The_Parallelism_Scan_Can_Actually_Fail",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 5,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PersistenceConventionTests.The_Soft_Delete_Index_Sweep_Can_Actually_Fail",
- "milliseconds": 54,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 478,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PersistenceConventionTests.The_parallelism_census_reaches_shared_code_and_bounds_its_singleton_exceptions",
- "milliseconds": 963,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 141,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PersistenceConventionTests.The_registration_marker_does_not_vouch_across_containers",
- "milliseconds": 5,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 3,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PersistenceConventionTests.TransactionBehavior_Does_Not_Reference_A_Module_Assembly",
- "milliseconds": 6,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 10144,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PersistenceConventionTests.Unique_Indexes_On_Soft_Deletable_Tables_Exclude_Deleted_Rows",
- "milliseconds": 6,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 325,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PlatformAdminScopeConventionTests.No_IgnoreQueryFilters_Outside_PlatformAdminScope",
- "milliseconds": 123,
- "source": "hosted-architecture-current"
+ "milliseconds": 155,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PlatformAdminScopeConventionTests.PlatformAdminScope_Entry_Requires_Platform_Permission",
- "milliseconds": 130,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 118,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PlatformAdminScopeConventionTests.Platform_DataSource_Resolved_Only_By_PlatformAdminScope",
- "milliseconds": 848,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 624,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PlatformAdminScopeConventionTests.The_Platform_Scope_Writes_No_Tenant_Context_And_Sets_No_Session_Variable",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 5,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.ProductionAssembliesTests.Project_census_includes_every_project_name_and_excludes_generated_projects",
- "milliseconds": 4,
- "source": "local-architecture"
+ "milliseconds": 6,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PublicSurfaceTests.Dispatch_guard_detects_handler_helper_and_minimal_api_bypasses",
- "milliseconds": 1000,
- "source": "hosted-architecture-current"
+ "milliseconds": 895,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PublicSurfaceTests.Off_guard_detects_every_audit_tier_and_unregistered_requests",
- "milliseconds": 85,
- "source": "hosted-architecture-current"
+ "milliseconds": 52,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PublicSurfaceTests.Persistence_guard_follows_helpers_concrete_types_and_service_location",
- "milliseconds": 95,
- "source": "hosted-architecture-current"
+ "milliseconds": 102,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PublicSurfaceTests.PublicSurface_Controllers_Do_Not_Access_Persistence",
- "milliseconds": 680,
- "source": "hosted-architecture-current"
+ "milliseconds": 442,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
@@ -1291,158 +1380,158 @@
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PublicSurfaceTests.PublicSurface_Reads_Do_Not_Invoke_JsonSchema_Validation",
- "milliseconds": 2000,
- "source": "hosted-architecture-current"
+ "milliseconds": 1497,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PublicSurfaceTests.PublicSurface_Requests_Are_Registered_Off",
- "milliseconds": 78,
- "source": "local-architecture"
+ "milliseconds": 23,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PublicSurfaceTests.Public_validation_guard_follows_direct_concrete_interface_and_generic_helpers",
- "milliseconds": 70,
- "source": "hosted-architecture-current"
+ "milliseconds": 24,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.PublicSurfaceTests.Public_validation_guard_uses_the_production_census_across_all_helper_layers",
- "milliseconds": 6000,
- "source": "hosted-architecture-current"
+ "milliseconds": 2591,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.RepositoryLayoutTests.Commit_Subject_Grammar_Is_Stated_Once",
- "milliseconds": 7765,
- "source": "local-architecture"
+ "milliseconds": 3536,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.RepositoryLayoutTests.Frontend_Has_Only_The_Web_App",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 3,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.RepositoryLayoutTests.No_Source_Folder_Named_Verticals",
- "milliseconds": 31,
- "source": "local-architecture"
+ "milliseconds": 8,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.RequestSurfaceTests.AllowsUnresolvedTenantContext_Only_On_Provisioning_Commands",
- "milliseconds": 23,
- "source": "local-architecture"
+ "milliseconds": 18,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.RequestSurfaceTests.PublicSurface_Marker_Set_Is_Enumerated",
- "milliseconds": 26,
- "source": "local-architecture"
+ "milliseconds": 16,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.RequestSurfaceTests.PublicSurface_Requests_Are_Never_ReadSensitive",
- "milliseconds": 46,
- "source": "local-architecture"
+ "milliseconds": 22,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.RequestSurfaceTests.Requests_Are_Never_Streamed",
- "milliseconds": 26,
- "source": "local-architecture"
+ "milliseconds": 14,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.RequestSurfaceTests.The_PublicSurface_Cross_Check_Can_Actually_Fail",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 3,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.RequestSurfaceTests.The_Request_Filter_Sees_Every_Shape_MediatR_Dispatches",
"milliseconds": 1,
- "source": "local-architecture"
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.RequestSurfaceTests.The_Sweep_Covers_Every_Production_Assembly",
- "milliseconds": 24,
- "source": "local-architecture"
+ "milliseconds": 16,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.SeederConventionTests.Seed_Literal_Source_Is_Complete_And_Readable",
- "milliseconds": 128,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 184,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.SeederConventionTests.Seeder_Caller_Fence_Catches_Calls_Method_Groups_And_Sql_But_Admits_Dispatch",
- "milliseconds": 2,
- "source": "local-architecture"
+ "milliseconds": 6,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.SeederConventionTests.Seeder_Does_Not_Call_Tenant_Context_Setters",
- "milliseconds": 56,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 374,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.SeederConventionTests.Seeder_Uses_The_Shared_Application_Role_Guard",
- "milliseconds": 46,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 37,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.SeederConventionTests.Seeder_Write_Fence_Catches_Ef_Mutations_And_Ad_Hoc_Commands",
- "milliseconds": 3,
- "source": "local-architecture"
+ "milliseconds": 4,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenancyConventionTests.Assertion_Budget_Does_Not_Depend_On_ICacheService",
- "milliseconds": 4,
- "source": "hosted-architecture-current"
+ "milliseconds": 18,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenancyConventionTests.Assertion_Recorder_Is_The_Only_Mismatch_Writer",
- "milliseconds": 10,
- "source": "hosted-architecture-current"
+ "milliseconds": 1314,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenancyConventionTests.Assertion_Recorder_Is_The_Only_Writer_Of_Its_Audit_Slugs",
- "milliseconds": 96,
- "source": "local-architecture"
+ "milliseconds": 1181,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenancyConventionTests.Effective_Host_Computed_In_One_Place",
- "milliseconds": 70,
- "source": "hosted-architecture-current"
+ "milliseconds": 47,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenancyConventionTests.Host_Resolution_Makes_No_Outbound_Calls",
- "milliseconds": 30,
- "source": "hosted-architecture-current"
+ "milliseconds": 66,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenancyConventionTests.Organization_Aggregate_Declared_In_Tenancy_Domain",
- "milliseconds": 3,
- "source": "hosted-architecture-current"
+ "milliseconds": 4,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenancyConventionTests.Out_Of_Band_Setters_Open_Read_Only_Transactions",
- "milliseconds": 131,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 109,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.architecture",
@@ -1453,38 +1542,38 @@
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenancyConventionTests.Tenant_Headers_Are_Never_A_Resolution_Source",
- "milliseconds": 29,
- "source": "hosted-architecture-current"
+ "milliseconds": 1909,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenancyConventionTests.Tenant_Scope_Widening_Is_Never_Set_From_Request_Input",
- "milliseconds": 276,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 169,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenancyConventionTests.The_Host_Read_Scan_Can_Actually_Fail",
"milliseconds": 2,
- "source": "hosted-architecture-current"
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenancyConventionTests.The_Outbound_Dependency_Scan_Can_Actually_Fail",
- "milliseconds": 12,
- "source": "hosted-architecture-current"
+ "milliseconds": 14,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenancyConventionTests.The_Scope_Setter_Scan_Can_Actually_Fail",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 3,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenancyConventionTests.The_Setter_Scan_Can_Actually_Fail",
- "milliseconds": 7,
- "source": "local-architecture"
+ "milliseconds": 8,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
@@ -1495,56 +1584,56 @@
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenantContextConstructionTests.HostScope_Is_Constructed_Only_By_The_Factory",
- "milliseconds": 31,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 20,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenantContextConstructionTests.Host_scope_guard_detects_mutable_public_shape_and_a_second_construction_site",
- "milliseconds": 33,
- "source": "hosted-architecture-current"
+ "milliseconds": 43,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenantContextConstructionTests.Organization_reader_census_rejects_missing_keys_extra_readers_and_unbounded_pages",
- "milliseconds": 4000,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 6359,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenantContextConstructionTests.Organizations_Are_Read_By_Composite_Key",
- "milliseconds": 522,
- "source": "hosted-architecture-first"
+ "milliseconds": 308,
+ "source": "hosted-architecture-39417319998-2-0"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenantContextConstructionTests.SetTenant_Callers_Are_The_Enumerated_Four",
- "milliseconds": 433,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 267,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenantContextConstructionTests.TenantContext_Is_Constructed_Only_By_The_Factory",
- "milliseconds": 9,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 11,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenantContextConstructionTests.TenantContext_Is_Instantiated_In_One_File",
- "milliseconds": 24,
- "source": "local-architecture"
+ "milliseconds": 23,
+ "source": "hosted-architecture-39417319998-2-2"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenantContextConstructionTests.Tenant_writer_scan_does_not_authorize_another_job_partial_or_an_equality_read",
- "milliseconds": 4,
- "source": "local-architecture"
+ "milliseconds": 6,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenantScopingTests.Every_Module_With_A_Schema_Is_Swept",
- "milliseconds": 2,
- "source": "hosted-architecture-predecessor"
+ "milliseconds": 9,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
@@ -1555,26 +1644,26 @@
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenantScopingTests.Every_Scoping_Interface_Carries_Its_Marker",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 2,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenantScopingTests.Every_TenantOwned_Entity_HasFilterAndRlsPolicy",
- "milliseconds": 51,
- "source": "hosted-architecture-current"
+ "milliseconds": 1312,
+ "source": "hosted-architecture-39417319998-2-5"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenantScopingTests.The_Host_Map_Carries_No_Tenant_Marker",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 1093,
+ "source": "hosted-architecture-39417319998-2-1"
},
{
"assembly": "learnstack.tests.architecture",
"method": "LearnStack.Tests.Architecture.TenantScopingTests.The_Tenant_Term_Check_Can_Actually_Fail",
- "milliseconds": 1,
- "source": "local-architecture"
+ "milliseconds": 2,
+ "source": "hosted-architecture-39417319998-2-4"
},
{
"assembly": "learnstack.tests.integration",
diff --git a/scripts/ci-test-shards.py b/scripts/ci-test-shards.py
index 21909e36..c957dd03 100644
--- a/scripts/ci-test-shards.py
+++ b/scripts/ci-test-shards.py
@@ -105,7 +105,9 @@ def validate_plan(plan):
require(isinstance(name, str) and re.fullmatch(r'[a-z0-9_-]{1,64}', name)
and isinstance(source, dict) and set(source) == {'kind', 'artifactSha256',
'sourceSha', 'runId', 'note'}
- and source['kind'] in ('local_trx', 'hosted_console')
+ and source['kind'] in ('local_trx', 'hosted_console', 'hosted_trx')
+ and (source['kind'] != 'hosted_trx'
+ or source['sourceSha'] is not None and source['runId'] is not None)
and isinstance(source['artifactSha256'], str)
and re.fullmatch(r'[0-9a-f]{64}', source['artifactSha256'])
and (source['sourceSha'] is None or isinstance(source['sourceSha'], str)
diff --git a/scripts/demo-process.d.mts b/scripts/demo-process.d.mts
index 96a9a11f..374620d5 100644
--- a/scripts/demo-process.d.mts
+++ b/scripts/demo-process.d.mts
@@ -1,7 +1,19 @@
import type { ChildProcess } from 'node:child_process';
export function installDemoCancellation(cancel: () => void): () => void;
export class DemoCommandError extends Error {
- constructor(label: string, cause?: unknown);
+ constructor(
+ label: string,
+ cause?: unknown,
+ status?: { code: number | null; signal: string | null },
+ outcome?: {
+ reason?: 'exit' | 'deadline' | 'cancelled' | 'spawn' | 'output' | 'cleanup' | 'unknown';
+ origin?: 'command' | 'daemon';
+ },
+ );
+ readonly reason: 'exit' | 'deadline' | 'cancelled' | 'spawn' | 'output' | 'cleanup' | 'unknown';
+ readonly origin: 'command' | 'daemon';
+ readonly exitCode: number | null;
+ readonly signalCode: string | null;
}
interface Options {
cwd?: string;
@@ -13,8 +25,12 @@ interface Options {
}
interface Running {
child: ChildProcess;
- exit: Promise<{ code: number }>;
- result(): { output: string; failed?: Error };
+ /** Native failures publish at exit; success retains complete output through close. */
+ exit: Promise<{ code: number | null; signal: string | null }>;
+ /** Every daemon leader exit is unexpected, independently of stream closure. */
+ nativeExit: Promise<{ code: number | null; signal: string | null }>;
+ refusal: Promise<{ cause: unknown; reason: 'spawn' | 'output' }>;
+ result(): { output: string; failed?: Error; failureReason?: 'spawn' | 'output' };
}
export class DemoProcesses {
constructor(signal: AbortSignal);
diff --git a/scripts/demo-process.mjs b/scripts/demo-process.mjs
index f42b9827..9e054d53 100644
--- a/scripts/demo-process.mjs
+++ b/scripts/demo-process.mjs
@@ -1,10 +1,26 @@
import { spawn } from 'node:child_process';
import { stopTestChild } from '../frontend/apps/web/scripts/stop-test-child.mjs';
+const commandReasons = new Set([
+ 'exit',
+ 'deadline',
+ 'cancelled',
+ 'spawn',
+ 'output',
+ 'cleanup',
+ 'unknown',
+]);
+
/** Only fixed caller-owned stage labels reach the foreground CLI. */
export class DemoCommandError extends Error {
- constructor(label, cause) {
+ constructor(label, cause, status, { reason = 'unknown', origin = 'command' } = {}) {
super(`demo: ${label} failed`, { cause });
+ this.reason = commandReasons.has(reason) ? reason : 'unknown';
+ this.origin = origin === 'daemon' ? 'daemon' : 'command';
+ // Only an observed spontaneous exit owns native status. A subsequent stop
+ // cannot supply the status of a deadline, spawn, output or cleanup failure.
+ this.exitCode = this.reason === 'exit' ? (status?.code ?? null) : null;
+ this.signalCode = this.reason === 'exit' ? (status?.signal ?? null) : null;
}
}
@@ -44,26 +60,43 @@ export class DemoProcesses {
}
assertActive() {
if (this.#closed) throw new Error('Demo process owner is closed');
- this.#signal.throwIfAborted();
+ if (this.#signal.aborted)
+ throw new DemoCommandError('Owned command cancellation', this.#signal.reason, undefined, {
+ reason: 'cancelled',
+ });
if (this.#failed) throw this.#failed;
}
start(command, args, { cwd, env, label, observe = () => {}, capture = false } = {}) {
this.assertActive();
- const child = spawn(command, args, {
- cwd,
- env,
- detached: process.platform !== 'win32',
- stdio: ['ignore', 'pipe', 'pipe'],
- });
+ let child;
+ try {
+ child = spawn(command, args, {
+ cwd,
+ env,
+ detached: process.platform !== 'win32',
+ stdio: ['ignore', 'pipe', 'pipe'],
+ });
+ } catch (cause) {
+ throw new DemoCommandError(label ?? command, cause, undefined, { reason: 'spawn' });
+ }
const closed = new Promise((resolve) => child.once('close', resolve));
const output = [];
let outputBytes = 0;
let failed;
+ let failureReason;
+ let publishRefusal;
+ const refusal = new Promise((resolve) => { publishRefusal = resolve; });
+ const refuseOutput = (cause) => {
+ if (!failed) {
+ failed = new Error(`${label} output refused`, { cause });
+ failureReason = 'output';
+ publishRefusal({ cause: failed, reason: failureReason });
+ }
+ // dispose still owns a failed stop; do not create an unhandled rejection.
+ void stopTestChild(child).catch(() => {});
+ };
for (const stream of [child.stdout, child.stderr]) {
- stream.once('error', (cause) => {
- failed ??= new Error(`${label} output refused`, { cause });
- void stopTestChild(child);
- });
+ stream.once('error', refuseOutput);
stream.on('data', (chunk) => {
try {
observe(chunk, stream === child.stdout ? 'stdout' : 'stderr');
@@ -73,21 +106,41 @@ export class DemoProcesses {
output.push(chunk);
}
} catch (cause) {
- failed ??= new Error(`${label} output refused`, { cause });
- void stopTestChild(child);
+ refuseOutput(cause);
}
});
}
+ let publishNativeExit;
+ const nativeExit = new Promise((resolve) => { publishNativeExit = resolve; });
const exit = new Promise((resolve) => {
- child.once('error', () => resolve({ code: 1 }));
- child.once('close', (code) => resolve({ code: code ?? 1 }));
+ child.once('error', (cause) => {
+ if (!failed) {
+ failed = cause;
+ failureReason = 'spawn';
+ publishRefusal({ cause: failed, reason: failureReason });
+ }
+ const status = { code: null, signal: null };
+ publishNativeExit(status);
+ resolve(status);
+ });
+ child.once('exit', (code, signal) => {
+ const status = { code, signal };
+ publishNativeExit(status);
+ // A failed leader is already authoritative even if inherited/paused
+ // streams delay close. Successful captured output still waits for EOF.
+ if (code !== 0 || signal !== null) resolve(status);
+ });
+ child.once('close', (code, signal) => resolve({ code, signal }));
});
const running = {
child,
exit,
+ nativeExit,
+ refusal,
result: () => ({
output: Buffer.concat(output).toString('utf8'),
failed,
+ failureReason,
}),
};
this.#children.set(child, {
@@ -100,9 +153,19 @@ export class DemoProcesses {
daemon(command, args, options) {
const running = this.start(command, args, options);
this.#children.get(running.child).daemon = true;
- void running.exit.then(() => {
- this.#failed = new DemoCommandError(`${options.label} exited`, running.result().failed);
+ const fail = (cause, status, reason) => {
+ if (this.#failed) return;
+ this.#failed = new DemoCommandError(
+ `${options.label} ${reason === 'exit' ? 'exited' : 'refused'}`,
+ cause,
+ status,
+ { reason, origin: 'daemon' },
+ );
this.#failure(this.#failed);
+ };
+ void running.refusal.then(({ cause, reason }) => fail(cause, undefined, reason));
+ void running.nativeExit.then((status) => {
+ fail(running.result().failed, status, running.result().failureReason ?? 'exit');
});
return running;
}
@@ -112,26 +175,52 @@ export class DemoProcesses {
const signal = AbortSignal.any([this.#signal, timeout]);
let cancelled;
const aborted = new Promise((resolve) => {
- cancelled = () => resolve({ code: 1, cancelled: true });
+ cancelled = () => resolve({ kind: 'aborted' });
signal.addEventListener('abort', cancelled, { once: true });
if (signal.aborted) cancelled();
});
+ let failure;
try {
const result = await Promise.race([
- running.exit,
+ running.exit.then((status) => ({ kind: 'exit', status })),
+ running.refusal.then((outcome) => ({ kind: 'refusal', outcome })),
aborted,
- this.failure.then(() => ({ code: 1 })),
+ this.failure.then((error) => ({ kind: 'daemon', error })),
]);
- if (result.code !== 0 || running.result().failed)
+ if (result.kind === 'daemon') throw result.error;
+ if (result.kind === 'refusal')
+ throw new DemoCommandError(options.label ?? command, result.outcome.cause, undefined, {
+ reason: result.outcome.reason,
+ });
+ if (result.kind === 'aborted')
throw new DemoCommandError(
options.label ?? command,
- running.result().failed ?? this.#failed,
+ this.#signal.aborted ? this.#signal.reason : timeout.reason,
+ undefined,
+ { reason: this.#signal.aborted ? 'cancelled' : 'deadline' },
);
+ const outcome = running.result();
+ if (result.status.code !== 0 || outcome.failed)
+ throw new DemoCommandError(options.label ?? command, outcome.failed, result.status, {
+ reason: outcome.failureReason ?? 'exit',
+ });
this.assertActive();
return running.result().output;
+ } catch (error) {
+ failure = error;
+ throw error;
} finally {
signal.removeEventListener('abort', cancelled);
- await stopTestChild(running.child);
+ try {
+ await stopTestChild(running.child);
+ } catch (error) {
+ // Retain this handle for dispose's cleanup failure; do not replace the
+ // triggering command failure or attribute teardown's signal to it.
+ if (!failure)
+ throw new DemoCommandError(options.label ?? command, error, undefined, {
+ reason: 'cleanup',
+ });
+ }
}
}
async dispose() {
@@ -157,7 +246,7 @@ export class DemoProcesses {
} finally {
clearTimeout(timer);
}
- if (daemon && result().failed)
+ if (daemon && result().failureReason === 'output')
throw new Error('Owned output was refused', {
cause: result().failed,
});
diff --git a/scripts/demo-source.d.mts b/scripts/demo-source.d.mts
new file mode 100644
index 00000000..503f427c
--- /dev/null
+++ b/scripts/demo-source.d.mts
@@ -0,0 +1,11 @@
+export interface DemoSource {
+ readonly commit: string;
+ readonly tree: string;
+}
+
+export function demoSource(
+ processes: Pick,
+ root: string,
+ remaining: (bound: number) => number,
+ expected?: DemoSource,
+): Promise;
diff --git a/scripts/demo-source.mjs b/scripts/demo-source.mjs
new file mode 100644
index 00000000..159d16e2
--- /dev/null
+++ b/scripts/demo-source.mjs
@@ -0,0 +1,46 @@
+import { realpathSync } from 'node:fs';
+
+/** Read only this checkout, without ambient Git repository/configuration controls. */
+export async function demoSource(processes, root, remaining, expected) {
+ const ownedRoot = realpathSync(root);
+ const environment = {
+ PATH: process.env.PATH,
+ GIT_CONFIG_NOSYSTEM: '1',
+ GIT_CONFIG_GLOBAL: process.platform === 'win32' ? 'NUL' : '/dev/null',
+ GIT_OPTIONAL_LOCKS: '0',
+ GIT_TERMINAL_PROMPT: '0',
+ };
+ const git = (args) => {
+ const timeout = remaining(15000);
+ if (!Number.isFinite(timeout) || timeout <= 0)
+ throw new Error('Demo source verification deadline');
+ return processes.run('git', args, {
+ cwd: ownedRoot,
+ env: environment,
+ label: 'Read-only demo source verification',
+ capture: true,
+ timeout: Math.min(timeout, 15000),
+ });
+ };
+ const topLevel = (await git(['rev-parse', '--show-toplevel'])).trim();
+ if (realpathSync(topLevel) !== ownedRoot)
+ throw new Error('Demo source must belong to the owned repository root');
+ const identity = async () => {
+ const lines = (await git(['rev-parse', 'HEAD', 'HEAD^{tree}'])).trim().split('\n');
+ if (lines.length !== 2 || lines.some((value) => !/^[a-f0-9]{40}$/.test(value)))
+ throw new Error('Demo source identity is invalid');
+ return Object.freeze({ commit: lines[0], tree: lines[1] });
+ };
+ const before = await identity();
+ // Include staged, tracked and nonignored untracked inputs. Status output stays
+ // private; ignored build/configuration artifacts are not source attestation.
+ const status = await git([
+ 'status', '--porcelain=v1', '-z', '--untracked-files=all', '--ignore-submodules=none',
+ ]);
+ if (status !== '') throw new Error('CI demo requires clean Git source');
+ const after = await identity();
+ if (after.commit !== before.commit || after.tree !== before.tree ||
+ (expected && (after.commit !== expected.commit || after.tree !== expected.tree)))
+ throw new Error('Demo source changed since its captured checkpoint');
+ return after;
+}
diff --git a/scripts/demo.d.mts b/scripts/demo.d.mts
index 5392fc16..030be516 100644
--- a/scripts/demo.d.mts
+++ b/scripts/demo.d.mts
@@ -15,6 +15,7 @@ export function disposeDemoResources(options: {
}): Promise;
export function startDemo(mode: 'local' | 'ci'): Promise<{
mode: 'local' | 'ci';
+ source?: import('./demo-source.mjs').DemoSource;
manifest: DemoLane[];
configuration: import('../frontend/apps/web/src/server/ingress').PublicServerConfiguration;
ca: string[] | Buffer;
diff --git a/scripts/demo.mjs b/scripts/demo.mjs
index 16384a35..fc56fab4 100644
--- a/scripts/demo.mjs
+++ b/scripts/demo.mjs
@@ -21,6 +21,7 @@ import { defaultServices, waitCompose } from './wait-compose.mjs';
import { publicDemoApiEnvironment, publicWebEnvironment } from './public-runtime.mjs';
import { apiCompletionObserver } from './demo-observation.mjs';
import { publicSeederEnvironment, validateDemoDatabaseConfiguration } from './demo-database.mjs';
+import { demoSource } from './demo-source.mjs';
const appRoot = join(repositoryRoot, 'frontend/apps/web');
const backendRoot = join(repositoryRoot, 'backend');
@@ -100,6 +101,7 @@ export async function startDemo(mode) {
let disposed;
let completions;
let signalRequested = false;
+ let source;
const cancel = () => {
signalRequested = true;
controller.abort();
@@ -197,6 +199,18 @@ export async function startDemo(mode) {
try {
if (process.platform === 'win32')
throw new Error('The demo requires the documented POSIX/WSL environment');
+ if (mode === 'ci') {
+ // The workflow already owns the absolute setup/proof allowance. Manual CI
+ // use retains the existing finite prerequisite-command bound.
+ const workflowDeadline = process.env.PUBLIC_DEMO_DEADLINE === undefined
+ ? null : Number(process.env.PUBLIC_DEMO_DEADLINE) * 1000;
+ source = await demoSource(processes, repositoryRoot, (bound) => {
+ if (workflowDeadline === null) return bound;
+ if (!Number.isSafeInteger(workflowDeadline))
+ throw new Error('CI demo deadline is invalid');
+ return Math.min(bound, workflowDeadline - Date.now());
+ });
+ }
mkdirSync(join(repositoryRoot, '.data'), { recursive: true });
try {
mkdirSync(lock, { mode: 0o700 });
@@ -320,7 +334,7 @@ export async function startDemo(mode) {
for (const [key, value] of Object.entries(rawValues))
if (process.env[key] !== undefined && process.env[key] !== value)
throw new Error('Environment disagrees with private root configuration');
- const values = loadLocalEnvironment(repositoryRoot, process.env);
+ const values = loadLocalEnvironment(repositoryRoot, process.env, { apiAuthentication: true });
const configuration = publicServerConfiguration(values, repositoryRoot);
await vacant(Number(new URL(configuration.apiOrigin).port || '80'));
validateDemoDatabaseConfiguration(rawValues);
@@ -487,6 +501,7 @@ export async function startDemo(mode) {
for (const url of [lane.catalogUrl, lane.courseUrl, lane.lessonUrl]) console.log(url);
return {
mode,
+ source,
manifest,
configuration,
ca,
diff --git a/scripts/public-local.mjs b/scripts/public-local.mjs
index 9b7167ce..d004c6fe 100644
--- a/scripts/public-local.mjs
+++ b/scripts/public-local.mjs
@@ -42,7 +42,7 @@ try {
chmodSync(path, 0o600);
console.warn('Private renderer configuration prepared; TLS and hosts remain manual.');
} else if (operation === 'api') {
- const values = loadLocalEnvironment(root, process.env);
+ const values = loadLocalEnvironment(root, process.env, { apiAuthentication: true });
const configuration = publicServerConfiguration(values, root);
if (!values.ConnectionStrings__Default) throw new Error();
const environment = publicApiEnvironment(values, configuration);
diff --git a/scripts/public-runtime.mjs b/scripts/public-runtime.mjs
index 86be368a..869dbbfc 100644
--- a/scripts/public-runtime.mjs
+++ b/scripts/public-runtime.mjs
@@ -9,6 +9,8 @@ export function publicApiEnvironment(values, configuration, base = process.env)
if (!values.ConnectionStrings__Default) throw new Error('Application credential is missing');
if (!values.ConnectionStrings__OutboxDispatcher)
throw new Error('Outbox dispatcher credential is missing');
+ if (values.Authentication__Tenant__Issuer)
+ environment.Authentication__Tenant__Issuer = values.Authentication__Tenant__Issuer;
environment.ConnectionStrings__Default = values.ConnectionStrings__Default;
environment.ConnectionStrings__OutboxDispatcher = values.ConnectionStrings__OutboxDispatcher;
if (values.ConnectionStrings__PlatformAdmin)
diff --git a/scripts/test-ci-test-shards.py b/scripts/test-ci-test-shards.py
index 771aab2b..65003f74 100644
--- a/scripts/test-ci-test-shards.py
+++ b/scripts/test-ci-test-shards.py
@@ -276,6 +276,30 @@ def test_duplicate_json_weights_and_false_type_provenance_are_refused(self):
dirty = copy.deepcopy(self.plan); next(iter(dirty['sources'].values()))['artifactSha256'] = 'not-a-hash'
self.refuses('weight_source_invalid', OWNER.validate_plan, dirty)
+ def test_hosted_trx_provenance_is_complete_and_unknown_kinds_are_refused(self):
+ plan = copy.deepcopy(self.plan)
+ source = next(iter(plan['sources'].values()))
+ source['kind'] = 'hosted_trx'
+ source['sourceSha'] = '1' * 40
+ source['runId'] = '1'
+ OWNER.validate_plan(plan)
+ for sha, run in ((None, '1'), ('1' * 40, None), (None, None)):
+ with self.subTest(sourceSha=sha, runId=run):
+ dirty = copy.deepcopy(plan)
+ record = next(iter(dirty['sources'].values()))
+ record['sourceSha'], record['runId'] = sha, run
+ self.refuses('weight_source_invalid', OWNER.validate_plan, dirty)
+ for kind in ('local_trx', 'hosted_console'):
+ with self.subTest(legacyKind=kind):
+ legacy = copy.deepcopy(plan)
+ record = next(iter(legacy['sources'].values()))
+ record['kind'], record['sourceSha'], record['runId'] = kind, None, None
+ OWNER.validate_plan(legacy)
+ for kind in ('hosted_trx_extra', 'hosted_results', '', None, True):
+ with self.subTest(kind=kind):
+ source['kind'] = kind
+ self.refuses('weight_source_invalid', OWNER.validate_plan, plan)
+
def result_fixture(self, output, expected, actual):
(output / 'results').mkdir(exist_ok=True)
tag = lambda name: '{' + LAUNCHER.NS['t'] + '}' + name