@opensoftware/contracts 0.0.0-stage → 1.4.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/LICENSE +202 -0
- package/README.md +112 -2
- package/dist/breaking.d.ts +27 -0
- package/dist/breaking.js +157 -0
- package/dist/breaking.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +69 -0
- package/dist/cli.js.map +1 -0
- package/dist/formats.d.ts +10 -0
- package/dist/formats.js +21 -0
- package/dist/formats.js.map +1 -0
- package/dist/generated/types.d.ts +636 -0
- package/dist/generated/types.js +2 -0
- package/dist/generated/types.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +75 -0
- package/dist/index.js.map +1 -0
- package/dist/roundtrip.d.ts +3 -0
- package/dist/roundtrip.js +90 -0
- package/dist/roundtrip.js.map +1 -0
- package/dist/run-record.d.ts +18 -0
- package/dist/run-record.js +27 -0
- package/dist/run-record.js.map +1 -0
- package/events/asyncapi.yaml +186 -0
- package/events/gate-decided/gate-decided.schema.json +122 -0
- package/events/gate-requested/gate-requested.schema.json +135 -0
- package/events/pr-closed/pr-closed.schema.json +80 -0
- package/events/pr-opened/pr-opened.schema.json +138 -0
- package/events/pr-reviewed/pr-reviewed.schema.json +111 -0
- package/events/run-completed/run-completed.schema.json +163 -0
- package/events/score-recorded/score-recorded.schema.json +163 -0
- package/events/work-received/work-received.schema.json +182 -0
- package/evidence-bundle/evidence-bundle.schema.json +180 -0
- package/factory-definition/factory-definition.schema.json +371 -0
- package/openapi/openapi.json +1715 -0
- package/package.json +50 -3
- package/run-record/run-record.schema.json +179 -0
- package/score/score.schema.json +110 -0
- package/typespec/common.tsp +62 -0
- package/typespec/events.tsp +168 -0
- package/typespec/evidence-bundle.tsp +99 -0
- package/typespec/factory-definition.tsp +193 -0
- package/typespec/main.tsp +10 -0
- package/typespec/run-record.tsp +88 -0
- package/typespec/score.tsp +68 -0
- package/typespec/work-item.tsp +65 -0
- package/work-item/work-item.schema.json +129 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import "@typespec/json-schema";
|
|
2
|
+
import "./common.tsp";
|
|
3
|
+
|
|
4
|
+
using TypeSpec.JsonSchema;
|
|
5
|
+
|
|
6
|
+
namespace Osf.Contracts;
|
|
7
|
+
|
|
8
|
+
// Every object in the factory definition is closed: people write it by hand, so an unknown field is a typo
|
|
9
|
+
// and is rejected with its path.
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* `factory.yaml`: the desired state of one factory (P3). Paths are relative to the file.
|
|
13
|
+
* Realizes SCN-FACTORY-CODE-001: agents and prompts; MCP servers, skills and secrets per agent;
|
|
14
|
+
* repositories; model and runtime; automations; scorers and benchmarks.
|
|
15
|
+
*/
|
|
16
|
+
@extension("additionalProperties", Json<false>)
|
|
17
|
+
model FactoryDefinition {
|
|
18
|
+
/** Editors may point at this schema. Ignored by every tool. */
|
|
19
|
+
`$schema`?: string;
|
|
20
|
+
|
|
21
|
+
/** Version of this format. A new major version of the format gets a new value. */
|
|
22
|
+
version: FormatVersion;
|
|
23
|
+
|
|
24
|
+
/** Name of the factory. */
|
|
25
|
+
factory: Name;
|
|
26
|
+
|
|
27
|
+
description?: string;
|
|
28
|
+
|
|
29
|
+
/** Adapters per port with a version range, pinned in `factory.lock` by `factory plan`. */
|
|
30
|
+
providers?: Providers;
|
|
31
|
+
|
|
32
|
+
/** Agents by name. */
|
|
33
|
+
@minProperties(1)
|
|
34
|
+
agents: Record<Agent>;
|
|
35
|
+
|
|
36
|
+
/** Stages in order. Each stage runs one agent; the agent defaults to the stage name. */
|
|
37
|
+
workflow?: Stage[];
|
|
38
|
+
|
|
39
|
+
/** What starts work without a person asking. */
|
|
40
|
+
automations?: Automation[];
|
|
41
|
+
|
|
42
|
+
/** Scorers by name. */
|
|
43
|
+
scorers?: Record<Scorer>;
|
|
44
|
+
|
|
45
|
+
/** Benchmark suites by name. */
|
|
46
|
+
benchmarks?: Record<Benchmark>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** A semver range of an adapter, for example "^2". */
|
|
50
|
+
@minLength(1)
|
|
51
|
+
scalar VersionRange extends string;
|
|
52
|
+
|
|
53
|
+
/** The adapters a factory uses, per port (docs/architecture/internals.md). Keys are adapter names. */
|
|
54
|
+
@extension("additionalProperties", Json<false>)
|
|
55
|
+
model Providers {
|
|
56
|
+
harness?: Record<VersionRange>;
|
|
57
|
+
sandbox?: Record<VersionRange>;
|
|
58
|
+
channel?: Record<VersionRange>;
|
|
59
|
+
tracker?: Record<VersionRange>;
|
|
60
|
+
forge?: Record<VersionRange>;
|
|
61
|
+
inference?: Record<VersionRange>;
|
|
62
|
+
secrets?: Record<VersionRange>;
|
|
63
|
+
telemetry?: Record<VersionRange>;
|
|
64
|
+
policy?: Record<VersionRange>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** A reference to a secret, resolved at the egress proxy, never in the sandbox: "ref:<provider>/<name>". */
|
|
68
|
+
@pattern("^ref:[a-z][a-z0-9-]*/[A-Za-z0-9_./-]+$")
|
|
69
|
+
scalar SecretReference extends string;
|
|
70
|
+
|
|
71
|
+
@extension("additionalProperties", Json<false>)
|
|
72
|
+
model Agent {
|
|
73
|
+
description?: string;
|
|
74
|
+
|
|
75
|
+
/** Harness adapter, declared under `providers.harness`, for example "claude-code". */
|
|
76
|
+
harness: Name;
|
|
77
|
+
|
|
78
|
+
/** A model ID or a routing alias of the inference gateway, for example "routes/triage". */
|
|
79
|
+
@minLength(1)
|
|
80
|
+
`model`: string;
|
|
81
|
+
|
|
82
|
+
/** Path to the Markdown prompt. */
|
|
83
|
+
@minLength(1)
|
|
84
|
+
prompt: string;
|
|
85
|
+
|
|
86
|
+
/** Repositories the agent operates on, as clone URLs or "<owner>/<name>". */
|
|
87
|
+
repositories?: string[];
|
|
88
|
+
|
|
89
|
+
/** MCP servers: `registry:<name>@<version>` or `<name>@<version>`. */
|
|
90
|
+
mcp?: string[];
|
|
91
|
+
|
|
92
|
+
/** Paths to skill folders. */
|
|
93
|
+
skills?: string[];
|
|
94
|
+
|
|
95
|
+
secrets?: SecretReference[];
|
|
96
|
+
|
|
97
|
+
/** Runtime of the agent. */
|
|
98
|
+
sandbox?: Sandbox;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@extension("additionalProperties", Json<false>)
|
|
102
|
+
model Sandbox {
|
|
103
|
+
/** Sandbox template name, for example "node-24". */
|
|
104
|
+
@minLength(1)
|
|
105
|
+
template: string;
|
|
106
|
+
|
|
107
|
+
/** CPU cores. The smallest unit is 0.001 (one millicore). */
|
|
108
|
+
@minValue(0.001)
|
|
109
|
+
cpu?: float64;
|
|
110
|
+
|
|
111
|
+
/** Kubernetes quantity, for example "8Gi". */
|
|
112
|
+
@pattern("^[0-9]+(Ki|Mi|Gi|Ti)?$")
|
|
113
|
+
memory?: string;
|
|
114
|
+
|
|
115
|
+
/** Hostnames the sandbox may reach. Everything else is blocked. */
|
|
116
|
+
egress?: string[];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@extension("additionalProperties", Json<false>)
|
|
120
|
+
model Stage {
|
|
121
|
+
stage: Name;
|
|
122
|
+
|
|
123
|
+
/** Agent that runs the stage. Defaults to the agent with the stage's name. */
|
|
124
|
+
agent?: Name;
|
|
125
|
+
|
|
126
|
+
/** The stage waits for a decision before it starts. */
|
|
127
|
+
gate?: Gate;
|
|
128
|
+
|
|
129
|
+
/** Whether the stage must produce an evidence bundle. */
|
|
130
|
+
evidence?: EvidenceRequirement;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
enum EvidenceRequirement {
|
|
134
|
+
required,
|
|
135
|
+
optional,
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@extension("additionalProperties", Json<false>)
|
|
139
|
+
model Gate {
|
|
140
|
+
/** Path to the Rego policy that decides or picks the approvers. */
|
|
141
|
+
@minLength(1)
|
|
142
|
+
policy: string;
|
|
143
|
+
|
|
144
|
+
/** Approver groups. The policy may narrow them. */
|
|
145
|
+
approvers?: Name[];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Starts the workflow, or the named job, on an event (`on`) or a schedule (`cron`). Exactly one of the two; a
|
|
150
|
+
* schedule names its job. One model rather than a union, so every generated client reads it without guessing.
|
|
151
|
+
*/
|
|
152
|
+
@extension("additionalProperties", Json<false>)
|
|
153
|
+
@extension(
|
|
154
|
+
"oneOf",
|
|
155
|
+
#[
|
|
156
|
+
#{ required: #["on"], not: #{ required: #["cron"] } },
|
|
157
|
+
#{ required: #["cron", "run"], not: #{ required: #["on"] } }
|
|
158
|
+
]
|
|
159
|
+
)
|
|
160
|
+
model Automation {
|
|
161
|
+
/** Event filter, for example "github.issue.labeled(auto-implement)" or "sentry.fatal". */
|
|
162
|
+
@minLength(1)
|
|
163
|
+
on?: string;
|
|
164
|
+
|
|
165
|
+
/** Five-field cron expression in UTC. */
|
|
166
|
+
@minLength(1)
|
|
167
|
+
cron?: string;
|
|
168
|
+
|
|
169
|
+
/** Job to run. Required with `cron`; without it an event starts the workflow. */
|
|
170
|
+
run?: Name;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
@extension("additionalProperties", Json<false>)
|
|
174
|
+
model Scorer {
|
|
175
|
+
/** Path to the Markdown judge prompt. */
|
|
176
|
+
@minLength(1)
|
|
177
|
+
judge: string;
|
|
178
|
+
|
|
179
|
+
/** Share of runs to score, 0 to 1. Default 1. */
|
|
180
|
+
@minValue(0)
|
|
181
|
+
@maxValue(1)
|
|
182
|
+
sample?: float64;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
@extension("additionalProperties", Json<false>)
|
|
186
|
+
model Benchmark {
|
|
187
|
+
/** Path to the Harbor task suite. */
|
|
188
|
+
@minLength(1)
|
|
189
|
+
suite: string;
|
|
190
|
+
|
|
191
|
+
/** Five-field cron expression in UTC. Without it the suite runs on request. */
|
|
192
|
+
schedule?: string;
|
|
193
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// The single source of the shared formats (ADR 0006). `pnpm build` emits one JSON Schema per format into
|
|
2
|
+
// contracts/<format>/<format>.schema.json and an OpenAPI 3.1 document for the generated clients.
|
|
3
|
+
import "@typespec/json-schema";
|
|
4
|
+
import "./common.tsp";
|
|
5
|
+
import "./factory-definition.tsp";
|
|
6
|
+
import "./work-item.tsp";
|
|
7
|
+
import "./run-record.tsp";
|
|
8
|
+
import "./evidence-bundle.tsp";
|
|
9
|
+
import "./score.tsp";
|
|
10
|
+
import "./events.tsp";
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import "@typespec/json-schema";
|
|
2
|
+
import "./common.tsp";
|
|
3
|
+
|
|
4
|
+
using TypeSpec.JsonSchema;
|
|
5
|
+
|
|
6
|
+
namespace Osf.Contracts;
|
|
7
|
+
|
|
8
|
+
// Attribute names follow the OpenTelemetry semantic conventions v1.44.0 (resource) and the GenAI semantic
|
|
9
|
+
// conventions at open-telemetry/semantic-conventions-genai@547934a (schema gen-ai-dev/1.42.0-dev, status
|
|
10
|
+
// Development). Factory attributes use the `osf.` namespace so they never collide with future OTel names.
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The attributes of one run: the OTel resource of every span of the run, and the attributes of its root
|
|
14
|
+
* `invoke_agent` span. The run record travels as OTLP; this schema fixes the attribute names and types.
|
|
15
|
+
* Realizes SCN-FACTORY-DATA-001 (record shape) and SCN-FACTORY-RUNTIME-008 (attribution).
|
|
16
|
+
*/
|
|
17
|
+
model RunRecord {
|
|
18
|
+
resource: RunResource;
|
|
19
|
+
span: RunSpan;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Set at launch through OTEL_RESOURCE_ATTRIBUTES, so it is on every span of the run. */
|
|
23
|
+
model RunResource {
|
|
24
|
+
`service.name`: string;
|
|
25
|
+
`osf.run.id`: Identifier;
|
|
26
|
+
`osf.work_item.id`: Identifier;
|
|
27
|
+
|
|
28
|
+
/** Commit of the factory definition the run used. */
|
|
29
|
+
`osf.factory.version`: CommitHash;
|
|
30
|
+
|
|
31
|
+
`osf.stage`?: Name;
|
|
32
|
+
`osf.requester.kind`: ActorKind;
|
|
33
|
+
`osf.requester.id`: string;
|
|
34
|
+
|
|
35
|
+
/** Where the run was started from, for example "slack", "github", "cli". */
|
|
36
|
+
`osf.source`: Name;
|
|
37
|
+
|
|
38
|
+
`osf.origin.url`?: url;
|
|
39
|
+
|
|
40
|
+
/** Harness adapter, for example "claude-code". */
|
|
41
|
+
`osf.harness.name`: Name;
|
|
42
|
+
|
|
43
|
+
`osf.harness.version`?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Attributes of the root span of the run. GenAI names where a convention exists. */
|
|
47
|
+
model RunSpan {
|
|
48
|
+
`gen_ai.operation.name`: "invoke_agent";
|
|
49
|
+
`gen_ai.provider.name`?: string;
|
|
50
|
+
`gen_ai.agent.name`?: string;
|
|
51
|
+
|
|
52
|
+
/** The harness session ID. */
|
|
53
|
+
`gen_ai.conversation.id`?: string;
|
|
54
|
+
|
|
55
|
+
`gen_ai.request.model`?: string;
|
|
56
|
+
`gen_ai.response.model`?: string;
|
|
57
|
+
|
|
58
|
+
@minValue(0)
|
|
59
|
+
`gen_ai.usage.input_tokens`?: safeint;
|
|
60
|
+
|
|
61
|
+
@minValue(0)
|
|
62
|
+
`gen_ai.usage.output_tokens`?: safeint;
|
|
63
|
+
|
|
64
|
+
@minValue(0)
|
|
65
|
+
`gen_ai.usage.cache_read.input_tokens`?: safeint;
|
|
66
|
+
|
|
67
|
+
@minValue(0)
|
|
68
|
+
`gen_ai.usage.cache_write.input_tokens`?: safeint;
|
|
69
|
+
|
|
70
|
+
`osf.outcome`: RunOutcome;
|
|
71
|
+
|
|
72
|
+
/** Cost of all model calls of the run, from the inference gateway. */
|
|
73
|
+
@minValue(0)
|
|
74
|
+
`osf.cost.usd`?: float64;
|
|
75
|
+
|
|
76
|
+
/** Full transcript, stored when the run ends. */
|
|
77
|
+
`osf.transcript.uri`?: url;
|
|
78
|
+
|
|
79
|
+
/** Artifacts of the run: reports, screenshots, videos. */
|
|
80
|
+
`osf.artifact.uris`?: url[];
|
|
81
|
+
|
|
82
|
+
`osf.pull_request.urls`?: url[];
|
|
83
|
+
`osf.issue.urls`?: url[];
|
|
84
|
+
`osf.evidence_bundle.uri`?: url;
|
|
85
|
+
|
|
86
|
+
/** Agent Trace record with line attribution of the run's changes. */
|
|
87
|
+
`osf.agent_trace.uri`?: url;
|
|
88
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import "@typespec/json-schema";
|
|
2
|
+
import "./common.tsp";
|
|
3
|
+
|
|
4
|
+
using TypeSpec.JsonSchema;
|
|
5
|
+
|
|
6
|
+
namespace Osf.Contracts;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The result of one scorer on one run. The fields are a subset of Langfuse's create-score request
|
|
10
|
+
* (POST /api/public/scores), so a score can be sent to Langfuse as is. Factory fields live in `metadata`.
|
|
11
|
+
*/
|
|
12
|
+
model Score {
|
|
13
|
+
id: Identifier;
|
|
14
|
+
|
|
15
|
+
/** Scorer name, for example "redundant-tests". */
|
|
16
|
+
@minLength(1)
|
|
17
|
+
name: string;
|
|
18
|
+
|
|
19
|
+
/** A number for NUMERIC, 0 or 1 for BOOLEAN, a string for CATEGORICAL. */
|
|
20
|
+
value: float64 | string;
|
|
21
|
+
|
|
22
|
+
dataType: ScoreDataType;
|
|
23
|
+
|
|
24
|
+
/** Trace of the run (the OTel trace ID). */
|
|
25
|
+
traceId: TraceId;
|
|
26
|
+
|
|
27
|
+
/** Span the score is about, when it is not the whole run. */
|
|
28
|
+
observationId?: string;
|
|
29
|
+
|
|
30
|
+
sessionId?: string;
|
|
31
|
+
|
|
32
|
+
/** The reason. */
|
|
33
|
+
comment?: string;
|
|
34
|
+
|
|
35
|
+
timestamp: utcDateTime;
|
|
36
|
+
|
|
37
|
+
/** API for scorers, ANNOTATION for scores a person reviews. */
|
|
38
|
+
source?: ScoreSource;
|
|
39
|
+
|
|
40
|
+
configId?: string;
|
|
41
|
+
environment?: string;
|
|
42
|
+
metadata: ScoreMetadata;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
enum ScoreDataType {
|
|
46
|
+
NUMERIC,
|
|
47
|
+
BOOLEAN,
|
|
48
|
+
CATEGORICAL,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
enum ScoreSource {
|
|
52
|
+
API,
|
|
53
|
+
ANNOTATION,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
model ScoreMetadata {
|
|
57
|
+
`osf.run.id`: Identifier;
|
|
58
|
+
`osf.scorer.version`?: string;
|
|
59
|
+
|
|
60
|
+
/** Judge model of an LLM scorer. */
|
|
61
|
+
`osf.judge.model`?: string;
|
|
62
|
+
|
|
63
|
+
/** Cost of the judge call. */
|
|
64
|
+
@minValue(0)
|
|
65
|
+
`osf.judge.cost.usd`?: float64;
|
|
66
|
+
|
|
67
|
+
...Record<unknown>;
|
|
68
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import "@typespec/json-schema";
|
|
2
|
+
import "./common.tsp";
|
|
3
|
+
|
|
4
|
+
using TypeSpec.JsonSchema;
|
|
5
|
+
|
|
6
|
+
namespace Osf.Contracts;
|
|
7
|
+
|
|
8
|
+
// Wire formats (work item, run record, evidence manifest, score, events) are open: consumers ignore fields they
|
|
9
|
+
// do not know, so producers can add optional fields in a minor version.
|
|
10
|
+
|
|
11
|
+
/** One unit of requested work: the `data` of a `work.received@v1` CloudEvent. */
|
|
12
|
+
model WorkItem {
|
|
13
|
+
id: Identifier;
|
|
14
|
+
type: WorkItemType;
|
|
15
|
+
|
|
16
|
+
@minLength(1)
|
|
17
|
+
@maxLength(300)
|
|
18
|
+
title: string;
|
|
19
|
+
|
|
20
|
+
description?: string;
|
|
21
|
+
|
|
22
|
+
/** The person or trigger that asked for the work. */
|
|
23
|
+
requester: Actor;
|
|
24
|
+
|
|
25
|
+
/** Where the work came from. */
|
|
26
|
+
origin: Origin;
|
|
27
|
+
|
|
28
|
+
/** Requests with the same key become one work item, for example "github:acme/web#42". */
|
|
29
|
+
@minLength(1)
|
|
30
|
+
dedupeKey: string;
|
|
31
|
+
|
|
32
|
+
/** Product surface the work is about, for example "checkout". */
|
|
33
|
+
surface?: string;
|
|
34
|
+
|
|
35
|
+
/** Set by triage. */
|
|
36
|
+
priority?: Priority;
|
|
37
|
+
|
|
38
|
+
/** Related material: an error report, a change, a document. */
|
|
39
|
+
links?: Link[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
enum WorkItemType {
|
|
43
|
+
idea,
|
|
44
|
+
bug,
|
|
45
|
+
task,
|
|
46
|
+
automation,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
enum Priority {
|
|
50
|
+
urgent,
|
|
51
|
+
high,
|
|
52
|
+
normal,
|
|
53
|
+
low,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
model Origin {
|
|
57
|
+
/** The channel or system, for example "slack", "github", "sentry", "cron", "mcp". */
|
|
58
|
+
source: Name;
|
|
59
|
+
|
|
60
|
+
/** Link back to the origin, for example the Slack thread. Absent for a schedule. */
|
|
61
|
+
url?: url;
|
|
62
|
+
|
|
63
|
+
/** ID in the source system. */
|
|
64
|
+
externalId?: string;
|
|
65
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/sesigl/open-software-factory/main/contracts/work-item/work-item.schema.json",
|
|
4
|
+
"title": "WorkItem",
|
|
5
|
+
"$comment": "Generated by `pnpm build` in contracts/ from typespec/. Do not edit.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"id": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"minLength": 1,
|
|
11
|
+
"maxLength": 128,
|
|
12
|
+
"description": "An identifier chosen by the producer. ULIDs are recommended; consumers treat it as opaque."
|
|
13
|
+
},
|
|
14
|
+
"type": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"enum": [
|
|
17
|
+
"idea",
|
|
18
|
+
"bug",
|
|
19
|
+
"task",
|
|
20
|
+
"automation"
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"title": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"minLength": 1,
|
|
26
|
+
"maxLength": 300
|
|
27
|
+
},
|
|
28
|
+
"description": {
|
|
29
|
+
"type": "string"
|
|
30
|
+
},
|
|
31
|
+
"requester": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"properties": {
|
|
34
|
+
"kind": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"enum": [
|
|
37
|
+
"person",
|
|
38
|
+
"trigger"
|
|
39
|
+
],
|
|
40
|
+
"description": "Who caused something: a person, or an automated trigger such as a cron rule or an alert."
|
|
41
|
+
},
|
|
42
|
+
"id": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"minLength": 1,
|
|
45
|
+
"description": "Stable ID in the source system, prefixed with the source, for example \"slack:U024BE7LH\"."
|
|
46
|
+
},
|
|
47
|
+
"displayName": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"description": "Shown to people, for example \"Varun\"."
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"required": [
|
|
53
|
+
"kind",
|
|
54
|
+
"id"
|
|
55
|
+
],
|
|
56
|
+
"description": "The person or trigger that asked for the work."
|
|
57
|
+
},
|
|
58
|
+
"origin": {
|
|
59
|
+
"type": "object",
|
|
60
|
+
"properties": {
|
|
61
|
+
"source": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"pattern": "^[a-z][a-z0-9-]{0,62}$",
|
|
64
|
+
"description": "The channel or system, for example \"slack\", \"github\", \"sentry\", \"cron\", \"mcp\"."
|
|
65
|
+
},
|
|
66
|
+
"url": {
|
|
67
|
+
"type": "string",
|
|
68
|
+
"format": "uri",
|
|
69
|
+
"description": "Link back to the origin, for example the Slack thread. Absent for a schedule."
|
|
70
|
+
},
|
|
71
|
+
"externalId": {
|
|
72
|
+
"type": "string",
|
|
73
|
+
"description": "ID in the source system."
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"required": [
|
|
77
|
+
"source"
|
|
78
|
+
],
|
|
79
|
+
"description": "Where the work came from."
|
|
80
|
+
},
|
|
81
|
+
"dedupeKey": {
|
|
82
|
+
"type": "string",
|
|
83
|
+
"minLength": 1,
|
|
84
|
+
"description": "Requests with the same key become one work item, for example \"github:acme/web#42\"."
|
|
85
|
+
},
|
|
86
|
+
"surface": {
|
|
87
|
+
"type": "string",
|
|
88
|
+
"description": "Product surface the work is about, for example \"checkout\"."
|
|
89
|
+
},
|
|
90
|
+
"priority": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"enum": [
|
|
93
|
+
"urgent",
|
|
94
|
+
"high",
|
|
95
|
+
"normal",
|
|
96
|
+
"low"
|
|
97
|
+
],
|
|
98
|
+
"description": "Set by triage."
|
|
99
|
+
},
|
|
100
|
+
"links": {
|
|
101
|
+
"type": "array",
|
|
102
|
+
"items": {
|
|
103
|
+
"type": "object",
|
|
104
|
+
"properties": {
|
|
105
|
+
"url": {
|
|
106
|
+
"type": "string",
|
|
107
|
+
"format": "uri"
|
|
108
|
+
},
|
|
109
|
+
"title": {
|
|
110
|
+
"type": "string"
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
"required": [
|
|
114
|
+
"url"
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
"description": "Related material: an error report, a change, a document."
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"required": [
|
|
121
|
+
"id",
|
|
122
|
+
"type",
|
|
123
|
+
"title",
|
|
124
|
+
"requester",
|
|
125
|
+
"origin",
|
|
126
|
+
"dedupeKey"
|
|
127
|
+
],
|
|
128
|
+
"description": "One unit of requested work: the `data` of a `work.received@v1` CloudEvent."
|
|
129
|
+
}
|