@cleocode/contracts 2026.9.21 → 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 +20 -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 +38 -2
- package/dist/dispatch/operations-registry.js.map +1 -1
- package/dist/done-plan.d.ts +19 -0
- package/dist/done-plan.d.ts.map +1 -1
- package/dist/evidence-atom-schema.d.ts +12 -44
- package/dist/evidence-atom-schema.d.ts.map +1 -1
- package/dist/evidence-atom-schema.js +32 -14
- package/dist/evidence-atom-schema.js.map +1 -1
- package/dist/index.d.ts +6 -4
- 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/docs.d.ts +5 -2
- package/dist/operations/docs.d.ts.map +1 -1
- package/dist/operations/docs.js.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 +32 -1
- 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 +25 -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 +40 -2
- package/src/done-plan.ts +20 -0
- package/src/evidence-atom-schema.ts +34 -18
- package/src/index.ts +25 -0
- package/src/operations/admin.ts +5 -0
- package/src/operations/docs.ts +5 -2
- package/src/operations/tasks.ts +26 -0
- package/src/task.ts +32 -1
- package/src/tools/index.ts +1 -0
- package/src/tools/score-task-priority.ts +29 -3
|
@@ -499,6 +499,28 @@ describe('parseEvidenceString (T10337)', () => {
|
|
|
499
499
|
|
|
500
500
|
// ─── GATE_EVIDENCE_REQUIREMENTS — gate-to-atom mapping ─────────────────────
|
|
501
501
|
|
|
502
|
+
describe('pr:/ci: component references (T12671)', () => {
|
|
503
|
+
it('pr:<component>@<integration> names the integration PR and its component', () => {
|
|
504
|
+
expect(parseEvidenceString('pr:42@41')).toEqual([
|
|
505
|
+
{ kind: 'pr', prNumber: 41, componentPrNumber: 42 },
|
|
506
|
+
]);
|
|
507
|
+
expect(parseEvidenceString('ci:42@41')).toEqual([
|
|
508
|
+
{ kind: 'ci', prNumber: 41, componentPrNumber: 42 },
|
|
509
|
+
]);
|
|
510
|
+
expect(parseEvidenceString('pr:41')).toEqual([{ kind: 'pr', prNumber: 41 }]);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it.each(['pr:42@42', 'pr:42@', 'pr:@41', 'ci:0@41', 'pr:42@4x'])('rejects %s', (raw) => {
|
|
514
|
+
expect(() => parseEvidenceString(raw)).toThrow(EvidenceParseError);
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
it('the schema accepts the component field', () => {
|
|
518
|
+
expect(
|
|
519
|
+
EvidenceAtomSchema.safeParse({ kind: 'ci', prNumber: 41, componentPrNumber: 42 }).success,
|
|
520
|
+
).toBe(true);
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
|
|
502
524
|
describe('GATE_EVIDENCE_REQUIREMENTS (T10337)', () => {
|
|
503
525
|
it('covers every VerificationGate', () => {
|
|
504
526
|
const expected: VerificationGate[] = [
|
|
@@ -662,7 +684,8 @@ describe('formatGateRequirement (T10337)', () => {
|
|
|
662
684
|
expect(s).toContain('[decision AND note]');
|
|
663
685
|
expect(s).toContain('[pr AND files]');
|
|
664
686
|
// Joiner between combinations is ' OR '.
|
|
665
|
-
|
|
687
|
+
// T12689: + [pr AND note] for a deletion-only PR.
|
|
688
|
+
expect(s.split(' OR ').length).toBe(6);
|
|
666
689
|
});
|
|
667
690
|
|
|
668
691
|
it('formats single-element combination without AND', () => {
|
|
@@ -737,7 +760,7 @@ describe('validateEvidenceForGate failure surface (T9949)', () => {
|
|
|
737
760
|
if (!r.ok) {
|
|
738
761
|
// Error includes the same artifact requirement as the validator.
|
|
739
762
|
expect(r.message).toBe(
|
|
740
|
-
"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]",
|
|
741
764
|
);
|
|
742
765
|
// New rich hint includes the same content as formatGateRequirementHint.
|
|
743
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',
|
|
@@ -8456,6 +8493,7 @@ export const OPERATIONS: OperationDef[] = [
|
|
|
8456
8493
|
enum: DOCS_LIFECYCLE_STATUSES,
|
|
8457
8494
|
description:
|
|
8458
8495
|
'Override the new lifecycle status. Defaults to "draft" on every update. ' +
|
|
8496
|
+
'Alone (no file or content) it changes only the lifecycle status. ' +
|
|
8459
8497
|
`Valid: ${DOCS_LIFECYCLE_STATUSES.join('|')}.`,
|
|
8460
8498
|
},
|
|
8461
8499
|
{
|
package/src/done-plan.ts
CHANGED
|
@@ -119,6 +119,11 @@ export interface TaskChangeSet {
|
|
|
119
119
|
executionRoot: string;
|
|
120
120
|
/** Why {@link executionRoot} was chosen. */
|
|
121
121
|
rootSource: ChangeSetRootSource;
|
|
122
|
+
/**
|
|
123
|
+
* Component PR whose change the (integration) `prNumber` landed on the
|
|
124
|
+
* default branch (T12671). Evidence is then `pr:<component>@<prNumber>`.
|
|
125
|
+
*/
|
|
126
|
+
componentPrNumber?: number;
|
|
122
127
|
/** Merged PR number (`source === 'pr'`). */
|
|
123
128
|
prNumber?: number;
|
|
124
129
|
/**
|
|
@@ -168,8 +173,23 @@ export interface TaskChangeSet {
|
|
|
168
173
|
blockers: DonePlanBlocker[];
|
|
169
174
|
/** Non-blocking observations, e.g. PR discovery that could not reach `gh`. */
|
|
170
175
|
warnings: string[];
|
|
176
|
+
/**
|
|
177
|
+
* The merged-PR lookup failed (e.g. `gh` unavailable), so a non-PR change set
|
|
178
|
+
* does not prove the change is unmerged (T12656).
|
|
179
|
+
*/
|
|
180
|
+
prDiscoveryFailed?: boolean;
|
|
181
|
+
/**
|
|
182
|
+
* Whether the task's PR has merged to the default branch, as the PR lookup
|
|
183
|
+
* found it — independent of `source` (T12656 review): a merged PR whose
|
|
184
|
+
* `pr:` check was refused still falls back to the task branch, and the
|
|
185
|
+
* branch outlives a squash merge. Absent when no PR was examined.
|
|
186
|
+
*/
|
|
187
|
+
mergeState?: ChangeSetMergeState;
|
|
171
188
|
}
|
|
172
189
|
|
|
190
|
+
/** Whether a task's change has merged to the default branch. */
|
|
191
|
+
export type ChangeSetMergeState = 'merged' | 'unmerged' | 'unknown';
|
|
192
|
+
|
|
173
193
|
/** Planned state of one verification gate. */
|
|
174
194
|
export interface DonePlanGate {
|
|
175
195
|
/** Gate name. */
|
|
@@ -198,7 +198,11 @@ export const decisionAtomSchema = z.object({
|
|
|
198
198
|
* checks task relationship and changed artifacts. Implementation also needs
|
|
199
199
|
* inspected file evidence; testing and review require actual result atoms.
|
|
200
200
|
*
|
|
201
|
-
* Format: `pr:<positive integer>` (e.g. `pr:357`)
|
|
201
|
+
* Format: `pr:<positive integer>` (e.g. `pr:357`), or
|
|
202
|
+
* `pr:<component>@<integration>` (e.g. `pr:356@357`, T12671): component PR
|
|
203
|
+
* #356 merged into an integration branch that PR #357 landed on the default
|
|
204
|
+
* branch. The integration PR supplies the merge commit; the component PR
|
|
205
|
+
* supplies the task linkage and the changed files.
|
|
202
206
|
*
|
|
203
207
|
* @task T9764
|
|
204
208
|
* @task T9838
|
|
@@ -206,6 +210,7 @@ export const decisionAtomSchema = z.object({
|
|
|
206
210
|
export const prAtomSchema = z.object({
|
|
207
211
|
kind: z.literal('pr'),
|
|
208
212
|
prNumber: z.number().int().positive('pr atom requires a positive integer PR number'),
|
|
213
|
+
componentPrNumber: z.number().int().positive().optional(),
|
|
209
214
|
});
|
|
210
215
|
|
|
211
216
|
/**
|
|
@@ -214,13 +219,16 @@ export const prAtomSchema = z.object({
|
|
|
214
219
|
* `qaPassed` only when the project opts in with `evidence.ciSatisfies`
|
|
215
220
|
* (owner decision D11149).
|
|
216
221
|
*
|
|
217
|
-
* Format: `ci:<positive integer>` (e.g. `ci:357`)
|
|
222
|
+
* Format: `ci:<positive integer>` (e.g. `ci:357`), or
|
|
223
|
+
* `ci:<component>@<integration>` (e.g. `ci:356@357`, T12671): the integration
|
|
224
|
+
* PR's merge-commit CI, for a task linked through its component PR.
|
|
218
225
|
*
|
|
219
226
|
* @task T12634
|
|
220
227
|
*/
|
|
221
228
|
export const ciAtomSchema = z.object({
|
|
222
229
|
kind: z.literal('ci'),
|
|
223
230
|
prNumber: z.number().int().positive('ci atom requires a positive integer PR number'),
|
|
231
|
+
componentPrNumber: z.number().int().positive().optional(),
|
|
224
232
|
});
|
|
225
233
|
|
|
226
234
|
/**
|
|
@@ -492,6 +500,9 @@ export const GATE_EVIDENCE_REQUIREMENTS: Readonly<
|
|
|
492
500
|
['decision', 'files'],
|
|
493
501
|
['decision', 'note'],
|
|
494
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'],
|
|
495
506
|
],
|
|
496
507
|
},
|
|
497
508
|
testsPassed: { oneOf: [['test-run'], ['tool'], ['ci']] },
|
|
@@ -771,6 +782,21 @@ export class EvidenceParseError extends Error {
|
|
|
771
782
|
* @task T10337
|
|
772
783
|
* @adr ADR-051
|
|
773
784
|
*/
|
|
785
|
+
/**
|
|
786
|
+
* `<n>` or `<component>@<integration>` (T12671), each a positive integer.
|
|
787
|
+
* The integration PR is the atom's `prNumber`.
|
|
788
|
+
*/
|
|
789
|
+
function parsePrReference(
|
|
790
|
+
payload: string,
|
|
791
|
+
): { prNumber: number; componentPrNumber?: number } | null {
|
|
792
|
+
const match = /^(\d+)(?:@(\d+))?$/.exec(payload);
|
|
793
|
+
if (!match) return null;
|
|
794
|
+
const first = Number(match[1]);
|
|
795
|
+
const second = match[2] === undefined ? null : Number(match[2]);
|
|
796
|
+
if (first <= 0 || (second !== null && (second <= 0 || second === first))) return null;
|
|
797
|
+
return second === null ? { prNumber: first } : { prNumber: second, componentPrNumber: first };
|
|
798
|
+
}
|
|
799
|
+
|
|
774
800
|
export function parseEvidenceString(raw: string): EvidenceAtom[] {
|
|
775
801
|
if (!raw || typeof raw !== 'string') {
|
|
776
802
|
throw new EvidenceParseError(
|
|
@@ -840,26 +866,16 @@ export function parseEvidenceString(raw: string): EvidenceAtom[] {
|
|
|
840
866
|
atoms.push({ kind: 'decision', decisionId: payload });
|
|
841
867
|
break;
|
|
842
868
|
}
|
|
843
|
-
case 'pr':
|
|
844
|
-
const prNumber = Number(payload);
|
|
845
|
-
if (!Number.isInteger(prNumber) || prNumber <= 0) {
|
|
846
|
-
throw new EvidenceParseError(
|
|
847
|
-
`pr atom requires a positive integer PR number, got "${payload}" in "${chunk}"`,
|
|
848
|
-
'Use format: pr:<number> e.g. pr:357',
|
|
849
|
-
);
|
|
850
|
-
}
|
|
851
|
-
atoms.push({ kind: 'pr', prNumber });
|
|
852
|
-
break;
|
|
853
|
-
}
|
|
869
|
+
case 'pr':
|
|
854
870
|
case 'ci': {
|
|
855
|
-
const
|
|
856
|
-
if (!
|
|
871
|
+
const ref = parsePrReference(payload);
|
|
872
|
+
if (!ref) {
|
|
857
873
|
throw new EvidenceParseError(
|
|
858
|
-
|
|
859
|
-
|
|
874
|
+
`${kind} atom requires a positive integer PR number, got "${payload}" in "${chunk}"`,
|
|
875
|
+
`Use format: ${kind}:<number> e.g. ${kind}:357, or ${kind}:<component>@<integration> e.g. ${kind}:356@357`,
|
|
860
876
|
);
|
|
861
877
|
}
|
|
862
|
-
atoms.push({ kind: 'ci',
|
|
878
|
+
atoms.push(kind === 'pr' ? { kind: 'pr', ...ref } : { kind: 'ci', ...ref });
|
|
863
879
|
break;
|
|
864
880
|
}
|
|
865
881
|
case 'loc-drop': {
|
package/src/index.ts
CHANGED
|
@@ -516,6 +516,7 @@ export type {
|
|
|
516
516
|
// === Done planner: read-only `cleo done --plan` evidence plan (T12623, T12624) ===
|
|
517
517
|
export type {
|
|
518
518
|
ChangeSetDoc,
|
|
519
|
+
ChangeSetMergeState,
|
|
519
520
|
ChangeSetPrCandidate,
|
|
520
521
|
ChangeSetRootSource,
|
|
521
522
|
ChangeSetSource,
|
|
@@ -2702,6 +2703,7 @@ export type {
|
|
|
2702
2703
|
ScoreFactor,
|
|
2703
2704
|
ScoreTaskContext,
|
|
2704
2705
|
ScoreTaskInput,
|
|
2706
|
+
ScoreTaskKey,
|
|
2705
2707
|
ScoreTaskResult,
|
|
2706
2708
|
} from './tools/score-task-priority.js';
|
|
2707
2709
|
// === Transport (low-level wire protocol) ===
|
|
@@ -2889,11 +2891,15 @@ export type {
|
|
|
2889
2891
|
ChoiceDecisionAnswer,
|
|
2890
2892
|
ChoiceDecisionQuestion,
|
|
2891
2893
|
DecisionAnswer,
|
|
2894
|
+
DecisionBatchItem,
|
|
2892
2895
|
DecisionInstructions,
|
|
2893
2896
|
DecisionJsonValue,
|
|
2894
2897
|
DecisionOutcome,
|
|
2895
2898
|
DecisionOutcomeSource,
|
|
2899
|
+
DecisionProviderCapabilities,
|
|
2896
2900
|
DecisionProviderConfig,
|
|
2901
|
+
DecisionProviderKind,
|
|
2902
|
+
DecisionProviderUsage,
|
|
2897
2903
|
DecisionQuestion,
|
|
2898
2904
|
DecisionQuestionType,
|
|
2899
2905
|
DecisionRequest,
|
|
@@ -2907,6 +2913,7 @@ export {
|
|
|
2907
2913
|
choiceDecisionAnswerSchema,
|
|
2908
2914
|
choiceDecisionQuestionSchema,
|
|
2909
2915
|
DECISION_OUTCOME_SOURCES,
|
|
2916
|
+
DECISION_PROVIDER_KINDS,
|
|
2910
2917
|
DECISION_QUESTION_TYPES,
|
|
2911
2918
|
DECISION_REQUEST_LIMITS,
|
|
2912
2919
|
decisionAnswerSchema,
|
|
@@ -2914,14 +2921,32 @@ export {
|
|
|
2914
2921
|
decisionOutcomeSchema,
|
|
2915
2922
|
decisionOutcomeSourceSchema,
|
|
2916
2923
|
decisionProviderConfigSchema,
|
|
2924
|
+
decisionProviderKindSchema,
|
|
2917
2925
|
decisionQuestionSchema,
|
|
2918
2926
|
decisionQuestionTypeSchema,
|
|
2919
2927
|
decisionRequestSchema,
|
|
2920
2928
|
decisionStateSchema,
|
|
2929
|
+
JEV_MINIMUM_CAPABILITIES,
|
|
2930
|
+
LAYAHOST_BASE_URL,
|
|
2931
|
+
LAYAHOST_DEFAULT_MODEL,
|
|
2921
2932
|
noulDecisionAnswerSchema,
|
|
2922
2933
|
noulDecisionQuestionSchema,
|
|
2923
2934
|
scoreDecisionAnswerSchema,
|
|
2924
2935
|
scoreDecisionQuestionSchema,
|
|
2925
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';
|
|
2926
2951
|
|
|
2927
2952
|
export { isStorableTaskId, isTaskId, TASK_ID_REGEX, type TaskId } from './task-id.js';
|