@cat-factory/contracts 0.347.0 → 0.349.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/environment-probe.d.ts +201 -0
- package/dist/environment-probe.d.ts.map +1 -0
- package/dist/environment-probe.js +316 -0
- package/dist/environment-probe.js.map +1 -0
- package/dist/environment-test.d.ts +81 -4
- package/dist/environment-test.d.ts.map +1 -1
- package/dist/environment-test.js +54 -4
- package/dist/environment-test.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +27 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/platform-agent-kinds.d.ts +24 -0
- package/dist/platform-agent-kinds.d.ts.map +1 -1
- package/dist/platform-agent-kinds.js +26 -0
- package/dist/platform-agent-kinds.js.map +1 -1
- package/dist/routes/environments.d.ts +119 -7
- package/dist/routes/environments.d.ts.map +1 -1
- package/dist/routes/environments.js +9 -2
- package/dist/routes/environments.js.map +1 -1
- package/dist/routes/workspaces.d.ts +72 -4
- package/dist/routes/workspaces.d.ts.map +1 -1
- package/dist/snapshot.d.ts +36 -2
- package/dist/snapshot.d.ts.map +1 -1
- package/dist/test-secrets.d.ts +29 -0
- package/dist/test-secrets.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import * as v from 'valibot';
|
|
2
|
+
import type { BlockType } from './primitives.js';
|
|
3
|
+
/** Which surface a dry run drove: HTTP calls, or a browser. */
|
|
4
|
+
export declare const environmentProbeSurfaceSchema: v.PicklistSchema<["api", "ui"], undefined>;
|
|
5
|
+
export type EnvironmentProbeSurface = v.InferOutput<typeof environmentProbeSurfaceSchema>;
|
|
6
|
+
/**
|
|
7
|
+
* Which surface a service frame's dry run drives, from the frame's own type.
|
|
8
|
+
*
|
|
9
|
+
* A `frontend` frame is a browser job and everything else is a protocol job: there is no third
|
|
10
|
+
* answer to invent, and no frame type where the choice is genuinely ambiguous: `library` and
|
|
11
|
+
* `document` frames have no ephemeral-environment provisioning to test, so a dry run never
|
|
12
|
+
* reaches one. Stated in contracts rather than in the engine because BOTH sides say it to a
|
|
13
|
+
* human: the backend picks the prober, and the SPA's button has to tell the user which one it is
|
|
14
|
+
* about to spend money on.
|
|
15
|
+
*/
|
|
16
|
+
export declare function environmentProbeSurfaceFor(type: BlockType): EnvironmentProbeSurface;
|
|
17
|
+
/**
|
|
18
|
+
* Why one attempted operation did not work, or why the agent never got to attempt it.
|
|
19
|
+
*
|
|
20
|
+
* The set is shaped around WHOSE PROBLEM each member is, because that is the whole output of the
|
|
21
|
+
* diagnostic:
|
|
22
|
+
*
|
|
23
|
+
* - `auth_missing` / `access_unclear` / `endpoint_unknown`: the PLATFORM did not tell the agent
|
|
24
|
+
* enough. These are the findings this feature exists to surface: nothing is broken in the
|
|
25
|
+
* service, the run would simply have been spent guessing.
|
|
26
|
+
* - `auth_rejected` / `bad_request`: the agent knew what to send and the service refused it. A
|
|
27
|
+
* credential that is present and rejected is a different fix from one that was never supplied,
|
|
28
|
+
* which is why they are not one member.
|
|
29
|
+
* - `unreachable` / `timeout` / `server_error`: the ENVIRONMENT is at fault, so the dry run has
|
|
30
|
+
* found something the provisioning self-test's `ready` verdict did not.
|
|
31
|
+
* - `tooling_missing`: the container had no HTTP client or browser, so the attempt says nothing
|
|
32
|
+
* about the environment at all. Its own member for that reason: collapsed into `other` it reads
|
|
33
|
+
* as a service that failed.
|
|
34
|
+
* - `other`: anything else, with the agent's own detail carrying the specifics.
|
|
35
|
+
*/
|
|
36
|
+
export declare const environmentProbeFailureSchema: v.PicklistSchema<["auth_missing", "auth_rejected", "access_unclear", "endpoint_unknown", "unreachable", "timeout", "server_error", "bad_request", "tooling_missing", "other"], undefined>;
|
|
37
|
+
export type EnvironmentProbeFailure = v.InferOutput<typeof environmentProbeFailureSchema>;
|
|
38
|
+
/** The failure vocabulary as a list, for a reader that has to enumerate it. */
|
|
39
|
+
export declare const ENVIRONMENT_PROBE_FAILURES: ["auth_missing", "auth_rejected", "access_unclear", "endpoint_unknown", "unreachable", "timeout", "server_error", "bad_request", "tooling_missing", "other"];
|
|
40
|
+
/** Whether a value is a currently-known failure kind (derived from the picklist's own options). */
|
|
41
|
+
export declare function isEnvironmentProbeFailure(value: unknown): value is EnvironmentProbeFailure;
|
|
42
|
+
/** How one attempted operation turned out. */
|
|
43
|
+
export declare const environmentProbeOutcomeSchema: v.PicklistSchema<["succeeded", "failed", "not_attempted"], undefined>;
|
|
44
|
+
export type EnvironmentProbeOutcome = v.InferOutput<typeof environmentProbeOutcomeSchema>;
|
|
45
|
+
/**
|
|
46
|
+
* The platform's verdict on a dry run, COMPUTED from the operations the agent reported (see
|
|
47
|
+
* {@link summarizeEnvironmentProbe}) rather than read off the reply.
|
|
48
|
+
*
|
|
49
|
+
* - `operable`: every operation the agent listed was attempted, every one of them worked, and at
|
|
50
|
+
* least one exercised authentication.
|
|
51
|
+
* - `partially_operable`: something worked, but not everything, or nothing that worked was
|
|
52
|
+
* behind auth, or the agent listed operations it could not even attempt. A public healthcheck
|
|
53
|
+
* answering is not evidence that an agent can operate the service, which is why it cannot earn
|
|
54
|
+
* `operable` on its own, and neither is a run whose one success sits beside four things the
|
|
55
|
+
* agent could not work out how to try.
|
|
56
|
+
* - `inoperable`: nothing the agent attempted worked, or it never got to attempt anything.
|
|
57
|
+
*/
|
|
58
|
+
export declare const environmentProbeVerdictSchema: v.PicklistSchema<["operable", "partially_operable", "inoperable"], undefined>;
|
|
59
|
+
export type EnvironmentProbeVerdict = v.InferOutput<typeof environmentProbeVerdictSchema>;
|
|
60
|
+
/** One operation the agent attempted against the live environment. */
|
|
61
|
+
export declare const environmentProbeOperationSchema: v.ObjectSchema<{
|
|
62
|
+
/** What it was, in the agent's own words ("list projects", "sign in and open the dashboard"). */
|
|
63
|
+
readonly name: v.StringSchema<undefined>;
|
|
64
|
+
/** How it was performed: `GET /api/v1/projects`, or the UI path it clicked through. */
|
|
65
|
+
readonly target: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
66
|
+
/**
|
|
67
|
+
* Whether this operation actually exercised authentication. The one field the verdict turns on:
|
|
68
|
+
* a dry run whose only successes are anonymous has not shown that an agent can operate the
|
|
69
|
+
* service, so it never reaches `operable`.
|
|
70
|
+
*/
|
|
71
|
+
readonly authenticated: v.BooleanSchema<undefined>;
|
|
72
|
+
readonly outcome: v.PicklistSchema<["succeeded", "failed", "not_attempted"], undefined>;
|
|
73
|
+
/** Why it did not work. Absent on a success. */
|
|
74
|
+
readonly failure: v.OptionalSchema<v.PicklistSchema<["auth_missing", "auth_rejected", "access_unclear", "endpoint_unknown", "unreachable", "timeout", "server_error", "bad_request", "tooling_missing", "other"], undefined>, undefined>;
|
|
75
|
+
/** The agent's evidence: the status code, the message, what it saw. */
|
|
76
|
+
readonly detail: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
77
|
+
}, undefined>;
|
|
78
|
+
export type EnvironmentProbeOperation = v.InferOutput<typeof environmentProbeOperationSchema>;
|
|
79
|
+
/** Something that stopped the dry run as a whole, rather than one operation. */
|
|
80
|
+
export declare const environmentProbeBlockerSchema: v.ObjectSchema<{
|
|
81
|
+
readonly kind: v.PicklistSchema<["auth_missing", "auth_rejected", "access_unclear", "endpoint_unknown", "unreachable", "timeout", "server_error", "bad_request", "tooling_missing", "other"], undefined>;
|
|
82
|
+
readonly detail: v.StringSchema<undefined>;
|
|
83
|
+
}, undefined>;
|
|
84
|
+
export type EnvironmentProbeBlocker = v.InferOutput<typeof environmentProbeBlockerSchema>;
|
|
85
|
+
/** A dry run's report, as persisted on the run row and rendered by the SPA. */
|
|
86
|
+
export declare const environmentProbeReportSchema: v.ObjectSchema<{
|
|
87
|
+
readonly surface: v.PicklistSchema<["api", "ui"], undefined>;
|
|
88
|
+
/** Platform-computed; never taken from the reply. */
|
|
89
|
+
readonly verdict: v.PicklistSchema<["operable", "partially_operable", "inoperable"], undefined>;
|
|
90
|
+
/** The agent's own account of what it did and what it concluded. */
|
|
91
|
+
readonly summary: v.StringSchema<undefined>;
|
|
92
|
+
/** Every operation it attempted (or explicitly did not), in the order it reported them. */
|
|
93
|
+
readonly operations: v.ArraySchema<v.ObjectSchema<{
|
|
94
|
+
/** What it was, in the agent's own words ("list projects", "sign in and open the dashboard"). */
|
|
95
|
+
readonly name: v.StringSchema<undefined>;
|
|
96
|
+
/** How it was performed: `GET /api/v1/projects`, or the UI path it clicked through. */
|
|
97
|
+
readonly target: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
98
|
+
/**
|
|
99
|
+
* Whether this operation actually exercised authentication. The one field the verdict turns on:
|
|
100
|
+
* a dry run whose only successes are anonymous has not shown that an agent can operate the
|
|
101
|
+
* service, so it never reaches `operable`.
|
|
102
|
+
*/
|
|
103
|
+
readonly authenticated: v.BooleanSchema<undefined>;
|
|
104
|
+
readonly outcome: v.PicklistSchema<["succeeded", "failed", "not_attempted"], undefined>;
|
|
105
|
+
/** Why it did not work. Absent on a success. */
|
|
106
|
+
readonly failure: v.OptionalSchema<v.PicklistSchema<["auth_missing", "auth_rejected", "access_unclear", "endpoint_unknown", "unreachable", "timeout", "server_error", "bad_request", "tooling_missing", "other"], undefined>, undefined>;
|
|
107
|
+
/** The agent's evidence: the status code, the message, what it saw. */
|
|
108
|
+
readonly detail: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
109
|
+
}, undefined>, undefined>;
|
|
110
|
+
/**
|
|
111
|
+
* What the PLATFORM failed to tell it: a credential it needed and had no reference for, an
|
|
112
|
+
* endpoint it could not discover, a base path it had to guess. The primary product of the
|
|
113
|
+
* diagnostic, since each entry is a thing to fix before a real run spends a step on it.
|
|
114
|
+
*/
|
|
115
|
+
readonly missingContext: v.ArraySchema<v.StringSchema<undefined>, undefined>;
|
|
116
|
+
/** Whole-run blockers, distinct from one operation's failure. */
|
|
117
|
+
readonly blockers: v.ArraySchema<v.ObjectSchema<{
|
|
118
|
+
readonly kind: v.PicklistSchema<["auth_missing", "auth_rejected", "access_unclear", "endpoint_unknown", "unreachable", "timeout", "server_error", "bad_request", "tooling_missing", "other"], undefined>;
|
|
119
|
+
readonly detail: v.StringSchema<undefined>;
|
|
120
|
+
}, undefined>, undefined>;
|
|
121
|
+
/** Platform-computed tallies over `operations`. */
|
|
122
|
+
readonly attempted: v.NumberSchema<undefined>;
|
|
123
|
+
readonly succeeded: v.NumberSchema<undefined>;
|
|
124
|
+
/** How many SUCCESSFUL operations exercised authentication. The verdict's deciding count. */
|
|
125
|
+
readonly authenticatedSucceeded: v.NumberSchema<undefined>;
|
|
126
|
+
/**
|
|
127
|
+
* How many reported operations were dropped AT THE CAP, so a reader never takes a truncated
|
|
128
|
+
* list for the whole attempt. Absent means nothing was dropped, which is a different fact from
|
|
129
|
+
* a drop nobody recorded.
|
|
130
|
+
*/
|
|
131
|
+
readonly operationsOmitted: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
|
|
132
|
+
/**
|
|
133
|
+
* How many reported operations were UNREADABLE (no usable name) and so could not be kept.
|
|
134
|
+
*
|
|
135
|
+
* Its own count rather than a second way to spell the one above, because the two send a reader
|
|
136
|
+
* somewhere different: a cap drop says the agent reported more than the report shows, and an
|
|
137
|
+
* unreadable entry says the model's reply was malformed. Rendered as "truncated at the cap",
|
|
138
|
+
* the second one tells an operator their agent did more work than it did.
|
|
139
|
+
*/
|
|
140
|
+
readonly operationsUnreadable: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
|
|
141
|
+
/** The model that produced the report. Absent when the dispatch did not say. */
|
|
142
|
+
readonly model: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
143
|
+
}, undefined>;
|
|
144
|
+
export type EnvironmentProbeReport = v.InferOutput<typeof environmentProbeReportSchema>;
|
|
145
|
+
/**
|
|
146
|
+
* Whether a value is something the coercion below can read anything out of: a plain object.
|
|
147
|
+
*
|
|
148
|
+
* Exported because the DISPATCHER asks the same question before it calls the coercion. A reply
|
|
149
|
+
* that came back as an array, a string or a number has told the platform nothing, and coercing it
|
|
150
|
+
* yields an empty report the verdict then grades `inoperable`: a finding about the service,
|
|
151
|
+
* invented out of a malformed reply. The caller that can still say "the agent produced nothing"
|
|
152
|
+
* has to ask before the shape is flattened, and it has to ask the same question this does.
|
|
153
|
+
*/
|
|
154
|
+
export declare function isEnvironmentProbeReportPayload(value: unknown): value is Record<string, unknown>;
|
|
155
|
+
/**
|
|
156
|
+
* Tally the operations and derive the verdict: the platform's half of the report.
|
|
157
|
+
*
|
|
158
|
+
* `not_attempted` counts towards neither `attempted` nor `succeeded`: an operation the agent
|
|
159
|
+
* declared it could not even try is a FINDING (it carries the failure kind saying why), not a
|
|
160
|
+
* failed call, and counting it as attempted would make "nothing was reachable" read as
|
|
161
|
+
* "everything was tried and failed".
|
|
162
|
+
*
|
|
163
|
+
* It does, however, keep the run OFF `operable`, which is the whole reason the counts and the
|
|
164
|
+
* verdict are computed together here. One authenticated success beside four operations the agent
|
|
165
|
+
* could not work out how to attempt is the exact shape this diagnostic exists to surface, and
|
|
166
|
+
* grading it on the attempted ones alone would render "an agent can operate this service" in
|
|
167
|
+
* green directly above the list of things it could not do.
|
|
168
|
+
*/
|
|
169
|
+
export declare function summarizeEnvironmentProbe(operations: readonly EnvironmentProbeOperation[]): {
|
|
170
|
+
attempted: number;
|
|
171
|
+
succeeded: number;
|
|
172
|
+
authenticatedSucceeded: number;
|
|
173
|
+
verdict: EnvironmentProbeVerdict;
|
|
174
|
+
};
|
|
175
|
+
/** Scrubs one model-authored string on its way into the report. Identity when none is supplied. */
|
|
176
|
+
type Scrub = (value: string) => string;
|
|
177
|
+
/**
|
|
178
|
+
* Coerce an agent's raw structured reply into an {@link EnvironmentProbeReport}.
|
|
179
|
+
*
|
|
180
|
+
* LENIENT by construction, in the same spirit as the analyst draft's schema: a dry run whose
|
|
181
|
+
* report was half-malformed still holds the finding somebody has to act on, and discarding the
|
|
182
|
+
* whole thing would report "the agent produced nothing" for a run that produced plenty. Every
|
|
183
|
+
* field the reply got wrong degrades to a stated absence rather than to a guess, and every cap
|
|
184
|
+
* records what it dropped.
|
|
185
|
+
*
|
|
186
|
+
* `surface` is supplied by the CALLER, not read from the reply: the platform chose which prober to
|
|
187
|
+
* dispatch, so a model claiming otherwise would be reporting about a run that did not happen.
|
|
188
|
+
*
|
|
189
|
+
* `scrub` is likewise the caller's, because contracts is SPA-visible and the redaction rules live
|
|
190
|
+
* in kernel. Supplying it is not optional in spirit: this is the COMPOSE site for a body that is
|
|
191
|
+
* persisted and rendered, the prompt hands the agent the environment's own credential verbatim,
|
|
192
|
+
* and an agent that pastes its `curl -H "Authorization: Bearer ..."` into an operation's `detail`
|
|
193
|
+
* is doing the natural thing. The prompt asks it not to, which is guidance, not a boundary.
|
|
194
|
+
*/
|
|
195
|
+
export declare function coerceEnvironmentProbeReport(raw: unknown, context: {
|
|
196
|
+
surface: EnvironmentProbeSurface;
|
|
197
|
+
model?: string;
|
|
198
|
+
scrub?: Scrub;
|
|
199
|
+
}): EnvironmentProbeReport;
|
|
200
|
+
export {};
|
|
201
|
+
//# sourceMappingURL=environment-probe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment-probe.d.ts","sourceRoot":"","sources":["../src/environment-probe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAC5B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAwBhD,+DAA+D;AAC/D,eAAO,MAAM,6BAA6B,4CAA4B,CAAA;AACtE,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,6BAA6B,CAAC,CAAA;AAEzF;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,SAAS,GAAG,uBAAuB,CAEnF;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,6BAA6B,2LAWxC,CAAA;AACF,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,6BAA6B,CAAC,CAAA;AAEzF,+EAA+E;AAC/E,eAAO,MAAM,0BAA0B,8JAAwC,CAAA;AAE/E,mGAAmG;AACnG,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,uBAAuB,CAI1F;AAED,8CAA8C;AAC9C,eAAO,MAAM,6BAA6B,uEAAuD,CAAA;AACjG,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,6BAA6B,CAAC,CAAA;AAEzF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,6BAA6B,+EAIxC,CAAA;AACF,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,6BAA6B,CAAC,CAAA;AAEzF,sEAAsE;AACtE,eAAO,MAAM,+BAA+B;IAC1C,iGAAiG;;IAEjG,uFAAuF;;IAEvF;;;;OAIG;;;IAGH,gDAAgD;;IAEhD,uEAAuE;;aAEvE,CAAA;AACF,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,+BAA+B,CAAC,CAAA;AAE7F,gFAAgF;AAChF,eAAO,MAAM,6BAA6B;;;aAGxC,CAAA;AACF,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,6BAA6B,CAAC,CAAA;AAEzF,+EAA+E;AAC/E,eAAO,MAAM,4BAA4B;;IAEvC,qDAAqD;;IAErD,oEAAoE;;IAEpE,2FAA2F;;QAhC3F,iGAAiG;;QAEjG,uFAAuF;;QAEvF;;;;WAIG;;;QAGH,gDAAgD;;QAEhD,uEAAuE;;;IAqBvE;;;;OAIG;;IAEH,iEAAiE;;;;;IAEjE,mDAAmD;;;IAGnD,6FAA6F;;IAE7F;;;;OAIG;;IAEH;;;;;;;OAOG;;IAEH,gFAAgF;;aAEhF,CAAA;AACF,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,4BAA4B,CAAC,CAAA;AAoCvF;;;;;;;;GAQG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEhG;AAUD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,SAAS,yBAAyB,EAAE,GAAG;IAC3F,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,sBAAsB,EAAE,MAAM,CAAA;IAC9B,OAAO,EAAE,uBAAuB,CAAA;CACjC,CAaA;AAED,mGAAmG;AACnG,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;AAItC;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE;IAAE,OAAO,EAAE,uBAAuB,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GAC3E,sBAAsB,CA0DxB"}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import * as v from 'valibot';
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// AGENT DRY RUN: what an agent found when it tried to OPERATE a freshly provisioned
|
|
4
|
+
// ephemeral environment.
|
|
5
|
+
//
|
|
6
|
+
// The provisioning self-test answers "does this service's environment stand up". This answers the
|
|
7
|
+
// question after it: given the environment, the credentials and the repository, can an agent work
|
|
8
|
+
// out what to call, authenticate, and get real work done? That gap is where a pipeline silently
|
|
9
|
+
// burns a whole run today: the deploy is green, the tester reaches the environment, and the agent
|
|
10
|
+
// then has no idea which endpoint carries the operation or which credential opens it. A dry run
|
|
11
|
+
// surfaces it in one diagnostic instead of at the end of a build.
|
|
12
|
+
//
|
|
13
|
+
// The vocabularies here are CLOSED and PERSISTED (they land on an `environment_test_runs` row and
|
|
14
|
+
// on the wire), so both sides read them from this module: the backend coerces a model's reply into
|
|
15
|
+
// the report, and the SPA renders each member with its own translated copy. A reader that meets a
|
|
16
|
+
// value it does not know renders the raw value rather than nothing (the SPA's `PROBE_FAILURE_KEYS`
|
|
17
|
+
// is `te`-guarded for exactly that).
|
|
18
|
+
//
|
|
19
|
+
// The division of labour is the platform's standing rule: the MODEL judges each operation it
|
|
20
|
+
// attempted, and the PLATFORM computes everything derived from those judgements (the counts, the
|
|
21
|
+
// verdict). A model is never asked for a verdict it could then grade itself against.
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
/** Which surface a dry run drove: HTTP calls, or a browser. */
|
|
24
|
+
export const environmentProbeSurfaceSchema = v.picklist(['api', 'ui']);
|
|
25
|
+
/**
|
|
26
|
+
* Which surface a service frame's dry run drives, from the frame's own type.
|
|
27
|
+
*
|
|
28
|
+
* A `frontend` frame is a browser job and everything else is a protocol job: there is no third
|
|
29
|
+
* answer to invent, and no frame type where the choice is genuinely ambiguous: `library` and
|
|
30
|
+
* `document` frames have no ephemeral-environment provisioning to test, so a dry run never
|
|
31
|
+
* reaches one. Stated in contracts rather than in the engine because BOTH sides say it to a
|
|
32
|
+
* human: the backend picks the prober, and the SPA's button has to tell the user which one it is
|
|
33
|
+
* about to spend money on.
|
|
34
|
+
*/
|
|
35
|
+
export function environmentProbeSurfaceFor(type) {
|
|
36
|
+
return type === 'frontend' ? 'ui' : 'api';
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Why one attempted operation did not work, or why the agent never got to attempt it.
|
|
40
|
+
*
|
|
41
|
+
* The set is shaped around WHOSE PROBLEM each member is, because that is the whole output of the
|
|
42
|
+
* diagnostic:
|
|
43
|
+
*
|
|
44
|
+
* - `auth_missing` / `access_unclear` / `endpoint_unknown`: the PLATFORM did not tell the agent
|
|
45
|
+
* enough. These are the findings this feature exists to surface: nothing is broken in the
|
|
46
|
+
* service, the run would simply have been spent guessing.
|
|
47
|
+
* - `auth_rejected` / `bad_request`: the agent knew what to send and the service refused it. A
|
|
48
|
+
* credential that is present and rejected is a different fix from one that was never supplied,
|
|
49
|
+
* which is why they are not one member.
|
|
50
|
+
* - `unreachable` / `timeout` / `server_error`: the ENVIRONMENT is at fault, so the dry run has
|
|
51
|
+
* found something the provisioning self-test's `ready` verdict did not.
|
|
52
|
+
* - `tooling_missing`: the container had no HTTP client or browser, so the attempt says nothing
|
|
53
|
+
* about the environment at all. Its own member for that reason: collapsed into `other` it reads
|
|
54
|
+
* as a service that failed.
|
|
55
|
+
* - `other`: anything else, with the agent's own detail carrying the specifics.
|
|
56
|
+
*/
|
|
57
|
+
export const environmentProbeFailureSchema = v.picklist([
|
|
58
|
+
'auth_missing',
|
|
59
|
+
'auth_rejected',
|
|
60
|
+
'access_unclear',
|
|
61
|
+
'endpoint_unknown',
|
|
62
|
+
'unreachable',
|
|
63
|
+
'timeout',
|
|
64
|
+
'server_error',
|
|
65
|
+
'bad_request',
|
|
66
|
+
'tooling_missing',
|
|
67
|
+
'other',
|
|
68
|
+
]);
|
|
69
|
+
/** The failure vocabulary as a list, for a reader that has to enumerate it. */
|
|
70
|
+
export const ENVIRONMENT_PROBE_FAILURES = environmentProbeFailureSchema.options;
|
|
71
|
+
/** Whether a value is a currently-known failure kind (derived from the picklist's own options). */
|
|
72
|
+
export function isEnvironmentProbeFailure(value) {
|
|
73
|
+
return (typeof value === 'string' && ENVIRONMENT_PROBE_FAILURES.includes(value));
|
|
74
|
+
}
|
|
75
|
+
/** How one attempted operation turned out. */
|
|
76
|
+
export const environmentProbeOutcomeSchema = v.picklist(['succeeded', 'failed', 'not_attempted']);
|
|
77
|
+
/**
|
|
78
|
+
* The platform's verdict on a dry run, COMPUTED from the operations the agent reported (see
|
|
79
|
+
* {@link summarizeEnvironmentProbe}) rather than read off the reply.
|
|
80
|
+
*
|
|
81
|
+
* - `operable`: every operation the agent listed was attempted, every one of them worked, and at
|
|
82
|
+
* least one exercised authentication.
|
|
83
|
+
* - `partially_operable`: something worked, but not everything, or nothing that worked was
|
|
84
|
+
* behind auth, or the agent listed operations it could not even attempt. A public healthcheck
|
|
85
|
+
* answering is not evidence that an agent can operate the service, which is why it cannot earn
|
|
86
|
+
* `operable` on its own, and neither is a run whose one success sits beside four things the
|
|
87
|
+
* agent could not work out how to try.
|
|
88
|
+
* - `inoperable`: nothing the agent attempted worked, or it never got to attempt anything.
|
|
89
|
+
*/
|
|
90
|
+
export const environmentProbeVerdictSchema = v.picklist([
|
|
91
|
+
'operable',
|
|
92
|
+
'partially_operable',
|
|
93
|
+
'inoperable',
|
|
94
|
+
]);
|
|
95
|
+
/** One operation the agent attempted against the live environment. */
|
|
96
|
+
export const environmentProbeOperationSchema = v.object({
|
|
97
|
+
/** What it was, in the agent's own words ("list projects", "sign in and open the dashboard"). */
|
|
98
|
+
name: v.string(),
|
|
99
|
+
/** How it was performed: `GET /api/v1/projects`, or the UI path it clicked through. */
|
|
100
|
+
target: v.optional(v.string()),
|
|
101
|
+
/**
|
|
102
|
+
* Whether this operation actually exercised authentication. The one field the verdict turns on:
|
|
103
|
+
* a dry run whose only successes are anonymous has not shown that an agent can operate the
|
|
104
|
+
* service, so it never reaches `operable`.
|
|
105
|
+
*/
|
|
106
|
+
authenticated: v.boolean(),
|
|
107
|
+
outcome: environmentProbeOutcomeSchema,
|
|
108
|
+
/** Why it did not work. Absent on a success. */
|
|
109
|
+
failure: v.optional(environmentProbeFailureSchema),
|
|
110
|
+
/** The agent's evidence: the status code, the message, what it saw. */
|
|
111
|
+
detail: v.optional(v.string()),
|
|
112
|
+
});
|
|
113
|
+
/** Something that stopped the dry run as a whole, rather than one operation. */
|
|
114
|
+
export const environmentProbeBlockerSchema = v.object({
|
|
115
|
+
kind: environmentProbeFailureSchema,
|
|
116
|
+
detail: v.string(),
|
|
117
|
+
});
|
|
118
|
+
/** A dry run's report, as persisted on the run row and rendered by the SPA. */
|
|
119
|
+
export const environmentProbeReportSchema = v.object({
|
|
120
|
+
surface: environmentProbeSurfaceSchema,
|
|
121
|
+
/** Platform-computed; never taken from the reply. */
|
|
122
|
+
verdict: environmentProbeVerdictSchema,
|
|
123
|
+
/** The agent's own account of what it did and what it concluded. */
|
|
124
|
+
summary: v.string(),
|
|
125
|
+
/** Every operation it attempted (or explicitly did not), in the order it reported them. */
|
|
126
|
+
operations: v.array(environmentProbeOperationSchema),
|
|
127
|
+
/**
|
|
128
|
+
* What the PLATFORM failed to tell it: a credential it needed and had no reference for, an
|
|
129
|
+
* endpoint it could not discover, a base path it had to guess. The primary product of the
|
|
130
|
+
* diagnostic, since each entry is a thing to fix before a real run spends a step on it.
|
|
131
|
+
*/
|
|
132
|
+
missingContext: v.array(v.string()),
|
|
133
|
+
/** Whole-run blockers, distinct from one operation's failure. */
|
|
134
|
+
blockers: v.array(environmentProbeBlockerSchema),
|
|
135
|
+
/** Platform-computed tallies over `operations`. */
|
|
136
|
+
attempted: v.number(),
|
|
137
|
+
succeeded: v.number(),
|
|
138
|
+
/** How many SUCCESSFUL operations exercised authentication. The verdict's deciding count. */
|
|
139
|
+
authenticatedSucceeded: v.number(),
|
|
140
|
+
/**
|
|
141
|
+
* How many reported operations were dropped AT THE CAP, so a reader never takes a truncated
|
|
142
|
+
* list for the whole attempt. Absent means nothing was dropped, which is a different fact from
|
|
143
|
+
* a drop nobody recorded.
|
|
144
|
+
*/
|
|
145
|
+
operationsOmitted: v.optional(v.number()),
|
|
146
|
+
/**
|
|
147
|
+
* How many reported operations were UNREADABLE (no usable name) and so could not be kept.
|
|
148
|
+
*
|
|
149
|
+
* Its own count rather than a second way to spell the one above, because the two send a reader
|
|
150
|
+
* somewhere different: a cap drop says the agent reported more than the report shows, and an
|
|
151
|
+
* unreadable entry says the model's reply was malformed. Rendered as "truncated at the cap",
|
|
152
|
+
* the second one tells an operator their agent did more work than it did.
|
|
153
|
+
*/
|
|
154
|
+
operationsUnreadable: v.optional(v.number()),
|
|
155
|
+
/** The model that produced the report. Absent when the dispatch did not say. */
|
|
156
|
+
model: v.optional(v.string()),
|
|
157
|
+
});
|
|
158
|
+
/** How much of a model's reply is kept. Every cap here records what it dropped. */
|
|
159
|
+
const CAPS = {
|
|
160
|
+
operations: 12,
|
|
161
|
+
name: 160,
|
|
162
|
+
target: 200,
|
|
163
|
+
detail: 800,
|
|
164
|
+
summary: 2000,
|
|
165
|
+
missingContext: 10,
|
|
166
|
+
missingContextEntry: 300,
|
|
167
|
+
blockers: 6,
|
|
168
|
+
};
|
|
169
|
+
/**
|
|
170
|
+
* One model-authored string, scrubbed and then capped, in that order.
|
|
171
|
+
*
|
|
172
|
+
* The order is the platform's standing rule for untrusted text: a scrub applied AFTER a cap can
|
|
173
|
+
* leave the head of a credential in the kept prefix, and a scrub that runs on the whole value
|
|
174
|
+
* keeps the prose and the JSON consistent about what was dropped.
|
|
175
|
+
*/
|
|
176
|
+
function text(value, max, scrub) {
|
|
177
|
+
if (typeof value !== 'string')
|
|
178
|
+
return undefined;
|
|
179
|
+
const trimmed = scrub(value).trim();
|
|
180
|
+
if (!trimmed)
|
|
181
|
+
return undefined;
|
|
182
|
+
return trimmed.length > max ? `${trimmed.slice(0, max)} [truncated]` : trimmed;
|
|
183
|
+
}
|
|
184
|
+
function outcomeOf(raw) {
|
|
185
|
+
if (raw === 'succeeded' || raw === 'failed' || raw === 'not_attempted')
|
|
186
|
+
return raw;
|
|
187
|
+
// A reply that named no outcome for an operation it listed has told us nothing about it, and
|
|
188
|
+
// `failed` would attribute a fault the agent never claimed. `not_attempted` is the honest
|
|
189
|
+
// reading and keeps the entry out of both tallies.
|
|
190
|
+
return 'not_attempted';
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Whether a value is something the coercion below can read anything out of: a plain object.
|
|
194
|
+
*
|
|
195
|
+
* Exported because the DISPATCHER asks the same question before it calls the coercion. A reply
|
|
196
|
+
* that came back as an array, a string or a number has told the platform nothing, and coercing it
|
|
197
|
+
* yields an empty report the verdict then grades `inoperable`: a finding about the service,
|
|
198
|
+
* invented out of a malformed reply. The caller that can still say "the agent produced nothing"
|
|
199
|
+
* has to ask before the shape is flattened, and it has to ask the same question this does.
|
|
200
|
+
*/
|
|
201
|
+
export function isEnvironmentProbeReportPayload(value) {
|
|
202
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
203
|
+
}
|
|
204
|
+
function record(value) {
|
|
205
|
+
return isEnvironmentProbeReportPayload(value) ? value : {};
|
|
206
|
+
}
|
|
207
|
+
function array(value) {
|
|
208
|
+
return Array.isArray(value) ? value : [];
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Tally the operations and derive the verdict: the platform's half of the report.
|
|
212
|
+
*
|
|
213
|
+
* `not_attempted` counts towards neither `attempted` nor `succeeded`: an operation the agent
|
|
214
|
+
* declared it could not even try is a FINDING (it carries the failure kind saying why), not a
|
|
215
|
+
* failed call, and counting it as attempted would make "nothing was reachable" read as
|
|
216
|
+
* "everything was tried and failed".
|
|
217
|
+
*
|
|
218
|
+
* It does, however, keep the run OFF `operable`, which is the whole reason the counts and the
|
|
219
|
+
* verdict are computed together here. One authenticated success beside four operations the agent
|
|
220
|
+
* could not work out how to attempt is the exact shape this diagnostic exists to surface, and
|
|
221
|
+
* grading it on the attempted ones alone would render "an agent can operate this service" in
|
|
222
|
+
* green directly above the list of things it could not do.
|
|
223
|
+
*/
|
|
224
|
+
export function summarizeEnvironmentProbe(operations) {
|
|
225
|
+
const attempted = operations.filter((op) => op.outcome !== 'not_attempted').length;
|
|
226
|
+
const succeeded = operations.filter((op) => op.outcome === 'succeeded').length;
|
|
227
|
+
const authenticatedSucceeded = operations.filter((op) => op.outcome === 'succeeded' && op.authenticated).length;
|
|
228
|
+
const verdict = succeeded === 0
|
|
229
|
+
? 'inoperable'
|
|
230
|
+
: succeeded === attempted && attempted === operations.length && authenticatedSucceeded > 0
|
|
231
|
+
? 'operable'
|
|
232
|
+
: 'partially_operable';
|
|
233
|
+
return { attempted, succeeded, authenticatedSucceeded, verdict };
|
|
234
|
+
}
|
|
235
|
+
const noScrub = (value) => value;
|
|
236
|
+
/**
|
|
237
|
+
* Coerce an agent's raw structured reply into an {@link EnvironmentProbeReport}.
|
|
238
|
+
*
|
|
239
|
+
* LENIENT by construction, in the same spirit as the analyst draft's schema: a dry run whose
|
|
240
|
+
* report was half-malformed still holds the finding somebody has to act on, and discarding the
|
|
241
|
+
* whole thing would report "the agent produced nothing" for a run that produced plenty. Every
|
|
242
|
+
* field the reply got wrong degrades to a stated absence rather than to a guess, and every cap
|
|
243
|
+
* records what it dropped.
|
|
244
|
+
*
|
|
245
|
+
* `surface` is supplied by the CALLER, not read from the reply: the platform chose which prober to
|
|
246
|
+
* dispatch, so a model claiming otherwise would be reporting about a run that did not happen.
|
|
247
|
+
*
|
|
248
|
+
* `scrub` is likewise the caller's, because contracts is SPA-visible and the redaction rules live
|
|
249
|
+
* in kernel. Supplying it is not optional in spirit: this is the COMPOSE site for a body that is
|
|
250
|
+
* persisted and rendered, the prompt hands the agent the environment's own credential verbatim,
|
|
251
|
+
* and an agent that pastes its `curl -H "Authorization: Bearer ..."` into an operation's `detail`
|
|
252
|
+
* is doing the natural thing. The prompt asks it not to, which is guidance, not a boundary.
|
|
253
|
+
*/
|
|
254
|
+
export function coerceEnvironmentProbeReport(raw, context) {
|
|
255
|
+
const scrub = context.scrub ?? noScrub;
|
|
256
|
+
const root = record(raw);
|
|
257
|
+
const rawOperations = array(root.operations);
|
|
258
|
+
const operations = [];
|
|
259
|
+
for (const entry of rawOperations.slice(0, CAPS.operations)) {
|
|
260
|
+
const op = record(entry);
|
|
261
|
+
const name = text(op.name, CAPS.name, scrub);
|
|
262
|
+
if (!name)
|
|
263
|
+
continue;
|
|
264
|
+
const target = text(op.target, CAPS.target, scrub);
|
|
265
|
+
const detail = text(op.detail, CAPS.detail, scrub);
|
|
266
|
+
const outcome = outcomeOf(op.outcome);
|
|
267
|
+
const failure = isEnvironmentProbeFailure(op.failure) ? op.failure : undefined;
|
|
268
|
+
operations.push({
|
|
269
|
+
name,
|
|
270
|
+
...(target ? { target } : {}),
|
|
271
|
+
authenticated: op.authenticated === true,
|
|
272
|
+
outcome,
|
|
273
|
+
// A non-success with no recognised kind still has to say SOMETHING about whose problem it
|
|
274
|
+
// was, and `other` is the member that says "the detail carries it".
|
|
275
|
+
...(outcome === 'succeeded' ? {} : { failure: failure ?? 'other' }),
|
|
276
|
+
...(detail ? { detail } : {}),
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
const blockers = [];
|
|
280
|
+
for (const entry of array(root.blockers).slice(0, CAPS.blockers)) {
|
|
281
|
+
const blocker = record(entry);
|
|
282
|
+
const detail = text(blocker.detail, CAPS.detail, scrub);
|
|
283
|
+
if (!detail)
|
|
284
|
+
continue;
|
|
285
|
+
blockers.push({
|
|
286
|
+
kind: isEnvironmentProbeFailure(blocker.kind) ? blocker.kind : 'other',
|
|
287
|
+
detail,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
const missingContext = [];
|
|
291
|
+
for (const entry of array(root.missingContext).slice(0, CAPS.missingContext)) {
|
|
292
|
+
const line = text(entry, CAPS.missingContextEntry, scrub);
|
|
293
|
+
if (line)
|
|
294
|
+
missingContext.push(line);
|
|
295
|
+
}
|
|
296
|
+
// The two ways an operation the model reported fails to reach the report, counted apart: the
|
|
297
|
+
// ones past the cap were never looked at, the ones inside it were looked at and had nothing to
|
|
298
|
+
// identify them by. See the schema's note on why one number for both misreports the first.
|
|
299
|
+
const overCap = Math.max(0, rawOperations.length - CAPS.operations);
|
|
300
|
+
const unreadable = Math.max(0, rawOperations.length - overCap - operations.length);
|
|
301
|
+
return {
|
|
302
|
+
surface: context.surface,
|
|
303
|
+
...summarizeEnvironmentProbe(operations),
|
|
304
|
+
// An empty summary stays empty rather than being filled with a sentence the model did not
|
|
305
|
+
// write: the SPA renders the operations and the verdict either way, and inventing prose here
|
|
306
|
+
// would make a model that returned nothing look like one that reported.
|
|
307
|
+
summary: text(root.summary, CAPS.summary, scrub) ?? '',
|
|
308
|
+
operations,
|
|
309
|
+
missingContext,
|
|
310
|
+
blockers,
|
|
311
|
+
...(overCap > 0 ? { operationsOmitted: overCap } : {}),
|
|
312
|
+
...(unreadable > 0 ? { operationsUnreadable: unreadable } : {}),
|
|
313
|
+
...(context.model ? { model: context.model } : {}),
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
//# sourceMappingURL=environment-probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment-probe.js","sourceRoot":"","sources":["../src/environment-probe.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
import * as v from 'valibot';
|
|
2
|
-
/**
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* What a run EXERCISES. Both modes share the whole lifecycle, the cleanup contract and the run
|
|
4
|
+
* store; the agent mode adds one stage in the middle.
|
|
5
|
+
*
|
|
6
|
+
* - `provision`: the provisioning config alone. Does the environment stand up and come down?
|
|
7
|
+
* - `agent-probe`: that, plus the question after it. Handed this environment, its credentials
|
|
8
|
+
* and the repository, can an AGENT work out how to operate the service? See
|
|
9
|
+
* `environment-probe.ts` for what it reports.
|
|
10
|
+
*/
|
|
11
|
+
export declare const environmentTestModeSchema: v.PicklistSchema<["provision", "agent-probe"], undefined>;
|
|
12
|
+
export type EnvironmentTestMode = v.InferOutput<typeof environmentTestModeSchema>;
|
|
13
|
+
/**
|
|
14
|
+
* The ordered lifecycle stages of an environment-test run.
|
|
15
|
+
*
|
|
16
|
+
* `probing` is reached only in `agent-probe` mode, between a `ready` environment and its
|
|
17
|
+
* teardown. Every mode still passes through `tearing_down` and `deleting_branch`, because the
|
|
18
|
+
* always-cleans-up contract is the whole reason a developer is willing to press either button.
|
|
19
|
+
*/
|
|
20
|
+
export declare const environmentTestStageSchema: v.PicklistSchema<["creating_branch", "provisioning", "probing", "tearing_down", "deleting_branch", "done"], undefined>;
|
|
4
21
|
export type EnvironmentTestStage = v.InferOutput<typeof environmentTestStageSchema>;
|
|
5
22
|
/** Terminal-ness of an environment-test run. */
|
|
6
23
|
export declare const environmentTestStatusSchema: v.PicklistSchema<["running", "succeeded", "failed"], undefined>;
|
|
@@ -11,9 +28,19 @@ export declare const environmentTestRunSchema: v.ObjectSchema<{
|
|
|
11
28
|
readonly workspaceId: v.StringSchema<undefined>;
|
|
12
29
|
/** The service frame (board block) whose provisioning config is being tested. */
|
|
13
30
|
readonly blockId: v.StringSchema<undefined>;
|
|
31
|
+
readonly mode: v.PicklistSchema<["provision", "agent-probe"], undefined>;
|
|
32
|
+
/**
|
|
33
|
+
* Terminal-ness of the LIFECYCLE, never the finding.
|
|
34
|
+
*
|
|
35
|
+
* `succeeded` means the run did everything it set out to do and left nothing behind, including
|
|
36
|
+
* an agent dry run whose verdict is `inoperable`, which is a completed diagnostic reporting bad
|
|
37
|
+
* news. Folding the verdict in here would make the one interesting outcome indistinguishable
|
|
38
|
+
* from a broken diagnostic, and would leave a real teardown failure with nothing to say. What
|
|
39
|
+
* the agent FOUND is {@link environmentTestRunSchema.entries.probe}'s `verdict`.
|
|
40
|
+
*/
|
|
14
41
|
readonly status: v.PicklistSchema<["running", "succeeded", "failed"], undefined>;
|
|
15
42
|
/** The stage currently in flight (or `done` when finished successfully). */
|
|
16
|
-
readonly stage: v.PicklistSchema<["creating_branch", "provisioning", "tearing_down", "deleting_branch", "done"], undefined>;
|
|
43
|
+
readonly stage: v.PicklistSchema<["creating_branch", "provisioning", "probing", "tearing_down", "deleting_branch", "done"], undefined>;
|
|
17
44
|
/** The temporary branch the run created; null until it is created. */
|
|
18
45
|
readonly branch: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
19
46
|
/** The provisioned environment's URL, when the provider exposed one. */
|
|
@@ -21,7 +48,57 @@ export declare const environmentTestRunSchema: v.ObjectSchema<{
|
|
|
21
48
|
/** One-line failure reason when `status` is `failed`; null otherwise. */
|
|
22
49
|
readonly error: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
23
50
|
/** The stage the run was at when it failed; null unless `status` is `failed`. */
|
|
24
|
-
readonly failedStage: v.NullableSchema<v.PicklistSchema<["creating_branch", "provisioning", "tearing_down", "deleting_branch", "done"], undefined>, undefined>;
|
|
51
|
+
readonly failedStage: v.NullableSchema<v.PicklistSchema<["creating_branch", "provisioning", "probing", "tearing_down", "deleting_branch", "done"], undefined>, undefined>;
|
|
52
|
+
/**
|
|
53
|
+
* What the dry-run agent reported, once the `probing` stage settled. Null in `provision` mode,
|
|
54
|
+
* and in `agent-probe` mode until the probe returns, including on a run that FAILED before or
|
|
55
|
+
* during the probe, where the run's `error` says why there is no report. The two absences are
|
|
56
|
+
* told apart by `mode` and `failedStage`, never by this field alone.
|
|
57
|
+
*/
|
|
58
|
+
readonly probe: v.NullableSchema<v.ObjectSchema<{
|
|
59
|
+
readonly surface: v.PicklistSchema<["api", "ui"], undefined>;
|
|
60
|
+
readonly verdict: v.PicklistSchema<["operable", "partially_operable", "inoperable"], undefined>;
|
|
61
|
+
readonly summary: v.StringSchema<undefined>;
|
|
62
|
+
readonly operations: v.ArraySchema<v.ObjectSchema<{
|
|
63
|
+
readonly name: v.StringSchema<undefined>;
|
|
64
|
+
readonly target: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
65
|
+
readonly authenticated: v.BooleanSchema<undefined>;
|
|
66
|
+
readonly outcome: v.PicklistSchema<["succeeded", "failed", "not_attempted"], undefined>;
|
|
67
|
+
readonly failure: v.OptionalSchema<v.PicklistSchema<["auth_missing", "auth_rejected", "access_unclear", "endpoint_unknown", "unreachable", "timeout", "server_error", "bad_request", "tooling_missing", "other"], undefined>, undefined>;
|
|
68
|
+
readonly detail: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
69
|
+
}, undefined>, undefined>;
|
|
70
|
+
readonly missingContext: v.ArraySchema<v.StringSchema<undefined>, undefined>;
|
|
71
|
+
readonly blockers: v.ArraySchema<v.ObjectSchema<{
|
|
72
|
+
readonly kind: v.PicklistSchema<["auth_missing", "auth_rejected", "access_unclear", "endpoint_unknown", "unreachable", "timeout", "server_error", "bad_request", "tooling_missing", "other"], undefined>;
|
|
73
|
+
readonly detail: v.StringSchema<undefined>;
|
|
74
|
+
}, undefined>, undefined>;
|
|
75
|
+
readonly attempted: v.NumberSchema<undefined>;
|
|
76
|
+
readonly succeeded: v.NumberSchema<undefined>;
|
|
77
|
+
readonly authenticatedSucceeded: v.NumberSchema<undefined>;
|
|
78
|
+
readonly operationsOmitted: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
|
|
79
|
+
readonly operationsUnreadable: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
|
|
80
|
+
readonly model: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
81
|
+
}, undefined>, undefined>;
|
|
82
|
+
/**
|
|
83
|
+
* The dry-run agent's live todo counts while the `probing` stage is in flight, lifted from the
|
|
84
|
+
* container's own progress exactly as a pipeline step's are.
|
|
85
|
+
*
|
|
86
|
+
* Carried on the run rather than left in the container because `probing` is the LONGEST stage
|
|
87
|
+
* this flow has (a model reading a repository and driving a service, minutes of it) and every
|
|
88
|
+
* other stage moves the SPA within seconds. With nothing written, no `envTestChanged` event
|
|
89
|
+
* fires for the whole probe and the card sits frozen on "probing with an agent", which reads
|
|
90
|
+
* exactly like a wedged run. Null in `provision` mode, before the prober reports any progress,
|
|
91
|
+
* and once the report has landed.
|
|
92
|
+
*/
|
|
93
|
+
readonly probeProgress: v.NullableSchema<v.ObjectSchema<{
|
|
94
|
+
readonly completed: v.NumberSchema<undefined>;
|
|
95
|
+
readonly inProgress: v.NumberSchema<undefined>;
|
|
96
|
+
readonly total: v.NumberSchema<undefined>;
|
|
97
|
+
readonly items: v.OptionalSchema<v.ArraySchema<v.ObjectSchema<{
|
|
98
|
+
readonly label: v.StringSchema<undefined>;
|
|
99
|
+
readonly status: v.PicklistSchema<["pending", "in_progress", "completed"], undefined>;
|
|
100
|
+
}, undefined>, undefined>, undefined>;
|
|
101
|
+
}, undefined>, undefined>;
|
|
25
102
|
readonly createdAt: v.NumberSchema<undefined>;
|
|
26
103
|
readonly updatedAt: v.NumberSchema<undefined>;
|
|
27
104
|
}, undefined>;
|