@cleocode/contracts 2026.9.22 → 2026.9.23
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/__tests__/evidence-atom-schema.test.js +3 -2
- package/dist/__tests__/evidence-atom-schema.test.js.map +1 -1
- package/dist/acceptance-gate-schema.d.ts.map +1 -1
- package/dist/acceptance-gate-schema.js +4 -1
- package/dist/acceptance-gate-schema.js.map +1 -1
- package/dist/data-accessor.d.ts +4 -0
- package/dist/data-accessor.d.ts.map +1 -1
- package/dist/data-accessor.js.map +1 -1
- package/dist/decide-sites.d.ts +157 -0
- package/dist/decide-sites.d.ts.map +1 -0
- package/dist/decide-sites.js +28 -0
- package/dist/decide-sites.js.map +1 -0
- package/dist/decide.d.ts +105 -0
- package/dist/decide.d.ts.map +1 -1
- package/dist/decide.js +31 -0
- package/dist/decide.js.map +1 -1
- package/dist/dispatch/operations-registry.d.ts.map +1 -1
- package/dist/dispatch/operations-registry.js +37 -2
- package/dist/dispatch/operations-registry.js.map +1 -1
- package/dist/evidence-atom-schema.d.ts.map +1 -1
- package/dist/evidence-atom-schema.js +3 -0
- package/dist/evidence-atom-schema.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/operations/admin.d.ts +5 -0
- package/dist/operations/admin.d.ts.map +1 -1
- package/dist/operations/tasks.d.ts +24 -0
- package/dist/operations/tasks.d.ts.map +1 -1
- package/dist/operations/tasks.js.map +1 -1
- package/dist/task.d.ts +14 -0
- package/dist/task.d.ts.map +1 -1
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/score-task-priority.d.ts +28 -3
- package/dist/tools/score-task-priority.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
- package/src/__tests__/evidence-atom-schema.test.ts +3 -2
- package/src/acceptance-gate-schema.ts +4 -1
- package/src/data-accessor.ts +4 -0
- package/src/decide-sites.ts +179 -0
- package/src/decide.ts +109 -0
- package/src/dispatch/operations-registry.ts +39 -2
- package/src/evidence-atom-schema.ts +3 -0
- package/src/index.ts +24 -0
- package/src/operations/admin.ts +5 -0
- package/src/operations/tasks.ts +26 -0
- package/src/task.ts +14 -0
- package/src/tools/index.ts +1 -0
- package/src/tools/score-task-priority.ts +29 -3
|
@@ -684,7 +684,8 @@ describe('formatGateRequirement (T10337)', () => {
|
|
|
684
684
|
expect(s).toContain('[decision AND note]');
|
|
685
685
|
expect(s).toContain('[pr AND files]');
|
|
686
686
|
// Joiner between combinations is ' OR '.
|
|
687
|
-
|
|
687
|
+
// T12689: + [pr AND note] for a deletion-only PR.
|
|
688
|
+
expect(s.split(' OR ').length).toBe(6);
|
|
688
689
|
});
|
|
689
690
|
|
|
690
691
|
it('formats single-element combination without AND', () => {
|
|
@@ -759,7 +760,7 @@ describe('validateEvidenceForGate failure surface (T9949)', () => {
|
|
|
759
760
|
if (!r.ok) {
|
|
760
761
|
// Error includes the same artifact requirement as the validator.
|
|
761
762
|
expect(r.message).toBe(
|
|
762
|
-
"Gate 'implemented' requires evidence: [commit AND files] OR [commit AND note] OR [decision AND files] OR [decision AND note] OR [pr AND files]",
|
|
763
|
+
"Gate 'implemented' requires evidence: [commit AND files] OR [commit AND note] OR [decision AND files] OR [decision AND note] OR [pr AND files] OR [pr AND note]",
|
|
763
764
|
);
|
|
764
765
|
// New rich hint includes the same content as formatGateRequirementHint.
|
|
765
766
|
expect(r.hint).toBe(formatGateRequirementHint('implemented'));
|
|
@@ -536,7 +536,10 @@ export const acceptanceGateResultSchema = z
|
|
|
536
536
|
message: 'Result cannot precede input capture',
|
|
537
537
|
});
|
|
538
538
|
const executable = ['test', 'command', 'lint'].includes(result.kind);
|
|
539
|
-
|
|
539
|
+
// T12718: a command the runner refused to split (shell syntax, open
|
|
540
|
+
// quote) never had an invocation. It is bound as an unmet `error` so the
|
|
541
|
+
// rest of its batch still records; any verdict needs the invocation.
|
|
542
|
+
if (executable && !binding.invocation && result.result !== 'error')
|
|
540
543
|
context.addIssue({
|
|
541
544
|
code: 'custom',
|
|
542
545
|
path: ['binding', 'invocation'],
|
package/src/data-accessor.ts
CHANGED
|
@@ -255,6 +255,8 @@ export interface AcBindingRow {
|
|
|
255
255
|
|
|
256
256
|
/** Query options for bounded reads from the append-only task audit log. @task T10594 */
|
|
257
257
|
export interface TaskAuditLogQuery {
|
|
258
|
+
/** Exact audit row ids (T12693: revert one ranking change). */
|
|
259
|
+
ids?: readonly string[];
|
|
258
260
|
taskIds?: readonly string[];
|
|
259
261
|
actions?: readonly string[];
|
|
260
262
|
since?: string;
|
|
@@ -271,6 +273,8 @@ export interface TaskAuditLogRow {
|
|
|
271
273
|
detailsJson: string | null;
|
|
272
274
|
beforeJson: string | null;
|
|
273
275
|
afterJson: string | null;
|
|
276
|
+
/** Session the mutation ran in, when recorded (T12693). */
|
|
277
|
+
sessionId?: string | null;
|
|
274
278
|
}
|
|
275
279
|
|
|
276
280
|
/**
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decision-site registry contracts — the "decision ladder as code" (spec
|
|
3
|
+
* `system-one-integration` §3, owner decisions D11158 and D11159).
|
|
4
|
+
*
|
|
5
|
+
* Every place CLEO makes a judgement is a decision site: it has one primary
|
|
6
|
+
* rung, an ordered ladder of rungs it may escalate to, a fallback, an
|
|
7
|
+
* owner-escalation rule, a mode and (to act on a System One answer) go-live
|
|
8
|
+
* evidence. The registry rows live in `packages/core/src/decide/sites/`;
|
|
9
|
+
* these are their types.
|
|
10
|
+
*
|
|
11
|
+
* Types and const data only (arch gate 10).
|
|
12
|
+
*
|
|
13
|
+
* @task T12662
|
|
14
|
+
* @epic T12486
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The decision rungs, cheapest first. Escalation only moves up this list.
|
|
19
|
+
*
|
|
20
|
+
* - `rule` — the answer follows from data (ids, statuses, thresholds).
|
|
21
|
+
* - `system-one` — a typed judgement over text with a small fixed answer set.
|
|
22
|
+
* - `generative` — the output must be free text or code.
|
|
23
|
+
* - `agent` — multi-step work.
|
|
24
|
+
* - `owner` — authority only the owner can take (the ask tool).
|
|
25
|
+
*/
|
|
26
|
+
export const DECISION_RUNGS = ['rule', 'system-one', 'generative', 'agent', 'owner'] as const;
|
|
27
|
+
|
|
28
|
+
/** One rung of the decision ladder. */
|
|
29
|
+
export type DecisionRung = (typeof DECISION_RUNGS)[number];
|
|
30
|
+
|
|
31
|
+
/** Site modes: `off` never asks, `shadow` asks and audits, `on` acts. */
|
|
32
|
+
export const DECISION_SITE_MODES = ['off', 'shadow', 'on'] as const;
|
|
33
|
+
|
|
34
|
+
/** How a site uses its System One rung. */
|
|
35
|
+
export type DecisionSiteModeValue = (typeof DECISION_SITE_MODES)[number];
|
|
36
|
+
|
|
37
|
+
/** The shape of the question a site answers. */
|
|
38
|
+
export type DecisionSiteQuestionType =
|
|
39
|
+
| 'noul'
|
|
40
|
+
| 'choice'
|
|
41
|
+
| 'score'
|
|
42
|
+
| 'text'
|
|
43
|
+
| 'multi-step'
|
|
44
|
+
| 'owner-choice';
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* When a site may put a question to the owner.
|
|
48
|
+
*
|
|
49
|
+
* - `never` — the site never asks the owner.
|
|
50
|
+
* - `escalate-only` — it may add an owner question, never clear one.
|
|
51
|
+
* - `below-floor` — it asks the owner when every lower rung is below its floor.
|
|
52
|
+
* - `always` — the owner always decides.
|
|
53
|
+
*/
|
|
54
|
+
export type DecisionSiteOwnerEscalation = 'never' | 'escalate-only' | 'below-floor' | 'always';
|
|
55
|
+
|
|
56
|
+
/** Classes of text a site may send off the machine (spec §8). */
|
|
57
|
+
export type DecisionSiteTextClass =
|
|
58
|
+
| 'task-text'
|
|
59
|
+
| 'memory-text'
|
|
60
|
+
| 'diff'
|
|
61
|
+
| 'skill-text'
|
|
62
|
+
| 'external-text';
|
|
63
|
+
|
|
64
|
+
/** Measured evidence that lets a site act on its System One answer (`mode: on`). */
|
|
65
|
+
export interface DecisionSiteGoLive {
|
|
66
|
+
/** Canonical doc slug holding the measurement. */
|
|
67
|
+
readonly evidenceDoc: string;
|
|
68
|
+
/** ISO date of the measurement. */
|
|
69
|
+
readonly measuredAt: string;
|
|
70
|
+
/** Number of labelled decisions measured. */
|
|
71
|
+
readonly n: number;
|
|
72
|
+
/** Metric name, e.g. `precision@acting-class`. */
|
|
73
|
+
readonly metric: string;
|
|
74
|
+
/** Measured value. */
|
|
75
|
+
readonly value: number;
|
|
76
|
+
/** Floor the value had to clear. */
|
|
77
|
+
readonly floor: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** One registry row: everything CLEO knows about a decision site. */
|
|
81
|
+
export interface DecisionSiteDefinition {
|
|
82
|
+
/** Site id; the string `decide()` and the audit log use, e.g. `tasks.duplicate-detection`. */
|
|
83
|
+
readonly id: string;
|
|
84
|
+
/** Human title. */
|
|
85
|
+
readonly title: string;
|
|
86
|
+
/** Repo-relative files where the site lives. */
|
|
87
|
+
readonly files: readonly string[];
|
|
88
|
+
/** The shape of the question. */
|
|
89
|
+
readonly questionType: DecisionSiteQuestionType;
|
|
90
|
+
/** The rung that answers first. */
|
|
91
|
+
readonly primaryRung: DecisionRung;
|
|
92
|
+
/** Rungs it may escalate to, in order, all above the primary. */
|
|
93
|
+
readonly ladder: readonly DecisionRung[];
|
|
94
|
+
/** What acts when the primary is unavailable. */
|
|
95
|
+
readonly fallback: DecisionRung | 'none';
|
|
96
|
+
/**
|
|
97
|
+
* Minimum confidence per question before `on` may act on a System One
|
|
98
|
+
* answer. The key `*` applies to every question of the site.
|
|
99
|
+
*/
|
|
100
|
+
readonly floors?: Readonly<Record<string, number>>;
|
|
101
|
+
/** When the site may ask the owner. */
|
|
102
|
+
readonly ownerEscalation: DecisionSiteOwnerEscalation;
|
|
103
|
+
/** Config key holding the mode (the existing `decide.sites.*` keys, D11159). */
|
|
104
|
+
readonly modeKey?: string;
|
|
105
|
+
/** Config key opting the site into its generative rung (`decide.generativeFallback.*`). */
|
|
106
|
+
readonly generativeKey?: string;
|
|
107
|
+
/** Mode when the config key is unset. */
|
|
108
|
+
readonly defaultMode: DecisionSiteModeValue;
|
|
109
|
+
/** Generative rung: the role or system key it resolves its model with. */
|
|
110
|
+
readonly llmSystemKey?: string;
|
|
111
|
+
/** True for a site on a write path: 300 ms budget, never blocks the write. */
|
|
112
|
+
readonly writePath: boolean;
|
|
113
|
+
/** Text classes it sends to a provider. */
|
|
114
|
+
readonly sends: readonly DecisionSiteTextClass[];
|
|
115
|
+
/** Go-live evidence, required before a System One site runs `on`. */
|
|
116
|
+
readonly goLive?: DecisionSiteGoLive;
|
|
117
|
+
/** Owning task. */
|
|
118
|
+
readonly task: string;
|
|
119
|
+
/** Free-form note (constraints such as "interactive only"). */
|
|
120
|
+
readonly note?: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Audit counts for one site over the last seven days. */
|
|
124
|
+
export interface DecisionSiteActivity {
|
|
125
|
+
/** Audit lines written for the site. */
|
|
126
|
+
readonly asked: number;
|
|
127
|
+
/** Answered by the provider. */
|
|
128
|
+
readonly provider: number;
|
|
129
|
+
/** Answered from the decision cache. */
|
|
130
|
+
readonly cache: number;
|
|
131
|
+
/** Answered by the fallback. */
|
|
132
|
+
readonly fallback: number;
|
|
133
|
+
/** Escalated to a higher rung (0 until the ladder executor lands, T12665). */
|
|
134
|
+
readonly escalated: number;
|
|
135
|
+
/** Share of compared shadow lines where decision and heuristic agreed, when any were compared. */
|
|
136
|
+
readonly agreement?: number;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** One site as `cleo decide sites` reports it. */
|
|
140
|
+
export interface DecisionSiteSummary {
|
|
141
|
+
/** Site id. */
|
|
142
|
+
readonly id: string;
|
|
143
|
+
/** Human title. */
|
|
144
|
+
readonly title: string;
|
|
145
|
+
/** Question shape. */
|
|
146
|
+
readonly questionType: DecisionSiteQuestionType;
|
|
147
|
+
/** Primary rung. */
|
|
148
|
+
readonly primaryRung: DecisionRung;
|
|
149
|
+
/** Escalation ladder. */
|
|
150
|
+
readonly ladder: readonly DecisionRung[];
|
|
151
|
+
/** Fallback. */
|
|
152
|
+
readonly fallback: DecisionRung | 'none';
|
|
153
|
+
/** Owner-escalation rule. */
|
|
154
|
+
readonly ownerEscalation: DecisionSiteOwnerEscalation;
|
|
155
|
+
/** Registry default mode. */
|
|
156
|
+
readonly mode: DecisionSiteModeValue;
|
|
157
|
+
/** Mode set in config, when the site has a mode key and it is set. */
|
|
158
|
+
readonly configuredMode?: DecisionSiteModeValue;
|
|
159
|
+
/** Mode in force: `off` for a System One site while no provider is configured. */
|
|
160
|
+
readonly effectiveMode: DecisionSiteModeValue;
|
|
161
|
+
/** Config key holding the mode, when the site has one. */
|
|
162
|
+
readonly modeKey?: string;
|
|
163
|
+
/** Go-live evidence, when recorded. */
|
|
164
|
+
readonly goLive?: DecisionSiteGoLive;
|
|
165
|
+
/** Owning task. */
|
|
166
|
+
readonly task: string;
|
|
167
|
+
/** Audit activity over the last seven days. */
|
|
168
|
+
readonly last7d: DecisionSiteActivity;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Result of `cleo decide sites`. */
|
|
172
|
+
export interface DecisionSitesListResult {
|
|
173
|
+
/** Whether a System One provider is configured (else System One sites are `off`). */
|
|
174
|
+
readonly providerConfigured: boolean;
|
|
175
|
+
/** Registered sites total, before filters. */
|
|
176
|
+
readonly total: number;
|
|
177
|
+
/** Matching sites. */
|
|
178
|
+
readonly sites: readonly DecisionSiteSummary[];
|
|
179
|
+
}
|
package/src/decide.ts
CHANGED
|
@@ -143,6 +143,17 @@ export interface DecisionRequest {
|
|
|
143
143
|
readonly state: DecisionState;
|
|
144
144
|
/** Question name → question (1–32 entries). Names key the answers in {@link DecisionOutcome}. */
|
|
145
145
|
readonly questions: Readonly<Record<string, DecisionQuestion>>;
|
|
146
|
+
/**
|
|
147
|
+
* Language hint (e.g. `en`). Sent only to a provider whose capabilities
|
|
148
|
+
* include `langHint`; others never see it (T12664).
|
|
149
|
+
*/
|
|
150
|
+
readonly lang?: string;
|
|
151
|
+
/**
|
|
152
|
+
* Provider-side answer cache. `false` asks a provider with `cacheControl`
|
|
153
|
+
* not to answer from its cache (real latency, sensitive text). Ignored by
|
|
154
|
+
* providers without the capability.
|
|
155
|
+
*/
|
|
156
|
+
readonly cache?: boolean;
|
|
146
157
|
}
|
|
147
158
|
|
|
148
159
|
/** Answer to a {@link NoulDecisionQuestion}. */
|
|
@@ -196,14 +207,100 @@ export interface DecisionOutcome {
|
|
|
196
207
|
readonly latencyMs: number;
|
|
197
208
|
/** Provider-reported cost in US dollars, when known. */
|
|
198
209
|
readonly costUsd?: number;
|
|
210
|
+
/** Provider-reported cost in integer micro-dollars, when known (T12664). */
|
|
211
|
+
readonly costMicros?: number;
|
|
212
|
+
/** Account balance in integer micro-dollars reported with this answer, when known. */
|
|
213
|
+
readonly balanceMicros?: number;
|
|
214
|
+
/** Provider model checkpoint that answered, when reported. */
|
|
215
|
+
readonly checkpoint?: string;
|
|
199
216
|
/** Provider-reported input-token count, when known. */
|
|
200
217
|
readonly inputTokens?: number;
|
|
201
218
|
}
|
|
202
219
|
|
|
220
|
+
// ─── Provider kinds (T12713) ─────────────────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Every supported decision-provider kind, recommended first.
|
|
224
|
+
*
|
|
225
|
+
* - `layahost` — the hosted provider at {@link LAYAHOST_BASE_URL}; the user
|
|
226
|
+
* supplies only an API key.
|
|
227
|
+
* - `jev` — any other Jev-compatible endpoint; the user supplies the URL.
|
|
228
|
+
*/
|
|
229
|
+
export const DECISION_PROVIDER_KINDS = ['layahost', 'jev'] as const;
|
|
230
|
+
|
|
231
|
+
/** A decision-provider kind. See {@link DECISION_PROVIDER_KINDS}. */
|
|
232
|
+
export type DecisionProviderKind = (typeof DECISION_PROVIDER_KINDS)[number];
|
|
233
|
+
|
|
234
|
+
/** Base URL of the layahost API (its OpenAPI `servers[0].url`). */
|
|
235
|
+
export const LAYAHOST_BASE_URL = 'https://layahost.com';
|
|
236
|
+
|
|
237
|
+
/** layahost's routing model, the default for a layahost connection. */
|
|
238
|
+
export const LAYAHOST_DEFAULT_MODEL = 'laya-auto';
|
|
239
|
+
|
|
203
240
|
/** Connection settings for a decision provider. */
|
|
204
241
|
export interface DecisionProviderConfig {
|
|
205
242
|
/** Absolute base URL of the provider's API (no trailing path to the endpoint). */
|
|
206
243
|
readonly baseUrl: string;
|
|
244
|
+
/** Provider kind; absent means a custom Jev-compatible endpoint (`jev`). */
|
|
245
|
+
readonly provider?: DecisionProviderKind;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ─── Provider capabilities (T12664) ──────────────────────────────────────────
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* What a decision provider can do beyond the Jev minimum
|
|
252
|
+
* (`POST /v1/systemone`, 32 questions, 32,000-character state). A caller asks
|
|
253
|
+
* for a capability; the client degrades when it is absent (spec
|
|
254
|
+
* `system-one-integration` §2.2).
|
|
255
|
+
*/
|
|
256
|
+
export interface DecisionProviderCapabilities {
|
|
257
|
+
/** Wire protocol the provider speaks. */
|
|
258
|
+
readonly wire: string;
|
|
259
|
+
/** Most questions one request may carry. */
|
|
260
|
+
readonly maxQuestionsPerRequest: number;
|
|
261
|
+
/** Largest state, in characters. */
|
|
262
|
+
readonly maxStateChars: number;
|
|
263
|
+
/** Batch endpoint limits, when the provider has one. */
|
|
264
|
+
readonly batch?: { readonly maxRequests: number; readonly maxQuestions: number };
|
|
265
|
+
/** Built-in template names, when the provider has templates. */
|
|
266
|
+
readonly templates?: readonly string[];
|
|
267
|
+
/** Server-side flows are available. */
|
|
268
|
+
readonly flows?: boolean;
|
|
269
|
+
/** A usage/balance endpoint and cost headers are available. */
|
|
270
|
+
readonly usage?: boolean;
|
|
271
|
+
/** The request's `cache` field is honoured. */
|
|
272
|
+
readonly cacheControl?: boolean;
|
|
273
|
+
/** The request's `lang` field is honoured. */
|
|
274
|
+
readonly langHint?: boolean;
|
|
275
|
+
/** How the provider reports cost. */
|
|
276
|
+
readonly reportsCost?: 'micros' | 'usd' | false;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** The Jev minimum every provider supports; the default when a provider reports nothing. */
|
|
280
|
+
export const JEV_MINIMUM_CAPABILITIES: DecisionProviderCapabilities = {
|
|
281
|
+
wire: 'jev-systemone/1',
|
|
282
|
+
maxQuestionsPerRequest: 32,
|
|
283
|
+
maxStateChars: 32_000,
|
|
284
|
+
reportsCost: 'usd',
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
/** One item of a batch call: its outcome, or why it failed. */
|
|
288
|
+
export type DecisionBatchItem =
|
|
289
|
+
| { readonly ok: true; readonly outcome: DecisionOutcome }
|
|
290
|
+
| { readonly ok: false; readonly status?: number; readonly errorKind: string };
|
|
291
|
+
|
|
292
|
+
/** Account usage and balance as a provider reports it. */
|
|
293
|
+
export interface DecisionProviderUsage {
|
|
294
|
+
/** Balance in integer micro-dollars. */
|
|
295
|
+
readonly balanceMicros?: number;
|
|
296
|
+
/** Decisions the balance still covers. */
|
|
297
|
+
readonly decisionsLeft?: number;
|
|
298
|
+
/** Plan name. */
|
|
299
|
+
readonly plan?: string;
|
|
300
|
+
/** End of the current plan period, ISO-8601. */
|
|
301
|
+
readonly periodEnd?: string;
|
|
302
|
+
/** Decisions and cost over the queried window. */
|
|
303
|
+
readonly totals?: { readonly decisions?: number; readonly costMicros?: number };
|
|
207
304
|
}
|
|
208
305
|
|
|
209
306
|
// ─── Zod schemas ──────────────────────────────────────────────────────────────
|
|
@@ -290,6 +387,11 @@ export const decisionQuestionSchema = z.discriminatedUnion('type', [
|
|
|
290
387
|
*/
|
|
291
388
|
export const decisionRequestSchema = z.object({
|
|
292
389
|
model: z.string().min(1).optional(),
|
|
390
|
+
lang: z
|
|
391
|
+
.string()
|
|
392
|
+
.regex(/^[a-z]{2,3}(-[A-Za-z0-9]{2,8})?$/)
|
|
393
|
+
.optional(),
|
|
394
|
+
cache: z.boolean().optional(),
|
|
293
395
|
state: decisionStateSchema,
|
|
294
396
|
questions: z.record(z.string().min(1), decisionQuestionSchema).refine(
|
|
295
397
|
(questions) => {
|
|
@@ -346,10 +448,17 @@ export const decisionOutcomeSchema = z.object({
|
|
|
346
448
|
requestId: z.string().min(1).optional(),
|
|
347
449
|
latencyMs: z.number().nonnegative(),
|
|
348
450
|
costUsd: z.number().nonnegative().optional(),
|
|
451
|
+
costMicros: z.number().int().nonnegative().optional(),
|
|
452
|
+
balanceMicros: z.number().int().optional(),
|
|
453
|
+
checkpoint: z.string().min(1).optional(),
|
|
349
454
|
inputTokens: z.number().int().nonnegative().optional(),
|
|
350
455
|
});
|
|
351
456
|
|
|
457
|
+
/** Zod schema for {@link DecisionProviderKind}. */
|
|
458
|
+
export const decisionProviderKindSchema = z.enum(DECISION_PROVIDER_KINDS);
|
|
459
|
+
|
|
352
460
|
/** Zod schema for {@link DecisionProviderConfig}; `baseUrl` must be an absolute URL. */
|
|
353
461
|
export const decisionProviderConfigSchema = z.object({
|
|
354
462
|
baseUrl: z.url(),
|
|
463
|
+
provider: decisionProviderKindSchema.optional(),
|
|
355
464
|
});
|
|
@@ -814,12 +814,22 @@ export const OPERATIONS: OperationDef[] = [
|
|
|
814
814
|
gateway: 'query' as const,
|
|
815
815
|
domain: 'tasks' as const,
|
|
816
816
|
operation: 'history',
|
|
817
|
-
description:
|
|
817
|
+
description:
|
|
818
|
+
'Show task work history; with taskId, the task audit trail; with ranking, who changed its priority/severity/kind/depends and why (T12693)',
|
|
818
819
|
tier: 1,
|
|
819
820
|
idempotent: true,
|
|
820
821
|
sessionRequired: false,
|
|
821
822
|
requiredParams: [],
|
|
822
|
-
params: [
|
|
823
|
+
params: [
|
|
824
|
+
{ name: 'taskId', type: 'string', required: false, description: 'Task to show history for' },
|
|
825
|
+
{ name: 'limit', type: 'number', required: false, description: 'Maximum entries' },
|
|
826
|
+
{
|
|
827
|
+
name: 'ranking',
|
|
828
|
+
type: 'boolean',
|
|
829
|
+
required: false,
|
|
830
|
+
description: 'Only ranking-input changes (actor, session, reason, before/after)',
|
|
831
|
+
},
|
|
832
|
+
] satisfies ParamDef[],
|
|
823
833
|
},
|
|
824
834
|
{
|
|
825
835
|
gateway: 'query',
|
|
@@ -3364,6 +3374,33 @@ export const OPERATIONS: OperationDef[] = [
|
|
|
3364
3374
|
] satisfies ParamDef[],
|
|
3365
3375
|
outputSchema: OUTPUT_CONTRACTS['tasks.assignee'],
|
|
3366
3376
|
},
|
|
3377
|
+
{
|
|
3378
|
+
gateway: 'mutate',
|
|
3379
|
+
domain: 'tasks',
|
|
3380
|
+
operation: 'ranking.revert',
|
|
3381
|
+
description:
|
|
3382
|
+
'tasks.ranking.revert (mutate) — undo one recorded ranking change (priority/severity/kind/depends) as a new audited change (T12693, D11161)',
|
|
3383
|
+
tier: 1,
|
|
3384
|
+
idempotent: false,
|
|
3385
|
+
sessionRequired: false,
|
|
3386
|
+
requiredParams: ['entryId'],
|
|
3387
|
+
params: [
|
|
3388
|
+
{
|
|
3389
|
+
name: 'entryId',
|
|
3390
|
+
type: 'string',
|
|
3391
|
+
required: true,
|
|
3392
|
+
description: 'ranking_changed audit row id (cleo history ranking <taskId>)',
|
|
3393
|
+
cli: { positional: true },
|
|
3394
|
+
},
|
|
3395
|
+
{ name: 'reason', type: 'string', required: false, description: 'Why it is reverted' },
|
|
3396
|
+
{
|
|
3397
|
+
name: 'force',
|
|
3398
|
+
type: 'boolean',
|
|
3399
|
+
required: false,
|
|
3400
|
+
description: 'Revert even though a field changed again since',
|
|
3401
|
+
},
|
|
3402
|
+
] satisfies ParamDef[],
|
|
3403
|
+
},
|
|
3367
3404
|
{
|
|
3368
3405
|
gateway: 'mutate',
|
|
3369
3406
|
domain: 'tasks',
|
|
@@ -500,6 +500,9 @@ export const GATE_EVIDENCE_REQUIREMENTS: Readonly<
|
|
|
500
500
|
['decision', 'files'],
|
|
501
501
|
['decision', 'note'],
|
|
502
502
|
['pr', 'files'],
|
|
503
|
+
// T12689: a deletion-only PR (no bytes to hash). Contextual validation
|
|
504
|
+
// accepts this only when every change the PR made is a deletion.
|
|
505
|
+
['pr', 'note'],
|
|
503
506
|
],
|
|
504
507
|
},
|
|
505
508
|
testsPassed: { oneOf: [['test-run'], ['tool'], ['ci']] },
|
package/src/index.ts
CHANGED
|
@@ -2703,6 +2703,7 @@ export type {
|
|
|
2703
2703
|
ScoreFactor,
|
|
2704
2704
|
ScoreTaskContext,
|
|
2705
2705
|
ScoreTaskInput,
|
|
2706
|
+
ScoreTaskKey,
|
|
2706
2707
|
ScoreTaskResult,
|
|
2707
2708
|
} from './tools/score-task-priority.js';
|
|
2708
2709
|
// === Transport (low-level wire protocol) ===
|
|
@@ -2890,11 +2891,15 @@ export type {
|
|
|
2890
2891
|
ChoiceDecisionAnswer,
|
|
2891
2892
|
ChoiceDecisionQuestion,
|
|
2892
2893
|
DecisionAnswer,
|
|
2894
|
+
DecisionBatchItem,
|
|
2893
2895
|
DecisionInstructions,
|
|
2894
2896
|
DecisionJsonValue,
|
|
2895
2897
|
DecisionOutcome,
|
|
2896
2898
|
DecisionOutcomeSource,
|
|
2899
|
+
DecisionProviderCapabilities,
|
|
2897
2900
|
DecisionProviderConfig,
|
|
2901
|
+
DecisionProviderKind,
|
|
2902
|
+
DecisionProviderUsage,
|
|
2898
2903
|
DecisionQuestion,
|
|
2899
2904
|
DecisionQuestionType,
|
|
2900
2905
|
DecisionRequest,
|
|
@@ -2908,6 +2913,7 @@ export {
|
|
|
2908
2913
|
choiceDecisionAnswerSchema,
|
|
2909
2914
|
choiceDecisionQuestionSchema,
|
|
2910
2915
|
DECISION_OUTCOME_SOURCES,
|
|
2916
|
+
DECISION_PROVIDER_KINDS,
|
|
2911
2917
|
DECISION_QUESTION_TYPES,
|
|
2912
2918
|
DECISION_REQUEST_LIMITS,
|
|
2913
2919
|
decisionAnswerSchema,
|
|
@@ -2915,14 +2921,32 @@ export {
|
|
|
2915
2921
|
decisionOutcomeSchema,
|
|
2916
2922
|
decisionOutcomeSourceSchema,
|
|
2917
2923
|
decisionProviderConfigSchema,
|
|
2924
|
+
decisionProviderKindSchema,
|
|
2918
2925
|
decisionQuestionSchema,
|
|
2919
2926
|
decisionQuestionTypeSchema,
|
|
2920
2927
|
decisionRequestSchema,
|
|
2921
2928
|
decisionStateSchema,
|
|
2929
|
+
JEV_MINIMUM_CAPABILITIES,
|
|
2930
|
+
LAYAHOST_BASE_URL,
|
|
2931
|
+
LAYAHOST_DEFAULT_MODEL,
|
|
2922
2932
|
noulDecisionAnswerSchema,
|
|
2923
2933
|
noulDecisionQuestionSchema,
|
|
2924
2934
|
scoreDecisionAnswerSchema,
|
|
2925
2935
|
scoreDecisionQuestionSchema,
|
|
2926
2936
|
} from './decide.js';
|
|
2937
|
+
// T12662: decision-site registry (the decision ladder as code).
|
|
2938
|
+
export type {
|
|
2939
|
+
DecisionRung,
|
|
2940
|
+
DecisionSiteActivity,
|
|
2941
|
+
DecisionSiteDefinition,
|
|
2942
|
+
DecisionSiteGoLive,
|
|
2943
|
+
DecisionSiteModeValue,
|
|
2944
|
+
DecisionSiteOwnerEscalation,
|
|
2945
|
+
DecisionSiteQuestionType,
|
|
2946
|
+
DecisionSiteSummary,
|
|
2947
|
+
DecisionSitesListResult,
|
|
2948
|
+
DecisionSiteTextClass,
|
|
2949
|
+
} from './decide-sites.js';
|
|
2950
|
+
export { DECISION_RUNGS, DECISION_SITE_MODES } from './decide-sites.js';
|
|
2927
2951
|
|
|
2928
2952
|
export { isStorableTaskId, isTaskId, TASK_ID_REGEX, type TaskId } from './task-id.js';
|
package/src/operations/admin.ts
CHANGED
|
@@ -1648,6 +1648,11 @@ export interface AdminBackupMutateParams {
|
|
|
1648
1648
|
file?: string;
|
|
1649
1649
|
/** Preview the restore without writing (restore.file only). */
|
|
1650
1650
|
dryRun?: boolean;
|
|
1651
|
+
/**
|
|
1652
|
+
* Allow a restore run from inside a git worktree to overwrite the owning
|
|
1653
|
+
* project's live store (T12680). Without it such a restore is refused.
|
|
1654
|
+
*/
|
|
1655
|
+
confirmOwnerStore?: boolean;
|
|
1651
1656
|
}
|
|
1652
1657
|
|
|
1653
1658
|
/** Result of `admin.backup` mutate (create action). */
|
package/src/operations/tasks.ts
CHANGED
|
@@ -921,6 +921,31 @@ export interface TasksComplexityEstimateResult {
|
|
|
921
921
|
export interface TasksHistoryParams {
|
|
922
922
|
taskId?: string;
|
|
923
923
|
limit?: number;
|
|
924
|
+
/**
|
|
925
|
+
* With `taskId`: only ranking-input changes (priority, severity, kind,
|
|
926
|
+
* depends) — who, which session, why, before/after (T12693, D11161).
|
|
927
|
+
*/
|
|
928
|
+
ranking?: boolean;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// tasks.ranking.revert
|
|
932
|
+
/** Parameters for `tasks.ranking.revert` (T12693, D11161). */
|
|
933
|
+
export interface TasksRankingRevertParams {
|
|
934
|
+
/** The `ranking_changed` audit row id to undo (from `cleo history ranking`). */
|
|
935
|
+
entryId: string;
|
|
936
|
+
/** Why it is reverted; recorded on the new audit row. */
|
|
937
|
+
reason?: string;
|
|
938
|
+
/** Revert even though a field changed again since. */
|
|
939
|
+
force?: boolean;
|
|
940
|
+
}
|
|
941
|
+
/** Result of `tasks.ranking.revert`. */
|
|
942
|
+
export interface TasksRankingRevertResult {
|
|
943
|
+
/** Task whose ranking inputs were restored. */
|
|
944
|
+
taskId: string;
|
|
945
|
+
/** Fields set back. */
|
|
946
|
+
fields: string[];
|
|
947
|
+
/** The values they were set back to. */
|
|
948
|
+
revertedTo: Record<string, unknown>;
|
|
924
949
|
}
|
|
925
950
|
/**
|
|
926
951
|
* Result of `tasks.history` — audit log entries for a task.
|
|
@@ -2277,6 +2302,7 @@ export type TasksOps = {
|
|
|
2277
2302
|
readonly 'relates.add': readonly [TasksRelatesAddParams, TasksRelatesAddResult];
|
|
2278
2303
|
readonly 'relates.add-batch': readonly [TasksRelatesAddBatchParams, TasksRelatesAddBatchResult];
|
|
2279
2304
|
readonly 'relates.remove': readonly [TasksRelatesRemoveParams, TasksRelatesRemoveResult];
|
|
2305
|
+
readonly 'ranking.revert': readonly [TasksRankingRevertParams, TasksRankingRevertResult];
|
|
2280
2306
|
readonly start: readonly [TasksStartQueryParams, TasksStartQueryResult];
|
|
2281
2307
|
readonly stop: readonly [TasksStopQueryParams, TasksStopQueryResult];
|
|
2282
2308
|
readonly 'sync.reconcile': readonly [TasksSyncReconcileParams, TasksSyncReconcileResult];
|
package/src/task.ts
CHANGED
|
@@ -187,6 +187,14 @@ export interface CriterionEvidenceLink {
|
|
|
187
187
|
artifactPaths: string[];
|
|
188
188
|
/** Indices of actual validated result atoms in the containing gate's atoms array. */
|
|
189
189
|
resultAtomIndices: number[];
|
|
190
|
+
/**
|
|
191
|
+
* How the `implemented` link is supported (T12689): `files` when a path the
|
|
192
|
+
* criterion names is among the inspected artifacts; `self-attested` when the
|
|
193
|
+
* criterion names no path in them, so the link rests on the agent's claim.
|
|
194
|
+
* Paths parsed from criterion text are advisory (T12118), so this flags
|
|
195
|
+
* rather than refuses. Absent on other gates and on older receipts.
|
|
196
|
+
*/
|
|
197
|
+
basis?: 'files' | 'self-attested';
|
|
190
198
|
}
|
|
191
199
|
|
|
192
200
|
/** Verification failure log entry. */
|
|
@@ -390,6 +398,12 @@ export type EvidenceAtom =
|
|
|
390
398
|
* (T12671): the task is linked through it and its files are the change.
|
|
391
399
|
*/
|
|
392
400
|
componentPrNumber?: number;
|
|
401
|
+
/**
|
|
402
|
+
* Paths the (component) PR deleted that stay deleted in the landing
|
|
403
|
+
* commit (T12689). A deletion has no bytes to hash, so a PR whose every
|
|
404
|
+
* change is a deletion implements with `pr:` + `note:` instead of `files:`.
|
|
405
|
+
*/
|
|
406
|
+
deletedPaths?: string[];
|
|
393
407
|
}
|
|
394
408
|
| {
|
|
395
409
|
/**
|
package/src/tools/index.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @epic T9835
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type { TaskPriority } from '../task.js';
|
|
9
|
+
import type { TaskKind, TaskPriority, TaskSeverity } from '../task.js';
|
|
10
10
|
|
|
11
11
|
/** Minimal task shape required by scoreTask. */
|
|
12
12
|
export interface ScoreTaskInput {
|
|
@@ -24,6 +24,10 @@ export interface ScoreTaskInput {
|
|
|
24
24
|
createdAt?: string;
|
|
25
25
|
/** Labels for pattern matching. */
|
|
26
26
|
labels?: string[];
|
|
27
|
+
/** Severity axis (orthogonal to priority); unset for most tasks. */
|
|
28
|
+
severity?: TaskSeverity | null;
|
|
29
|
+
/** Kind axis; a `bug` with no severity is scored at the default bug severity (T12661). */
|
|
30
|
+
kind?: TaskKind | null;
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
/** Context provided to scoreTask to enable phase-aware and dependency-aware scoring. */
|
|
@@ -38,6 +42,8 @@ export interface ScoreTaskContext {
|
|
|
38
42
|
successPatterns?: Array<{ pattern: string }>;
|
|
39
43
|
/** Matched failure patterns from BRAIN (optional penalty scoring). */
|
|
40
44
|
failurePatterns?: Array<{ pattern: string }>;
|
|
45
|
+
/** Open tasks that depend on each task id — its leverage (bounded bonus, T12661). */
|
|
46
|
+
leverage?: ReadonlyMap<string, number>;
|
|
41
47
|
}
|
|
42
48
|
|
|
43
49
|
/** A scoring factor contributing to the final score. */
|
|
@@ -48,12 +54,32 @@ export interface ScoreFactor {
|
|
|
48
54
|
delta: number;
|
|
49
55
|
/** Human-readable explanation. */
|
|
50
56
|
detail: string;
|
|
57
|
+
/**
|
|
58
|
+
* Comparator tier the factor belongs to (T12691): 1 priority band, 2 severity,
|
|
59
|
+
* 3 bounded tiebreak; `null` for an informational factor outside the order.
|
|
60
|
+
*/
|
|
61
|
+
tier?: 1 | 2 | 3 | null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The lexicographic sort key of a scored task (T12691); higher sorts first. */
|
|
65
|
+
export interface ScoreTaskKey {
|
|
66
|
+
/** Owner priority band: critical 4, high 3, medium 2, low 1. */
|
|
67
|
+
band: number;
|
|
68
|
+
/** Attested severity: P0 4, P1 3, P2 2, P3 1, unknown 0. */
|
|
69
|
+
severity: number;
|
|
70
|
+
/** Bounded computed tiebreak (deps, phase, leverage, age). */
|
|
71
|
+
tiebreak: number;
|
|
51
72
|
}
|
|
52
73
|
|
|
53
74
|
/** Result of scoreTask. */
|
|
54
75
|
export interface ScoreTaskResult {
|
|
55
|
-
/**
|
|
76
|
+
/**
|
|
77
|
+
* The key as one number that sorts identically (band × 10000 + severity ×
|
|
78
|
+
* 1000 + tiebreak), for display and single-number consumers.
|
|
79
|
+
*/
|
|
56
80
|
score: number;
|
|
57
|
-
/** Individual scoring factors. */
|
|
81
|
+
/** Individual scoring factors, each tagged with its comparator tier. */
|
|
58
82
|
factors: ScoreFactor[];
|
|
83
|
+
/** The lexicographic sort key (T12691). */
|
|
84
|
+
key?: ScoreTaskKey;
|
|
59
85
|
}
|